PHP応用編

電子辞書を作る

1. 設計と準備

 応用編3つ目は電子辞書を作りましょう。とはいっても何でも調べられるようなものはできませんので英和辞書。機能的にもシンプルなものですが、応用すれば辞書だけでなく、データベースの検索システムや対話型システム、さらには対話AIまで作ることも可能です。
 そこでまずは電子辞書の設計です。イメージとしては、英単語を入力するとそれに対応した意味が表示される、という感じでしょうか。どうせなら調べた履歴も表示しておいた方が便利なので、前の資料で作った掲示板のような感じで、英語→意味、英語→意味・・・というように表示していくことにしましょう。
 ただ掲示板のように、入力画面→確認画面→登録画面→入力画面の表示更新、というページ展開は不要です。掲示板でやり取りされるメッセージなら「書いたことが間違ってないか」確かめて登録した方がよいでしょうが、自分で調べる英単語にいちいち確認画面とか「登録ありがとう画面」はいらないでしょう。英語の勉強をしながら使うことを考えると「入力したらすぐその画面に結果表示」ぐらいスッキリさせた方が使いやすいと思います。
 それから英語辞書データの検索です。本来ならばデータベースを使って、ということになりますが、この資料は超入門ですのでデータベースは使いません。テキストファイルをデータファイルとして使います。膨大な辞書データだと厳しいでしょうが、数万件ぐらいのデータなら大丈夫です。
 そして検索結果をテキストファイルに書き込みます。検索と結果の履歴を表示させるためです。検索結果の表示処理はこのテキストファイルのデータを読み込んで行います。この辺りは前の資料の掲示板と同じ仕組みですね。というわけで、このような電子英語辞書を作ることにしましょう。
 準備としては、2つのphpファイルと2つのテキストファイルを作ればよいでしょう。phpファイルは、入力&結果表示の画面処理と、データを検索して意味データを取得する処理です。テキストファイルは、英語の辞書データファイルと、検索結果履歴の記録データファイルです。
 これまでと同じく、phpkisoフォルダのひな形ファイルhina.htmlを[Ctrl]+[c]でコピーし、[Ctrl]+[v]を3回やってファイルを3つ作ります。そして以下のように名前を変更して下さい。ファイル名の「a」「b」「c」と拡張子の違いに注意して下さい。
  • ouyou03a.php (入力&結果出力画面)
  • ouyou03b.php (検索&履歴データ登録画面)
  • ouyou03c.txt (検索履歴データ)

SPONSORED LINK

 それから英和辞書データouyou03d.txtは、こちらから右クリックして保存して下さい。自己解凍形式の圧縮ファイルになっています。ダブルクリックして解凍して(自己解凍キーはPHP100%)、他のファイルと同じphpkisoフォルダに保存しておいて下さい。
 一方でほかのファイルも準備しておきましょう。まず検索履歴データouyou03c.txtは中身をクリアしておく必要があります。テキストエディタ(Terapad等)にドロップ&ドラッグして開いてすべて削除して上書き保存して閉じましょう。
 次に、入力&結果出力画面ouyou03a.phpと、検索&履歴データ登録画面ouyou03b.phpです。テキストエディタで開いて以下のように編集します。応用編では他の資料と同様に「■■■」で伏字にしてあります。「■■■」が3文字とは限りませんので何が入るか考えましょう。

  ouyou03a.php
  

  ouyou03b.php
  

 大丈夫と思いますが念のため説明します。まずouyou03a.php。一応PHPファイルとなっていますが実質的にはPHPスクリプトはなく、HTMLだけの内容になっています。これまでの資料で何度も出てきているデータの受け付けを<form>タグと<input>タグなどで実装しています。英単語データの入力を受け取り、postメソッドでouyou03b.phpに向けて送信することになっています。「■■■」は簡単なので思い出して下さい。
 続いてouyou03b.phpです。HTML部分はOKですが、PHPスクリプトの最初のところが少し難しく感じるかもしれません。でも一つ前の資料で簡易掲示板を作った時にもやったのでぜひ思い出してほしいところ。postメソッドで届いたデータを受け取り、無害化した上で変数に代入しています。postメソッドの受け取り変数を、無害化の関数の中に直接設定しています。つまり「■■■」は・・・わかりますね。わからない人は、PHP基本編・データ受け渡し無害化の説明を確認しましょう。

