英語・語句選択問題検索サイトとその作り方
英語・語句選択問題検索を公開しています(現在試用版につきデータ数はごくわずかです)。
高校生向けに英文法の語句選択問題を作成するのは,かなり頭を悩ませる作業です。
Let’s take an express train ( ) we can get there 20 minutes earlier .
1. in order 2. so that 3. such as 4. while
出題の趣旨に合わせ適切な選択肢を用意するには,ある程度経験が必要です。そこで,選択肢を考案する際の参考資料として問題文をデータベース化しキーワードで検索できるようにしてみてはどうか,というのが今回の出発点です。
また,以下で検索エンジンのコードと解説を記載しています。教育における業務効率化の(素人でもできる)実例として参考にしていただければ幸いです。
PHPを用いた検索エンジンの構築
今回使用する言語はPHPです。
画面に入力された値などに応じてホームページの内容を変化させる言語にもう一つJavaScriptがあります。PHPとJavaScriptではそれぞれできることや役割が異なるのですが,今回のように検索エンジンを構築する場合にはPHPの方が向いています。あとで述べますが,この検索エンジンはレーベンシュタイン距離というものを用いて検索順位を決定しています。PHPはこの機能が標準で搭載されています。
bootstrap
bootstrapはホームページのデザインを改善し,スマートフォンやタブレットからでも見やすいレイアウト,いわゆるレスポンシブデザインに対応させるために使用します。
また,bootstrapを使うと,ページのレイアウトを調整するときにスタイルシートのファイルを開いて作業する手間が省けます。今回のコードではマージンの設定でbootstrapを多用しています。
PHPのコードを書く
PHPと言っても基本はHTMLであり,HTMLの中に<?php
~?>
を挿入することでPHPを動かします。
<head>
<title>英語・語句選択問題検索</title>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<link rel="stylesheet" href="gsearch.css" type="text/css">
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>
<![endif]-->
</head>
ヘッダーの部分です。ここでbootstrapを読み込みます。
次にbody
の構造はこのようになっています。
body
の中にcontainer
というページ全体を格納するブロックを用意します。bootstrapは基本的にcontainer
の中で動作することになっているので,このブロックが必要です。
<header>
と<footer>
は必ずしも必要ではありませんが,ここでは上部のページの見出しと下部の製作者の表示に使用しています。
ページの主な部分は<article>
~</article>
の間に記述します。ここで構築する検索エンジンは,画面上部に検索窓とボタンがあり,その下に検索結果を表示するだけのシンプルな構造です。検索窓とボタンのコードを<form>
~</form>
の間に記述し,検索結果を表示するコードを<div id="result>
~</div>
の間に記述します。従って,今回のコードで重要な部分はこの2か所ということになります。
検索窓
<form method="get">
<div class="form-row align-items-center">
<div>
<?php
echo '<input type="text" id="search_form" class="form-control mt-2 mb-2" name="search" size="33" maxlength="20"';
echo 'value="'.@$_GET['search'].'" autofocus>';
?>
</div>
<div class="col-auto">
<input type="submit" class="btn btn-outline-info align-middle" value="検索">
</div>
検索窓のコードです。一つずつ確認していきましょう。
<form method="get">
<form>
~</form>
の間にはフォームやボタンなどサーバーに送信したい内容を記述します。データを送信する方法にget
とpost
がありますが,検索エンジンの場合はget
を使います。
<div class="form-row align-items-center">
フォームとボタンを格納するブロックを用意します。またクラスにbootstrapの設定を記述します。form-row
はフォームやボタンを横一列に並べます。その一方で,これらのパーツはブラウザの幅に収まるように自動的に配置が調節されます。さらに,align-items-center
はフォームやボタンの垂直方向の位置を中心線で揃えます。
<div>
入力窓のフォームを格納するブロックを用意します。
<?php
echo '<input type="text" id="search_form" class="form-control mt-2 mb-2" name="search" size="33" maxlength="20"';
echo 'value="'.htmlspecialchars(@$_GET['search'], ENT_QUOTES, 'UTF-8').'" autofocus>';
?>
ここでようやくPHPのコードが登場します。echo
はHTMLのコードを書きこむ命令です。このようにしているのはHTMLの一部を書き換えたいからです。
<input>
は文字列の入力フォームです。詳しい説明は他所に譲りますが,ここでのポイントはvalue
に値を代入しているところです。
@$_GET['search']
はname="search"
で指定された要素に入力された内容を示しています。ここでは,@$_GET['search']
は<input>
,つまり検索窓自身に入力された文字列のことです。例えば,検索窓に入力された文字列がin caseなら,echo 'value="'.htmlspecialchars(@$_GET['search'], ENT_QUOTES, 'UTF-8').'" autofocus>';
はecho 'value="in case" autofocus>';
と同じことです。
最初にページにアクセスしたとき,@$_GET['search']
は空白です。そして,文字列を入力し検索ボタンを押すと,同じページが再び呼び出されます。このとき,@$_GET['search']
には入力した文字列が格納されているので,その内容を検索窓に表示するようにしているのです。
この処理を行わないと,検索ボタンを押したときに入力した文字列が消えてしまいます。実際にwebサイトで検索し,検索ボタンを押しても入力した文字列が消えずに残っていることを確認してください。これはサイトの利便性を高めるための工夫です。
<div class="col-auto">
<input type="submit" class="btn btn-outline-info" value="検索">
</div>
<div>
~</div>
で検索ボタンのブロックを用意し,その中にボタンを配置します。class="col-auto"
はボタンの大きさを文字の長さに自動的に合わせるbootstrapの書式です。class="btn btn-outline-info"
も同様にbootstrapの書式で,btn
はボタンであること,btn-outline-info
は中を塗りつぶさない青緑色の枠線のみのボタンを表します。
トグルボタン
トグルボタンは切り替え式のボタンのことです。検索エンジンでは2種類の検索方法と,8種類の表示方法を用意しているので,これらをトグルボタンで切り替えます。
<div class="btn-group btn-group-toggle ml-2" data-toggle="buttons">
一つ目のトグルボタンを格納するブロックを用意します。btn-group btn-group-toggle
はボタンの外観を指定します。ml-2
は左マージンの大きさを指定します。2
を大きくするとボタンの左側の余白をもっと大きくすることができます。
ちなみに,mt-2
は上マージン,mr-2
は右マージン,mb-2
は下マージンを指定します。このようにわざわざcssファイルを開いてスタイルシートの操作しなくても簡単なコードで余白を指定できるところがbootstrapの利点です。
<?php
$display1 = "";
$display2 = "";
switch (@$_GET['display']) {
case null:
$display1 = "checked";
break;
case 1:
$display1 = "checked";
break;
case 2:
$display2 = "checked";
break;
}
?>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="display" id="option1" value="1"'.$display1.'>問題文と選択肢';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="display" id="option2" value="2"'.$display2.'>選択肢のみ';
?>
</label>
次に一つ目のトグルボタンを設置する部分を見ていきましょう。
switch (@$_GET['display']) {
case null:
$display1 = "checked";
break;
case 1:
$display1 = "checked";
break;
case 2:
$display2 = "checked";
break;
}
一つ目のトグルボタンには「問題文と選択肢」と「選択肢のみ」の2つのボタンがあります。@$_GET['display']
にはトグルボタンの選択状態が格納されています。検索ボタンを押したとき,「問題文と選択肢」が選択されていれば@$_GET['display']=1
,「選択肢のみ」が選択されていれば@$_GET['display']=2
となっています。また,最初にページにアクセスしたときは@$_GET['display']=null
です。
そして,switch
を用いて,@$_GET['display']
がnull
または1
のときと2
のときで処理を分けます。
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="display" id="option1" value="1"'.$display1.'><label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="display" id="option1" value="1"'.$display1.'>問題文と選択肢';
?>
</label>
';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="display" id="option2" value="2"'.$display2.'>選択肢のみ';
?>
</label>
ボタンを設置します。ここで行いたいことは検索窓のときと同様で,検索ボタンを押したときにトグルボタンの状態がリセットされないようにすることです。
<input>
の中にchecked
が記述されていると,そのボタンが初めから選択された状態で表示されます。したがって,最初にサイトにアクセスしたとき,また「問題文と選択肢」ボタンが選択された状態で検索ボタンが押された場合に,「問題文と選択肢」のボタンにchecked
が指定されます。一方で「選択肢のみ」ボタンが選択された状態で検索ボタンを押すと,「選択肢のみ」ボタンにchecked
が指定されます。こうして,ボタンの選択状態を引き継ぐようにしています。
<div class="btn-group btn-group-toggle ml-2" data-toggle="buttons">
<?php
$label0 = "";
$label1 = "";
$label2 = "";
$label3 = "";
$label4 = "";
$label5 = "";
$label6 = "";
$label7 = "";
switch (@$_GET['label']) {
case null:
$label0 = "checked";
break;
case 0:
$label0 = "checked";
break;
case 1:
$label1 = "checked";
break;
case 2:
$label2 = "checked";
break;
case 3:
$label3 = "checked";
break;
case 4:
$label4 = "checked";
break;
case 5:
$label5 = "checked";
break;
case 6:
$label6 = "checked";
break;
case 7:
$label7 = "checked";
break;
}
?>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label0" value="0"'.$label0.'>1.';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label1" value="1"'.$label1.'>1';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label2" value="2"'.$label2.'>①';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label3" value="3"'.$label3.'>A.';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label4" value="4"'.$label4.'>A';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label5" value="5"'.$label5.'>a.';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label6" value="6"'.$label6.'>a';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label7" value="7"'.$label7.'>ア';
?>
</label>
</div>
二つ目のトグルボタンを設置します。行っていることは一つ目のトグルボタンと全く同じです。二つ目のトグルボタンはボタンが8個あるので,コードが長くなっています。
検索の実行
<div id="result" class="mt-4">
検索結果を格納するブロックを設置します。
<?php
if (@$_GET['search'] != null) {
最初にwebサイトにアクセスしたとき,当然ながら検索文字は入力されていません。したがって,検索窓の文字列が空白ではないときに検索を行うという条件を記述しておきます。
$query = htmlspecialchars(@$_GET['search'], ENT_QUOTES, 'UTF-8');
検索窓に入力された文字列を$query
に格納します。
$texts = file('data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
テキストファイルdata.txt
を読み込み,それぞれの行ごとに配列$texts
に格納します。
data.txt :
Look! There's a dog in the hall . Someone must have left the door # .
be opened
open
opening
to open
センター試験
Each of the sumo wrestlers # over 100 kg .
is weigh
is weight
weighs
weights
センター試験
・・・
$texts[0] = 'Look! There's a dog in the hall . Someone must have left the door # .'
$texts[1] = 'be opened'
$texts[2] = 'open'
$texts[3] = 'opening'
$texts[4] = 'to open'
$texts[5] = 'センター試験'
$texts[6] = 'Each of the sumo wrestlers # over 100 kg .'
$texts[7] = 'is weigh'
$texts[8] = 'is weight'
$texts[9] = 'weighs'
$texts[10] = 'weights'
$texts[11] = 'センター試験'
・・・
data.txt
は,問題文,選択肢1つ目,2つ目,3つ目,4つ目,大学名の順にデータが並んだテキストファイルです。問題文の#
はもともとは括弧の部分です。このように,一つの設問は6つのデータで構成されています。
通常,このようなデータファイルはjson形式かcsv形式のファイルとして準備します。しかしながら,今回はデータ入力・確認・フォーマット変換の作業効率を考え,原始的な行区切りのテキストファイルを使用しています。どのようなデータ形式を採用するかは作業コストを考慮して決めると良いでしょう。
$count = 0;
for ($i = 0; $i < count($texts); $i++) {
if ($i % 6 == 0) {
$text[$count] = $texts[$i];
}
if ($i % 6 == 1) {
$option1[$count] = $texts[$i];
}
if ($i % 6 == 2) {
$option2[$count] = $texts[$i];
}
if ($i % 6 == 3) {
$option3[$count] = $texts[$i];
}
if ($i % 6 == 4) {
$option4[$count] = $texts[$i];
}
if ($i % 6 == 5) {
$univ_name[$count] = $texts[$i];
$count += 1;
}
}
データを配列に格納します。$i % 6 == 0
は,i
を6で割った余りが0なら,という意味です。このように余りを用いて,問題文を配列$text
,4つの選択肢を配列$option1
,$option2
,$option3
,$option4
,大学名を配列$univ_name
にそれぞれ格納します。
$text[0] = 'Look! There's a dog in the hall . Someone must have left the door # .'
$option1[0] = 'be opened'
$option2[0] = 'open'
$option3[0] = 'opening'
$option4[0] = 'to open'
$univ_name[0] = 'センター試験'
$text[1] = 'Each of the sumo wrestlers # over 100 kg .'
$option1[1] = 'is weigh'
$option2[1] = 'is weight'
$option3[1] = 'weighs'
$option4[1] = 'weights'
$univ_name[1] = 'センター試験'
・・・
$ln1 = array("1.","1","①","A.","A","a.","a","ア");
$ln2 = array("2.","2","②","B.","B","b.","b","イ");
$ln3 = array("3.","3","③","C.","C","c.","c","ウ");
$ln4 = array("4.","4","④","D.","D","d.","d","エ");
$ln = @$_GET['label'];
選択肢の先頭の記号を配列に格納します。そして,選択された二つ目のトグルボタンの値を$ln
に格納します。つまり,$ln=0
なら記号は「1. 2. 3. 4.」を使用し,$ln=2
なら「① ② ③ ④」を使用することにします。
if (@$_GET['display'] == 1) {
一つ目のトグルボタンが「問題文と選択肢」である場合の検索を実行していきます。
for ($i = 0; $i < count($text); $i++) {
$fixed_text[$i] = str_ireplace("#",$option1[$i],$text[$i]);
$fixed_text[$i] .= " ".str_ireplace("#",$option2[$i],$text[$i]);
$fixed_text[$i] .= " ".str_ireplace("#",$option3[$i],$text[$i]);
$fixed_text[$i] .= " ".str_ireplace("#",$option4[$i],$text[$i]);
}
count($text)
は配列$text
の要素の数を表します。例えば,問題文が10個ある場合,count($text)=10
です。for
文は問題文の数だけ繰り返し処理します。
str_ireplace()
は文字列を置換します。問題文の#
の部分をそれぞれの選択肢に置き換えた文字列を作ります。この作業を4つの選択肢それぞれについて行い,文字列を連結します。
$text[0] = 'Look! There's a dog in the hall . Someone must have left the door # .'
$option1[0] = 'be opened'
$option2[0] = 'open'
$option3[0] = 'opening'
$option4[0] = 'to open'
$univ_name[0] = 'センター試験'
$fixed_text[0] ='Look! There's a dog in the hall . Someone must have left the door be opened . Look! There's a dog in the hall . Someone must have left the door open . Look! There's a dog in the hall . Someone must have left the door opening . Look! There's a dog in the hall . Someone must have left the door to open .'
こうして得られた配列$fixed_text
と検索窓に入力された文字列を比べ,両者の類似度を調べます。
レーベンシュタイン距離による類似度測定
for ($id = 0; $id < count($fixed_text); $id++) {
$q_chars = explode(" ",$query);
$chars = explode(" ",$fixed_text[$id]);
$q_length = count($q_chars);
$chars_length = count($chars);
$score_min = 100;
for ($j = 0; $j < ($chars_length-$q_length+1); $j++) {
$sequence = "";
for ($k = 0; $k < $q_length; $k++) {
$sequence .= " ".$chars[$j+$k];
}
$score = levenshtein(mb_strtolower($query), mb_strtolower($sequence));
if ($score < $score_min) {
$score_min = $score;
}
}
$scores[$id] = $score_min;
}
レーベンシュタイン距離を用いて文字列の類似度を測定します。レーベンシュタイン距離についての詳細は省きますが,levenshtein()
は比較する2つの文字列どうしが似ているほど小さな値を返します。
for ($id = 0; $id < count($fixed_text); $id++) {
count($fixed_text)
は配列$fixed_text
の要素の数を表します。for
文は問題文の数だけ繰り返し処理します。
$q_chars = explode(" ",$query);
$chars = explode(" ",$fixed_text[$id]);
---> $chars= array('Look!', 'There's', 'a', 'dog', 'in', ・・・)
explode()
は文字列を分割します。検索窓に入力された文字列と比較対象の文字列を単語の間の空白で分割し,配列に格納します。
$q_length = count($q_chars);
$chars_length = count($chars);
それぞれの配列の要素の数を数えます。
$score_min = 100;
$score_min
はレーベンシュタイン距離によって測定された値の最小値を格納します。初期値として100を与えます。
for ($j = 0; $j < ($chars_length-$q_length+1); $j++) {
$sequence = "";
for ($k = 0; $k < $q_length; $k++) {
$sequence .= " ".$chars[$j+$k];
}
$score = levenshtein(mb_strtolower($query), mb_strtolower($sequence));
if ($score < $score_min) {
$score_min = $score;
}
}
$scores[$id] = $score_min;
時系列データを作成し,検索窓に入力された文字列と比較します。
$sequence
は時系列データを格納します。例えば,検索窓に入力された単語が3個で,$chars
が以下の場合
$chars= array('Look!', 'There's', 'a', 'dog', 'in', ・・・)
$sequence
には"Look! There's a"
,"There's a dog"
,"a dog in"
・・・という文字列が順番に格納されていきます。これらを一つずつ入力された文字列と比較していきます。
$score
はレーベンシュタイン距離によって判定した値を格納します。そして,それが最小値$score_min
よりも小さい場合に$score_min
を更新します。
検索した文字列の中に入力した文字列と似ている部分があれば,最小値は小さくなります。反対に,似ている文字列が見つからなければ最小値は大きな値をとるはずです。
求めた最小値は,連想配列$scores
にそれぞれの設問ごとに格納します。$scores
は設問のIDと類似度を格納しています。
データの並べ替え
asort($scores);
配列$scores
を類似度の値の小さい順に並べ替えます。レーベンシュタイン距離は類似度が高いほど小さな値を返します。そして,値の小さな順に問題文の10個を結果として表示します。
$count = 0;
foreach($scores as $key => $value) {
echo "<p>".str_ireplace("#","( )",$text[$key]);
echo " <span class='univ_name'>(".$univ_name[$key].")</span>";
echo "</p>";
echo "<p style='text-indent:1.5em'>";
echo $ln1[$ln]." ".$option1[$key];
echo " ".$ln2[$ln]." ".$option2[$key];
echo " ".$ln3[$ln]." ".$option3[$key];
echo " ".$ln4[$ln]." ".$option4[$key];
echo "</p>";
$count += 1;
if ($count == 10) {
break;
}
}
foreach
は連想配列の要素を先頭から一つずつ取り出します。$scores
は類似度の高いものほど先頭にある状態に並べ替えられています。$key
は問題文のIDを表し,$value
はレーベンシュタイン距離の最小値を表しますが,ここでは$value
の値は使用しません。
そして,問題文のIDを手がかりに問題文の文字列を取り出し,文中の#
を括弧に置き換えます。さらに,大学名の文字列を,例えば「センター試験」から「(センター試験)」に置き換えます。
また,トグルボタンによって選択された選択肢の先頭の記号をもとに,4つの選択肢をそれぞれ表示していきます。
問題文を選択肢を表示する毎に,$count
を1つずつ増やしていき,10になった時点でforeach
の繰り返しを抜け出します。こうすることで検索結果を10個だけ表示します。
選択肢のみの検索
if (@$_GET['display'] == 2) {
for ($id = 0; $id < 10; $id++) {
$score = levenshtein(mb_strtolower($query), mb_strtolower($option1[$id]));
$score_min = $score;
$score = levenshtein(mb_strtolower($query), mb_strtolower($option2[$id]));
if ($score < $score_min) {
$score_min = $score;
}
$score = levenshtein(mb_strtolower($query), mb_strtolower($option3[$id]));
if ($score < $score_min) {
$score_min = $score;
}
$score = levenshtein(mb_strtolower($query), mb_strtolower($option4[$id]));
if ($score < $score_min) {
$score_min = $score;
}
$scores[$id] = $score_min;
}
asort($scores);
foreach($scores as $key => $value) {
echo "<p>";
echo $ln1[$ln]." ".$option1[$key];
echo " ".$ln2[$ln]." ".$option2[$key];
echo " ".$ln3[$ln]." ".$option3[$key];
echo " ".$ln4[$ln]." ".$option4[$key];
echo "</p>";
}
}
選択肢のみの検索の場合も,上で述べた検索と基本的な方法は変わりません。先ほどは問題文を時系列データに変換しましたが,こちらでは検索窓に入力された文字列と選択肢の文字列を直接比較しています。
そのため,検索窓に入力された文字列と選択肢の単語数が大きく異なる場合には検索の精度が下がることになります。
最後にコード全体を示します。
<head>
<meta charset="utf-8">
<title>英語・語句選択問題検索</title>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<link rel="stylesheet" href="gsearch.css" type="text/css">
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>
<![endif]-->
</head>
<body>
<div class="container">
<header class="mt-2">英語・語句選択問題検索</header>
<article>
<form method="get">
<div class="form-row align-items-center">
<div>
<?php
echo '<input type="text" id="search_form" class="form-control mt-2 mb-2" name="search" size="33" maxlength="20"';
echo 'value="'.htmlspecialchars(@$_GET['search'], ENT_QUOTES, 'UTF-8').'" autofocus>';
?>
</div>
<div class="col-auto">
<input type="submit" class="btn btn-outline-info" value="検索">
</div>
<div class="btn-group btn-group-toggle ml-2" data-toggle="buttons">
<?php
$display1 = "";
$display2 = "";
switch (@$_GET['display']) {
case null:
$display1 = "checked";
break;
case 1:
$display1 = "checked";
break;
case 2:
$display2 = "checked";
break;
}
?>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="display" id="option1" value="1"'.$display1.'>問題文と選択肢';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="display" id="option2" value="2"'.$display2.'>選択肢のみ';
?>
</label>
</div>
<div class="btn-group btn-group-toggle ml-2" data-toggle="buttons">
<?php
$label0 = "";
$label1 = "";
$label2 = "";
$label3 = "";
$label4 = "";
$label5 = "";
$label6 = "";
$label7 = "";
switch (@$_GET['label']) {
case null:
$label0 = "checked";
break;
case 0:
$label0 = "checked";
break;
case 1:
$label1 = "checked";
break;
case 2:
$label2 = "checked";
break;
case 3:
$label3 = "checked";
break;
case 4:
$label4 = "checked";
break;
case 5:
$label5 = "checked";
break;
case 6:
$label6 = "checked";
break;
case 7:
$label7 = "checked";
break;
}
?>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label0" value="0"'.$label0.'>1.';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label1" value="1"'.$label1.'>1';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label2" value="2"'.$label2.'>①';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label3" value="3"'.$label3.'>A.';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label4" value="4"'.$label4.'>A';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label5" value="5"'.$label5.'>a.';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label6" value="6"'.$label6.'>a';
?>
</label>
<label class="btn btn-outline-info">
<?php
echo '<input type="radio" name="label" id="label7" value="7"'.$label7.'>ア';
?>
</label>
</div>
</div>
</form>
<div id="result" class="mt-4">
<?php
if (@$_GET['search'] != null) {
$query = htmlspecialchars(@$_GET['search'], ENT_QUOTES, 'UTF-8');
$texts = file('data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$count = 0;
for ($i = 0; $i < count($texts); $i++) {
if ($i % 6 == 0) {
$text[$count] = $texts[$i];
}
if ($i % 6 == 1) {
$option1[$count] = $texts[$i];
}
if ($i % 6 == 2) {
$option2[$count] = $texts[$i];
}
if ($i % 6 == 3) {
$option3[$count] = $texts[$i];
}
if ($i % 6 == 4) {
$option4[$count] = $texts[$i];
}
if ($i % 6 == 5) {
$univ_name[$count] = $texts[$i];
$count += 1;
}
}
$ln1 = array("1.","1","①","A.","A","a.","a","ア");
$ln2 = array("2.","2","②","B.","B","b.","b","イ");
$ln3 = array("3.","3","③","C.","C","c.","c","ウ");
$ln4 = array("4.","4","④","D.","D","d.","d","エ");
$ln = @$_GET['label'];
if (@$_GET['display'] == 1) {
for ($i = 0; $i < count($text); $i++) {
$fixed_text[$i] = str_ireplace("#",$option1[$i],$text[$i]);
$fixed_text[$i] .= " ".str_ireplace("#",$option2[$i],$text[$i]);
$fixed_text[$i] .= " ".str_ireplace("#",$option3[$i],$text[$i]);
$fixed_text[$i] .= " ".str_ireplace("#",$option4[$i],$text[$i]);
}
for ($id = 0; $id < count($fixed_text); $id++) {
$q_chars = explode(" ",$query);
$chars = explode(" ",$fixed_text[$id]);
$q_length = count($q_chars);
$chars_length = count($chars);
$score_min = 100;
for ($j = 0; $j < ($chars_length-$q_length+1); $j++) {
$sequence = "";
for ($k = 0; $k < $q_length; $k++) {
$sequence .= " ".$chars[$j+$k];
}
$score = levenshtein(mb_strtolower($query), mb_strtolower($sequence));
if ($score < $score_min) {
$score_min = $score;
}
}
$scores[$id] = $score_min;
}
asort($scores);
$count = 0;
foreach($scores as $key => $value) {
echo "<p>".str_ireplace("#","( )",$text[$key]);
echo " <span class='univ_name'>(".$univ_name[$key].")</span>";
echo "</p>";
echo "<p style='text-indent:1.5em'>";
echo $ln1[$ln]." ".$option1[$key];
echo " ".$ln2[$ln]." ".$option2[$key];
echo " ".$ln3[$ln]." ".$option3[$key];
echo " ".$ln4[$ln]." ".$option4[$key];
echo "</p>";
$count += 1;
if ($count == 10) {
break;
}
}
}
if (@$_GET['display'] == 2) {
for ($id = 0; $id < 10; $id++) {
$score = levenshtein(mb_strtolower($query), mb_strtolower($option1[$id]));
$score_min = $score;
$score = levenshtein(mb_strtolower($query), mb_strtolower($option2[$id]));
if ($score < $score_min) {
$score_min = $score;
}
$score = levenshtein(mb_strtolower($query), mb_strtolower($option3[$id]));
if ($score < $score_min) {
$score_min = $score;
}
$score = levenshtein(mb_strtolower($query), mb_strtolower($option4[$id]));
if ($score < $score_min) {
$score_min = $score;
}
$scores[$id] = $score_min;
}
asort($scores);
$scores as $key => $value) {
echo "<p>";
echo $ln1[$ln]." ".$option1[$key];
echo " ".$ln2[$ln]." ".$option2[$key];
echo " ".$ln3[$ln]." ".$option3[$key];
echo " ".$ln4[$ln]." ".$option4[$key];
echo "</p>";
}
}
}
?>
</div>
</article>
</div>
</body>
</html>
SNSでシェア