JSP: 検索(正規表現の利用)

ここでは、送信データを利用した検索について演習します。 完全に一致する場合と、部分的に一致すればいい場合とがあり、その 両者を実現する方法を学びます。 部分的な一致を調べる場合には、正規表現に関する 知識が必要になります。かいつまんで説明していますので、必要があれば インターネットで検索するなどして調べてみてください。

日本語文字コードの変換

JSPでは日本語データのやり取りの際には、文字コードを意識しなければ いけません。Javaによる内部処理はUnicodeが使われており、 WebブラウザからのデータがUnicodeでない時には、自動的に判断 してくれるようになっていますが、文字化けが起こる場合もあります。 正しい動作を保証するためには文字コードを明示した方がよさそうです。

下記では、変数Nameに格納されたデータをShift_JISへ変換するまでの 処理を表しています。

String orgName = request.getParameter("Name");
String convertedName = new String(orgName.getBytes("ISO-8859-1"), "Shift_JIS");

上記のorgNameに格納されているデータに対して、 getBytesというメソッドにISO-8859-1を明記して、 ISO-8859-1という文字コードで バイト単位でデータを取り出しています。そして、その取り出したデータを Shift_JISに変換して、StringクラスのconvertedNameというインスタンスに 格納している、という流れを意味しています。これにより、 Shift_JISで作成されたJSPファイルからのデータも、文字化けせずに 済みます。

上記ではShift_JISへ変換してWebブラウザに出力するよう明記していますが、 下記のようにJISAutoDitectという方法で自動判別による 変換を指示することもできます。ですが、書籍やWebページを見てみると、 誤った判別の可能性もあり、上記のように文字コードを明記する方が無難との ことです。

String orgName = request.getParameter("Name");
String convertedName = new String(orgName.getBytes("ISO-8859-1"), "JISAutoDitect");

追記事項:

日本語の文字化けに対して、Servlet API2.3から、 setCharacterEncoding()というメソッドが使えるようにな りました。したがって、一度だけ、

<% request.setCharacterEncoding("Shift_JIS"); %>

と記述するだけで、後はgetParameter()メソッドでデータを受け取っても 文字化けしなくなります。今後はこちらを使用していきます。

それでは下記の見本、address1.html、address1.jspを作ってみましょう。 見本では、名前とメールアドレ スを送信して処理するだけのHTML、JSPファイルです。 両者をSun ONEを使って自分自身で入力して作成し、送信される日本語デー タがどのように表示されるか、確認しましょう。

address1.html

<html>
<head>
<title>メールアドレスの検索</title>
</head>

<body>
<form method="post" action="address1.jsp">

<p>
入力して下さい
</p>

<table border="1">
<tr>
  <th>名前</th>
  <td><input type="text" size="30" name="yourName" /></td>
</tr>
<tr>
  <th>メールアドレス</th>
  <td><input type="text" size="30" name="yourMail" /></td>
</tr>
</table>
<br />
<input type="submit" value="送信" />

</form>
</body>
</html>

address1.jsp

<%@ page contentType="text/html;charset=Shift_JIS" %>
<html>
<head>
<title>メールアドレスの検索結果</title>
</head>
<body>

<%
  String orgName = request.getParameter("yourName");
  String yourName = new String(orgName.getBytes("ISO-8859-1"), "Shift_JIS");

  String orgMail = request.getParameter("yourMail");
  String yourMail = new String(orgMail.getBytes("ISO-8859-1"), "Shift_JIS");
%>
<p>
送信結果
</p>
<table border="1">
<tr>
  <th></th>
  <th>文字コード変換前</th>
  <th>文字コード変換後</th>
</tr>
<tr>
  <th>名前</th>
  <td><%=orgName%></td>
  <td><%=yourName%></td>
</tr>
<tr>
  <th>メールアドレス</th>
  <td><%=orgMail%></td>
  <td><%=yourMail%></td>
</tr>
</table>

</body>
</html>

エスケープ文字への変換

address1.jspでは単に文字コードを変換しただけでした。ここでは form要素を利用したクロスサイトスクリプティングという、 悪意を持った攻撃への対策として、特定の特殊記号(メタキャラクタ)を 別の表現に置換する エスケープ文字への変換を行います。

クロスサイトスクリプティングに関するWebページの例

メタキャラクタとその置換

メタキャラクタ エスケープ文字
< &lt;
> &gt;
& &amp;
" &quot;

上記のように、form要素から送られてきた「<」のような記号は、対応する エスケープ文字である「&lt;」に置換する必要があります。

以下では、 address1.jspに、エスケープ文字への変換処理を追記したaddress2.jspを 示してありますが、 <%!〜%>の間で、変換処理のメソッドを宣言 しています。Sun ONE上で作成してください。address2.htmlは address1.htmlをコピーして作成してください。ただし、 「action="address1.jsp"」の箇所を 「action="address2.jsp"」のように 修正してください。