SPONSORED LINK


 できたらXamppが起動していることを確かめた上で、ブラウザから「localhost/phpkiso」にアクセスしouyou03a.phpを開いてみて下さい。適当な英単語を入れてみて送信すると「英単語:~」と表示されればOK。ouyou03b.phpから戻る仕組みはまだないのでブラウザの戻るボタンで戻って下さい。未入力の場合や、<font size="7">のようなタグが入力された場合も確かめておきましょう。

  

  

  

2. 検索処理

 ここからが電子英和辞書のキモ。検索処理です。どのように作ったらよいでしょうか。
 英和辞書データouyou03d.txtは、既にダウンロードして保存しましたので、これを読み込んで、入力された英単語の文字列と比較し、一致するものを出力する、というのが基本的な流れになります。なお、英和辞書データouyou03d.txtは、内容を見ていただくとお分かりの通り、「<>」で区切られた「英単語<>意味」という形式の1行1件のデータになっています。というわけで、ouyou03b.phpを次のように編集しましょう。

  ouyou03b.php(php部分以外は省略)
  

 内容を確認しましょう。最初の6行は先ほどと同じです。修正したのはelse以下の{ }内の2行目からです。
 まず6行目のecho命令で入力された単語データを表示した後、7行目で「$imi = 'マッチしませんでした。<br>';」としています。つまり変数$imiに「マッチしませんでした」という文字列を設定しているわけです。これは辞書データの全てを調べた結果、マッチしなかった場合のメッセージです。なぜここで設定しているかは続きを見ていくとわかってきますので、とりあえず一旦置いておきましょう。
 次に、辞書データファイルouyou03d.txtを読み込んでいます。「■■■」は関数ですが何が入るでしょうか。コメントにある通りファイルを読み込んで配列に指定する処理です。「$dictdatalist」は配列です。わかりますよね。
 続いてforeachループです。今設定した配列「$dictdatalist」のすべての要素を一つずつ$valueに代入して処理していきます。
 foreachループの中では、まず$valueの内容を「<>」という記号で分割し、分割された要素を配列「$dictdata」に格納しています。辞書データファイルには「英単語<>意味」という形式で辞書データが設定されていましたので、英単語が「$dictdata[0]」に、意味が「$dictdata[1]」に入れられます。ですのでここでの「■■■」は、文字列を区切り記号で分割して配列に格納する関数が入ります。
 そしてif文です。入力された単語の文字列が入った$tangoと、辞書データファイルから順に読み込んだ単語データの$dictdata[0]を比較しています。「==」(イコール2つ)ですので両辺が同じであれば、$imiに$dictdata[1]の内容つまり「意味データ」を代入した上でforeachループを終了させています。「■■■」はループを中断して抜ける関数です。元々$imiには「マッチしませんでした」という文字列が入っていましたが、マッチした時点で辞書データから取得した意味データで書き換えられることになります。ですのでループ後の「echo $imi.'<br>';」では、マッチした意味データが表示されます。
 ではif文で同じデータがなかった場合はどうなるでしょうか。foreachループの最後までいってマッチするものがなかったとすると、$imiの内容は書き換えられず「マッチしませんでした」という文字列のままです。ループ後の「echo $imi.'<br>';」では「マッチしませんでした」と表示されます。最初のところで$imiに「マッチしませんでした」という文字列を設定していたのはこのためです。
 完成したら実行してみましょう。「localhost」経由でouyou03a.phpにアクセスして英単語を入力してみて下さい。意味が表示されましたか?うまくいったら今度は適当な文字列を入れてみましょう。「マッチしませんでした」と表示されたらOKです。

  

  

  

  


SPONSORED LINK