動作確認の際に、入力データにわざと「<」のような特殊記号を入力して 送信してみてください。出力結果では正しくエスケープ文字へ 変換されているか、Webブラウザの「ソースの表示」で確認しましょう。

address2.jsp

<%@ page contentType="text/html;charset=Shift_JIS" %>
<%!
private String escapeChar(String strInput){
  StringBuffer strOutput=new StringBuffer();
  for(int i=0;i<strInput.length();i++){
    switch(strInput.charAt(i)){
      case '<' :
        strOutput.append("&lt;");
        break;
      case '>' :
        strOutput.append("&gt;");
        break;
      case '&' :
        strOutput.append("&amp;");
        break;
      case '"' :
        strOutput.append("&quot;");
        break;
      default :
        strOutput.append(strInput.charAt(i));
        break;
    }
  }
  return strOutput.toString();
}
%>
<html>
<head>
<title>メールアドレスの検索結果</title>
</head>
<body>

<%
  request.setCharacterEncoding("Shift_JIS");

  String yourName = request.getParameter("yourName");
  yourName = escapeChar(yourName);

  String yourMail = request.getParameter("yourMail");
  yourMail = escapeChar(yourMail);
%>

<p>
送信結果
</p>
<table border="1">
<tr>
  <th>名前</th>
  <td><%=yourName%></td>
</tr>
<tr>
  <th>メールアドレス</th>
  <td><%=yourMail%></td>
</tr>
</table>

</body>
</html>

配列における完全一致検索

それでは、送信されてきたデータが予め用意してあるデータに合致するかどうか 調べてみましょう。JSPスクリプト内に配列を用意し、メールアドレスを格納し ています。for文による繰り返し処理で、送信データと配列の各要素の 内容が等しいか調べています。文字列が等しいかどうかは equalsメソッドを利用しています。

address3.jsp

<%@ page contentType="text/html;charset=Shift_JIS" %>
<%!
private String escapeChar(String strInput){
  StringBuffer strOutput=new StringBuffer();
  for(int i=0;i<strInput.length();i++){
    switch(strInput.charAt(i)){
      case '<' :
        strOutput.append("&lt;");
        break;
      case '>' :
        strOutput.append("&gt;");
        break;
      case '&' :
        strOutput.append("&amp;");
        break;
      case '"' :
        strOutput.append("&quot;");
        break;
      default :
        strOutput.append(strInput.charAt(i));
        break;
    }
  }
  return strOutput.toString();
}
%>
<html>
<head>
<title>メールアドレスの検索結果</title>
</head>
<body>

<%
  request.setCharacterEncoding("Shift_JIS");

  String yourName = escapeChar(request.getParameter("yourName"));
  String yourMail = escapeChar(request.getParameter("yourMail"));

  String[] mailArray = {"yamada_jp@hotmail.com", "gogo@docomo.ne.jp", 
  "e01200yy@edu.tuis.ac.jp", "g03010yy@rsch.tuis.ac.jp", 
  "y_yamada@rsch.tuis.ac.jp"};

%>

<p>
送信結果
</p>

<%
  int flag = 0;

  if (yourName.equals("")) {
    out.println("名前を入力してください<br />");
    flag = -1;
  }
  if (yourMail.equals("")) {
    out.println("メールアドレスを入力してください<br />");
    flag = -1;
  }

  if (flag != -1) {
%>
<table border="1">
<tr>
  <th>名前</th>
  <td><%=yourName%></td>
</tr>
<tr>
  <th>メールアドレス</th>
  <td><%=yourMail%></td>
</tr>
</table>
<%
    for (int i=0; i<mailArray.length; i++) {
      if (mailArray[i].equals(yourMail)) {
        out.println("<p>入力されたメールアドレスはありました</p>");
        flag = 1;
        break;
      }
    } 
    if (flag == 0) {
      out.println("<p>入力されたメールアドレスはありませんでした</p>");
    }
  }
%>
</body>
</html>

正規表現による入力データの判定

今度は、入力されたデータが特定の形式にしたがっているか判断し、その形式に 合わなければ、検索する前にエラー処理を行います。ここで 正規表現 (regular expression)という処理が必要になりま す。

正規表現そのものは、UNIXのコマンドでの処理やPerlでの処理でも利用 され、またWindowsの代表的なテキストエディタである秀丸でもある 程度の機能を利用できます。Perlでの正規表現は こちらのWebページ、 また秀丸での正規表現は こちらのWebページに記載されていますので、参考にしてください。

java.util.regexパッケージで使用できる主な正規表現

正規表現 意味
. 任意の1文字が存在すればマッチ
* 直前のものが0回以上繰り返すとマッチ
+ 直前のものが1回以上繰り返すとマッチ
{m} 直前のものがm回繰り返すとマッチ
{m,} 直前のものがm回以上繰り返すとマッチ
[  ]
例:[あさと]んま
または(1文字のみ対応可能)
「あんま」、「さんま」、「とんま」にマッチ
(  |  )
例:東京(情報|農業)大学
または(1文字以上に対応可能)
「東京情報大学」、「東京農業大学」にマッチ
[0-9] 0から9までの数字にマッチ
[a-z] aからzまでの英文字にマッチ
^ 文字列の先頭にあればマッチ
$ 文字列の終わりにあればマッチ
[^ ] 否定

下記の見本、address4-1.jspでは、入力されたメールアドレスに対して、 「.+@.+」 というパターン(正規表現)にあてはまるか判定しています。 この意味は以下の通りです。

正規表現 .+ @ .+
意味 任意の1字以上の文字列 @マーク(ただの文字) 任意の1字以上の文字列

したがって、「@の前には1字以上の文字列、後にも1字以上の文字列で 構成される文字列」という条件を指定していることになります。

address4-1.jsp

<%@ page contentType="text/html;charset=Shift_JIS" %>
<%@ page import="java.util.regex.*" %>
<%!
private String escapeChar(String strInput){
  StringBuffer strOutput=new StringBuffer();
  for(int i=0;i<strInput.length();i++){
    switch(strInput.charAt(i)){
      case '<' :
        strOutput.append("&lt;");
        break;
      case '>' :
        strOutput.append("&gt;");
        break;
      case '&' :
        strOutput.append("&amp;");
        break;
      case '"' :
        strOutput.append("&quot;");
        break;
      default :
        strOutput.append(strInput.charAt(i));
        break;
    }
  }
  return strOutput.toString();
}
%>
<html>
<head>
<title>メールアドレスの検索結果</title>
</head>
<body>

<%
  request.setCharacterEncoding("Shift_JIS");

  String yourName = escapeChar(request.getParameter("yourName"));
  String yourMail = escapeChar(request.getParameter("yourMail"));

  String[] mailArray = {"yamada_jp@hotmail.com", "gogo@docomo.ne.jp", 
  "e01200yy@edu.tuis.ac.jp",  "g03010yy@rsch.tuis.ac.jp", 
  "y_yamada@rsch.tuis.ac.jp"};

%>

<p>
送信結果
</p>

<%
  int flag = 0;

  if (yourName.equals("")) {
    out.println("名前を入力してください<br />");
    flag = -1;
  }
  if (yourMail.equals("")) {
    out.println("メールアドレスを入力してください<br />");
    flag = -1;

  } else {

    Pattern searchPattern = Pattern.compile(".+@.+");
    Matcher searchMatcher = searchPattern.matcher(yourMail);

    if (!searchMatcher.find()) {
      out.println("正しいメールアドレスを入力してください<br />");
      flag = -1;
    }
  }

  if (flag != -1) {
%>
<table border="1">
<tr>
  <th>名前</th>
  <td><%=yourName%></td>
</tr>
<tr>
  <th>メールアドレス</th>
  <td><%=yourMail%></td>
</tr>
</table>
<%
    for (int i=0; i<mailArray.length; i++) {
      if (mailArray[i].equals(yourMail)) {
        out.println("<p>入力されたメールアドレスはありました</p>");
        flag = 1;
        break;
      }
    } 
    if (flag == 0) {
      out.println("<p>入力されたメールアドレスはありませんでした</p>");
    }
  }
%>
</body>
</html>

上記の処理の中でも、正規表現に関する部分について説明します。 まず、正規表現のパターンを記述するインスタンスが必要になります。 そのため、下記のように

Pattern searchPattern = Pattern.compile(".+@.+");

Patternクラスのインスタンスを生成しています。正規表現のパターンは、 Patternクラスのcompile()メソッドを利用して、 ".+@.+"のように記述しています。

次に、パターンを含むかどうか調べたい対象を特定します。それが、

Matcher searchMatcher = searchPattern.matcher(yourMail);

のように記述されています。Matcherクラスのインスタンスとして、 searchMatcherを生成しています。上記の処理では、yourMailに 格納されている文字列(メールアドレス)に対して正規表現があてはまるか どうかを調べようとしています。そして、