3. 検索結果の保存処理

 どんどん行きましょう。今回の電子英和辞書では、入力した英単語とその結果の意味を、英単語→意味→英単語→意味・・という具合に対話形式で表示することにしていました。そのように表示するためには、入力した英単語と結果の意味のセットを記録しておく必要があります。
 一方でその英単語と意味のセットの記録は、読み出して使いやすいことも重要です。使い勝手が悪いというのはよろしくありません。ただ、既に英和辞書データがあるので、あれこれ変えるよりも、それと同様の形式にしておけば、ワンパタでいけるので楽です。というわけで、英和辞書データと同じく「検索した文字列<>検索結果」という形でファイルに保存するようにしましょう。
 ところでこのようなテキストデータのファイルへの保存はどうするんでしたっけ?ちょっと思い出しつつサンプルを確認しましょう。

  ouyou03b.php(php部分以外は省略)
  

 前の方はほとんど先のサンプルと同じです。追加されたのは最後の数行。echo分の次の行からです。コメントに「追記モードでのデータの書き込み」とありますが、これを実装すればよいわけです。テキストファイルにデータを書き込む方法は、これまで何度かやってきましたが、我々がパソコンで作業するときと同じでした。ファイルを開いて、内容を編集して、保存して、閉じる、という流れです。PHPプログラムでも同様です。
 サンプルを見るとまず「$fp = ■■■('ouyou03c.txt','■■■');」となってます。1行に2か所も「■■■」があるなんてイジワルなようですが、流れを見れば難しくありません。ここでやりたいのはファイルを開くこと。検索履歴データを保存するouyou03c.txtが指定されていることからもわかります。最初の「■■■」にはファイルを開く関数が入りますよね。後の「■■■」にはファイルを開くモードが入ります。「追記」はなんでしたっけ?ひとつの■は1文字とは限りませんよ。思い出して下さい。
 ファイルを開いたら書き込むデータを成形して書き込み処理を行います。「■■■($fp, $tango.'<>'.$imi."¥n");」の「■■■」にはやはり関数が入ります。$tangoの内容と記号「<>」、$imi、そして改行コード「¥n」を結合して、$fpで指定されたファイルに書き込んでいます。改行コード「¥n」は覚えてますか。テキストエディタやwordファイルの中で行われる改行を指定する記号です。
 書き込みが終わったら・・・ファイルを閉じる処理が続きます。最後の「■■■」はファイルを閉じる関数です。プログラム内のコメントにある通りです。
 というわけで理解できたら動作させてみましょう。でもouyou03a.phpにアクセスして検索し、ouyou03b.phpに展開しても見た目は変わりません。変わったところは記録データファイルですね。何度か検索したら、phpkisoフォルダにアクセスしてouyou03c.txtを開いて確かめて下さい。今検索した文字列とその結果のデータが「<>」記号で結合されて保存されていればひとまず大丈夫です。
 でも、これでは改行が入りすぎる問題が起きます。つまり、データ行とデータ行の間に空白行が入る問題です。実際、ouyou03c.txtを開いたら、以下のイメージのような空白行に気づいた人もいるはずです。なぜ空白行が入るのでしょうか。次にこの問題を考えましょう。

  


SPONSORED LINK


4. 見えない改行文字の混入

 上記サンプルを動かすと一見うまくいっているように見えますが、検索履歴の記録データファイルをよく見ると、余計な空白行が入っていることがわかります。つまり、プログラムが文法的には正しいものの、論理的におかしいところがあるわけです。いわゆる「論理エラー」と言いますが、エラー表示が出ないのでかえって直すのが厄介だったりします。応用編なので、ここでの論理エラーの原因を考えてみましょう。
 上記のイメージの通り、記録データファイルに余計な空白行が入るわけですが、一方で、マッチしない文字列を入れて「マッチしませんでした」と表示されているところは空白行がありません。つまり、マッチした時だけ問題が起きていることになります。
 さらにouyou03c.txtをテキストエディタで開いて確かめると、空白行には、半角スペースやTabスペースなど目に見えない何かが入っているわけではなく、何も入力されていない状態であることがわかります。どういうことか。それは空白行は改行コードだけが入力されている、ということを意味します。
 ではどこで改行コードが混入したか、ということです。ファイル書き込みの個所で「¥n」を設定していますが一つだけですので、これとは別に見えない改行コードが含まれているわけです。ただ、空白行には何もないわけですから、指定した改行記号「¥n」と連続してその前か後に改行コードが混入していることになります。ファイル書き込みの際、改行記号「¥n」の後には何もないので何かが混入することはないとすると、改行記号「¥n」の前に改行コードが含まれることが予想されます。つまり、改行記号「¥n」の前にある$imiがアヤシイ、となるわけです。
 さらに、マッチした時だけ含まれることを考えると、辞書データファイルから読み込んだデータに原因があるんじゃないか、と気付けたら素晴らしい。そうです。辞書データファイルから読み込んだデータは、1件1件のデータが1行ずつに分けられています。つまり、データの最後に改行コードがくっついていますが、これが悪さをしているわけですね。
 というわけで、この改行コードを取り除いてあげましょう・・・ということですが、どの位置でどのように取り除くか。
 どのように取り除くかは、前の資料でやりました。改行コードを置き換えてやるんでしたね。改行コードには「¥r¥n」と「¥n」と「¥r」があって、これを関数で別の文字列に置換すればOKでした。今回は不要な改行コードですので「''」(シングルコーテーション2つ)に置き換えれば、改行コードは消えることになります。
 次にこの処理をどこに入れるか、です。問題は、入力された文字列が英和辞書の登録英単語にマッチし、その結果を履歴データファイルに書き込む際に生じるわけです。ですので例えば、ファイルから読み込んだデータのすべてに置換処理を行ったり、foreachループで取り出された$valueにいちいち処理を行うというのは無駄です。データがマッチした場合だけ、ファイルに書き込まれる直前に置換すれば、1件だけ処理すれば済みます。ですので、if文でマッチした直後か、ファイルに書き込むためにファイルオープンする直前かぐらいで処理を行えばよいでしょう。
 以下のサンプルで確かめましょう。

  ouyou03b.php(php部分以外は省略)
  

 追加したのはforeachループの後のecho文の後。コメント含めて4行分です。
 まず「改行記号を配列に設定」とコメントのある箇所。3種類の改行コードを配列$kaigyoに設定しています。「■■■」はわかりますか?配列を定義する関数です。
 次に「改行記号を改行タグに置換」とコメントのある箇所。ここでは、変数$imiの内容に含まれる3種類の改行コードを「''」(シングルコーテーション2つ)つまり「文字列なし」に置き換えています。「文字列なし」に置き換えとはすなわち削除。3種類の改行コードのどれが含まれても削除する、ということです。ここでの「■■■」は、文字列の置換の関数です。str・・・おっと書きそうになりました。前の資料でやりましたね。
 できたら実際に動かして確かめましょう。ouyou03c.txtはテキストエディタで開いて内容をクリアして保存し閉じておきましょう。余計な改行はなくなりましたか?

  


SPONSORED LINK


5. 検索履歴の表示

 今回作る電子英和辞書は、検索履歴を表示する仕様にすることにしていました。検索文字列の入力欄の下に、これまで調べた英単語と意味のセットをずらっと並べたい、ということです。
 検索履歴のデータファイルはouyou03c.txtとしてすでにできています。追記モードでデータを追加していきますので、このファイルの中には、古いものから下方向へ新しい履歴が並び、最後が一番新しい検索結果、ということになります。検索履歴もそのまま同じように表示すれば簡単ですが、検索履歴が増えてくると最新の検索結果が下の方に行ってしまうので不便です。ですので表示は、新しいものを上に、古いものが下に来るように逆順に並べ替えたいと思います。
 処理の流れとしては、まずouyou03c.txtを読み込んで配列に格納。配列を逆順にして順に表示していく、という形になるでしょう。というわけでサンプルを見ましょう。

  ouyou03a.php(php部分以外は省略)
  

 順に見ましょう。まず最初のところ。検索結果の履歴が保存されたouyou03c.txtを読み込んで配列$rirekilistに保存しています。「■■■」はファイルを読み込んで1行ずつに分けて配列に格納する関数です。上でも登場しました。
 次に配列$rirekilistを逆順に入れ替えています。「■■■」は、簡易掲示板を作る資料にも出てきた関数です。配列をリバースするやつを思い出して下さい。
 その上でforeachループを使って、配列$rirekilistの要素を取り出して処理しています。このとき検索文字列と検索結果を別々に表示するために、両者を区切り記号「<>」で分割して配列$rirekiに格納しています。検索履歴のデータは「検索文字列<>検索結果」という形式で保存されていますので$rirekiの要素は2つ。$rireki[0]と$rireki[1]にデータが保存されます。ここの「■■■」も再び登場です。もう覚えましたか?
 できたらlocalhost経由でouyou03a.phpにアクセスしてみましょう。ouyou03c.txtの履歴データが残っていれば(消されてなければ)、検索文字列の入力欄の下にこれまでの検索履歴が新しい順に表示されるはずです。以下は出力イメージです。検索文字列がヒットしない場合が「マッチしませんでした。です」となっているのが変ですがそこは少し工夫すれば改良できますので、とりあえずこれでOKとしましょう。

  


6. ページのリダイレクト

 完成に近づいてきましたがもう一つ。辞書データを検索しつつ、検索結果を履歴データファイルに保存するouyou03b.phpから入力画面に戻る仕組みが実装されていません。
 前の資料の簡易掲示板のように、最後のページでトップページへのリンクや戻るボタンを設置する方法もありますが、今回は電子辞書ですし、検索文字列の入力画面に、検索文字列と結果のセットが表示されるようにしましたので、いちいちクリックして戻るのは面倒・・・ユーザーフレンドリーではありません。というわけで、ouyou03a.phpから検索文字列を入力してouyou03b.phpに移ったらすぐに検索&履歴登録処理と同時にouyou03a.phpに戻るリダイレクトの仕組みを作りたいと思います。実際に使うと、検索文字列の入力画面で英単語を入力したらそのまま同じページ内に英単語と意味のセットが追加表示される、というような感じになります。