if (!searchMatcher.find()) {

は、与えられた正規表現にあてはまらなければ、if文が実行されます。 与えられた正規表現にあてはまれば、searchMatcher.find()はtrueを返します。 「!」の記号で、否定を表すことも注意してください。

課題1:上記のaddress4-1.jspを参考にし、正規表現の 部分を修正して、@の直後には、 「.以外の1文字以上の文字列」、 「.」、 「.以外の1文字以上の文字列」 があるメールアドレス のみを通し、それ以外のメールアドレスに対してはエラーを表示させたい。 そのようなJSPファイルをaddress4-2.jspとして作成し、動作確認せよ。 ちなみにJSPでの正規表現では、 文字としてのピリオドは\\. で表現される。

要求されている正規表現:

正規表現 .+ @ 自分で考えよう \\. 自分で考えよう
意味 任意の1字以上の文字列 @マーク(ただの文字) .以外の1字以上の文字列 . .以外の1字以上の文字列

エラー表示させたいメールアドレスの例:

課題1の解答(正規表現の部分だけ): 演習中の説明と若干違います

^.+@[^\\.]+\\.[^\\.]+$

課題2:課題1の条件に加えて、正規表現の 部分を修正して、@が2つ以上あるメールアドレスに対しては エラーを表示させるJSPファイルをaddress4-3.jspとして作成し、動作確認せよ。

エラー表示させたいメールアドレスの例:

課題3: @が1つだけであり、末尾がtuis.ac.jpであるメールアドレス 以外にはエラーを表示させるJSPファイルをaddress4-4.jspとして作成し、 動作確認せよ。

条件を満たすメールアドレスの例:

エラー表示させるメールアドレスの例:

課題4:課題3の条件に加えて @の前は、「m, e, s, c, gのうちの1文字」+ 「5桁の数字」+「2桁の英文字」から構成されるメールアドレス 以外にはエラーを表示させるJSPファイルをaddress4-5.jspとして作成し、 動作確認せよ。

条件を満たすメールアドレスの例:

エラー表示させるメールアドレスの例:

課題(正規表現)

上記の課題1〜4までの、正規表現の部分だけを記述したテキストファイルを作成 し、2004/01/19(月)の演習開始時に提出せよ。

提出課題の例

学籍番号:e01000ab
氏名:環境花子

解答:
  課題1:.+@[^\\.]+\\.[^\\.]+
  課題2:
  課題3:
  課題4:

配列における部分一致検索(正規表現)

今度は入力されたメールアドレスの一部分を手がかりに、配列に格納された データに部分的に一致するかどうか調べていきます。下記のJSPファイルを address5.jspとして作成しましょう。

動作確認として様々な文字列、および正規表現を取り入れた文字列で 試してみましょう。例えば、 「yamada」と「^yamada」との違い、 「jp」と「jp$」との違いを確認 してみましょう。

address5.jsp

<%@ page contentType="text/html;charset=Shift_JIS" %>
<%@ page import="java.util.regex.*" %>
<%!
private String escapeChar(String strInput){
  StringBuffer strOutput=new StringBuffer();
  for(int i=0;i<strInput.length();i++){
    switch(strInput.charAt(i)){
      case '<' :
        strOutput.append("&lt;");
        break;
      case '>' :
        strOutput.append("&gt;");
        break;
      case '&' :
        strOutput.append("&amp;");
        break;
      case '"' :
        strOutput.append("&quot;");
        break;
      default :
        strOutput.append(strInput.charAt(i));
        break;
    }
  }
  return strOutput.toString();
}
%>
<html>
<head>
<title>メールアドレスの検索結果</title>
</head>
<body>

<%
  request.setCharacterEncoding("Shift_JIS");

  String yourName = escapeChar(request.getParameter("yourName"));
  String yourMail = escapeChar(request.getParameter("yourMail"));

  String[] mailArray = {"yamada_jp@hotmail.com", "gogo@docomo.ne.jp", 
"e01200yy@edu.tuis.ac.jp", "g03010yy@rsch.tuis.ac.jp", "y_yamada@rsch.tuis.ac.jp"};
%>

<p>
送信結果
</p>

<%
  int flag = 0;

  if (yourName.equals("")) {
    out.println("名前を入力してください<br />");
    flag = -1;
  }
  if (yourMail.equals("")) {
    out.println("検索したい文字列を入力してください<br />");
    flag = -1;
  }

  if (flag != -1) {
%>
<table border="1">
<tr>
  <th>名前</th>
  <td><%=yourName%></td>
</tr>
<tr>
  <th>メールアドレス</th>
  <td><%=yourMail%></td>
</tr>
</table>

<%
    Pattern searchPattern = Pattern.compile(yourMail);

    for (int i=0; i<mailArray.length; i++) {

      Matcher searchMatcher = searchPattern.matcher(mailArray[i]);

      if (searchMatcher.find()) {
        out.println("合致したメールアドレス:"+mailArray[i]+"<br />");
        flag = 1;
      }
    }
    if (flag == 0) {
      out.println("合致したメールアドレスはありません。<br />");
    }

  }
%>
</body>
</html>

[JSPのページ] [環境情報システム論のページ]
須崎純一 京都大学大学 工学研究科都市環境工学専攻 環境情報学講座