SPONSORED LINK

 PHPでのリダイレクト処理には「header()」関数を使います。使い方は次の通りです。

  header ('Location: ジャンプ先URL');

 簡単ですね。「ジャンプ先URL」には「https://hayalab.com/」のようなフルのURLでも、同じサーバー内のページにジャンプするなら「../pages/home.php」のような相対パスでも指定できます。あるいはフォルダも同じなら「ouyou03a.php」のように直接ファイル名を指定しても行けます。
 ただ、header()関数を使うときには、いくつか注意事項があります。まずは、header()関数の前には何も出力させない、ということです。リダイレクトで画面が表示される前に別ページに飛ばすわけですから、HTMLの基本タグとかいらないわけです。スペースや改行もダメです。
 それからUnicodeでプログラムを書いて保存するときにつけられるBOM(Byte Order Mark)という識別情報が悪さをすることがあるようです。この資料で想定しているテキストエディタ「Terapad」なら通常BOMなしで保存されますが、別のテキストエディタを使っている場合はちょっと意識してみて下さい。
 というわけで実際にやってみましょう。ただ、ちょっと盛大に消したりしますので、ここまでに作ったファイルはバックアップ取っておきましょう。
 ここでheader()関数を追加するのは、ouyou03b.phpですので、これを[Ctrl]+[c]でコピーし[Ctrl]+[v]で貼り付けます。「ouyou03b - コピー.php」というファイルができますがバックアップはこれでOKです。
 というわけで「コピー」ではない方のファイル「ouyou03b.php」を次のサンプルのように編集しましょう。

  ouyou03b.php
  

 まずはリダイレクトするヘッダー関数を設定します。どこに設定したらよいでしょうか。最初のところに設定すると検索したり履歴データファイルに記録したり処理する前にジャンプしてしまうことになるのでダメです。ということは最後のところが正解、ということになります。最初のif文で入力チェックをして未入力の場合でも入力があった場合でもそれらの処理が終わってからジャンプする必要があるので、if~else~の{ }の外になります。{ }に注意して下さいね。書き方はサンプルでは一応「■■■」としていますが、上でやったばかりですからわかりますね。
 次にこのリダイレクト処理をする以前に、何も出力してはダメ、というルールに従うようにしなければなりません。一つはこのPHPサンプルの前後にあるHTMLの基本タグです。つまりこのouyou03b.phpの「<?php~?>」以外の箇所をすべて削除する必要があります。「<?php」の前にあるHTMLの基本タグと「?>」の後にも基本タグがあります。スペースや改行を残してはいけませんよ。ouyou03b.phpの1行目に「<?php」が来るようにして下さい。
 次にPHPプログラムでリダイレクト前に出力しているところを出力させないようにする必要があります。「出力」と言えばechoとかprintです。この資料では基本的にechoを使っていますのでechoの箇所を修正しましょう。
 「出力させない」ということなので削除することもできますが、削除してしまうと後から何があったかわからなくなってしまうので、コメントアウトして無効化しておきましょう。コメントアウトの方法はわかりますか?「//」(スラッシュ2つ)でその行のそれ以降のプログラムが無効化できました。プログラム内のecho文を探すと3か所あります。最初のif文のところの未入力メッセージと、else{ }の最初の英単語の表示、マッチング処理後の結果の表示です。これらをすべて「//」でコメントアウトします。
 編集が終わったら実行しましょう。ouyou03a.phpにアクセスして検索文字列を入力してみて下さい。入力欄の下にすぐ入力内容と結果が表示されるはずです。ouyou03b.phpには展開しているのですが表示せずにリダイレクトされるのでouyou03a.phpだけで処理されているように見えます。リダイレクトはよく使われるので覚えておきましょう。


SPONSORED LINK


練習問題

 このページで用いたファイルを使い以下の課題に取り組みましょう。
  1. 「5. 検索履歴の表示」の最後のところで、検索文字列がヒットしない場合に、「マッチしませんでした。です」とおかしな表示になる点を改善せよ。様々な方法がありうる。
  2. 検索履歴が増えすぎると見にくいため、新しいものを最大10件まで表示するようにせよ。基本は10件表示とし指定した時だけ全件表示するなど可変表示にしてもよい。
練習問題の出力例

ページのトップへ戻る
SPONSORED LINK