2010年5月5日水曜日

Servletでのパラメータ解析(3)

HttpプロトコルのGETリクエスト時に、パラメータの変数値などに日本語が含まれた場合のパラメータ解析方法について「Servletでのパラメータ解析について(2)」のようにするれば、各ブラウザでの日本語文字コード変換に合わせてパラメータ値を取得が可能であるとお話しました。
しかし、この方法で上手く行かない場合があることが判明しました。
ブラウザからURLを指定時に、URLにパラメータを指定してGETリクエストを発行する以外の方法として、以下のようなHTMLによりGETリクエストを発行することが可能です。

<html>
<body>
<form name="f1" action="http://localhost:8080/TestServlet/Test" method="get">
<input type="text" name="p1">
<input type="submit" nput>
</form>
</body>
</html>

このようなHTMLでは、テキスト入力された日本語文字列をどのような文字コード(ShiftJIS,EUC,UTF8など)で出力するか指定することができます。

例えば、
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
と言うタグがHTML上に存在していれば、入力フィールドで指定された日本語文字列は、ShiftJISに変換され、URL上のパラメータとして指定されます。
「Servletでのパラメータ解析について(2)」では、各ブラウザに合わせて固定の文字コードに変換するようになっていますが、同じブラウザであっても、HTMLからGETリクエストを行った場合には、HTMLの書き方によって、文字コードがShiftJISになったり、UTF-8に成ったりします。

ブラウザからのURL指定によるGETリクエストと、HTMLからのGETリクエストでの違いがあれば、何らかの対応も可能でしたが、Servletで渡されるHttpServletRequestパラメータからは、全く違いがないために処理を変えることが不可能であることが判明しました。Webアプリケーションでは、各社ブラウザでの動作の違いによって実装が面倒ですが、なんでこんな仕様になってしまったのでしょうか?標準化して欲しいものです。

対応方法としては、ブラウザからの直接GETリクエスト指定時と、HTMLからGETリクエスト時に渡すパラメータで、文字コードを指定するなどの対応が必要かと思います。

2010年5月3日月曜日

Servletでのパラメータ解析(2)

「Servletでのパラメータ解析について(1)」では、ブラウザから「http://localhost:8080/TestSevlet/Test?p1=あいう」などのように直接URL指定を行った場合に、後続のパラメータの処理がブラウザによって異なることをお話しました。
各ブラウザの動作を、プロトコルヘッダのUser-Agentの特定のパラメータから引き出せないか、Webなどで確認したところ、このUser-Agentは、各メーカーが勝手に定義しており定義などないようです。ただし、各ブラウザメーカーともに、他社のブラウザと同等の機能を提供したいので、あまり、突飛なUser-Agentは存在していないようです。
したがって、User-Agentからブラウザの種別を判別して、String命令で各ブラウザにあわせて変換する文字コードを個別に実装するしかなさそうです。

なお、本日、Operaブラウザでの動作を確認しました。すると「http://localhost:8080/TestSevlet/Test?p1=あいう」などと指定するとChromeや他社ブラウザとも異なり、JISコードで展開しているようです。
例えば、上の例をOperaブラウザから入力すると
「http://localhost:8080/TestSevlet/Test?p1=%1B$B$%22$$$&%1B(B」と展開れます。文字列の最初にある「%1B$B」は、これから2バイトの漢字を開始するための指示、最後の「%1B(B」は、2バイト漢字が終了したことを示しています。途中も文字列は、JIS漢字コードとなります。
したがって、OperaブラウザからサーブレットにGetメソッドが送信された場合には、

    String p = req.getParameter("p1");
    v = new String(p.getBytes("iso-8859-1"),"iso-2022-jp");
    
と処理する必要があります。

※ これは、Operaのバグと思われますが、上記の仕様だと、上のp1パラメータとして「あいう」などと入力した例を見てください。展開されたURLは次のようになっています。
   「http://localhost:8080/TestSevlet/Test?p1=%1B$B$%22$$$&%1B(B」
このURLでは、パラメータの途中に「&」が現れています。このため、Tomcatでは、「&」は、パラメータ指定の終了を示すので「あいう」のパラメータ値をサーブレットに渡すことは不可能となってしまいます。

なお、いままでの話を整理すると、

① User_Agentから、呼び出しブラウザがChromeだったら、
    String p = req.getParameter("p1");
    v = new String(p.getBytes("iso-8859-1"),"UTF-8");
としてパラメータ値を取り出す。

② User_Agentから、呼び出しブラウザがOperaだったら、
    String p = req.getParameter("p1");
    v = new String(p.getBytes("iso-8859-1"),"iso-2022-jp");
としてパラメータ値を取り出す。

③ それ以外だったら、
    String p = req.getParameter("p1");
    v = new String(p.getBytes("iso-8859-1"),"Windows-31J");
としてパラメータ値を取り出す。

となります。ただし、Operaのようにパラメータの日本語処理で問題が発生することも想定されますので、パラメータは極力英語とすべきでしょう。

Servletでのパラメータ解析(1)

Servletに、パラメータを指定する方法として、FORMタグ無いの変数を設定しておきHTMLでSubmitでPOSTメソッドやGETメソッドで指定する方法が一般的です。
このほかに直接、Webブラウザで
http://localhost:8080/TestSevlet/Test?P1=aaaa
等と指定する方法があります。この指定では、P1と言うパラメータに「aaaa」を指定することになります。
ところが、Chromeブラウザのみ変数の値を「日本語文字」あるいは「漢字」にする(例えば:http://localhost:8080/TestSevlet/Test?p1=あいう)と正しいパラメータが取得できないことが判明しました。文字化けが発生してしまいます。

サーブレット側のコーディングは、通常のサーブレットプログラミング同様に以下のようなコーディングとなっているだけです。
  String p = req.getParameter("p1");
  v = new String(p.getBytes("iso-8859-1"),"Windows-31J");

そこで、Chromeとその他のブラウザgetParameterで得られた変数pを使ってp.getBytesによって取り出した結果を調べました。その結果、Chrome以外のブラウザでは、取得したバイト列がシフトJISである一方、Chromeでは、UTF-8であることが分かりました。

実際、
  String p = req.getParameter("p1");
  v = new String(p.getBytChes("iso-8859-1"),"UTF-8");
とするとChromeでは正常にパラメータが取得できました。

コーディング上で見ると、RequestパラメータからgetHeaderメソッドを使って”User-Agent"のブラウザ情報を取得して、この情報がChromeブラウザであることを示していたら、変換を"UTF-8"にするのが簡単でしょう。

なお、私が調べてブラウザでのUser-Agentは次のようになっていました。

Chrome 4.1.249
Agent=Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.1.249.1064 Safari/532.5

FireFox 3.5.2
Agent=Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 GTB7.0 ( .NET CLR 3.5.30729)

IE8
Agent=Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB6.3; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)

Safari 3.0.3
Agent=Mozilla/5.0 (Windows; U; Windows NT 5.1; ja) AppleWebKit/522.15.5 (KHTML, like Gecko) Version/3.0.3 Safari/522.15.5

今回の現象面からみると、Chromeかどうか判断できれば、ChromeのみUTF-8にするだけでよいが、その他、UTF-8が標準のブラウザの場合に同等の処理が必要となります。上に掲載したUser-Agent文字列などから、言語体系が分かるのかもしれません。(User-Agent文字内には、en-US,ja等と言った言語圏を示す文字列が存在しています。
明日は、ゴールデンウィークなのでUser-Agent文字列について調べようと思います。

続きは、Servetでのパラメータ解析について(2)をご覧ください。




2010年3月23日火曜日

プログラム言語について-Basic言語-

Basic言語は、1964年にコンピュータ教育用の言語として開発されたようです。言語的には、当時使われていたFORTRANに近いものとなります。FORTRAN言語での処理系は、軽いので非常に小さなメモリでも動作できたことから、パソコン上で動作できるようになり、普及しました。
私も以前、Z80パソコン上で動作するBasicインタプリターを開発したことがあります。コンパイラーがコーディングされたプログラムのロジックが動作する機械語を作成するものですが、インタプリターは、機械語を作成するのではなく、直接、動作します。

例えば、以下のBasicプログラムを実行した場合には、
10 A = 10
20 FOR I=1 TO 3
30 A = A+1
40 NEXT I
をRUNコマンドで実行すると、次のように動作します。
① 行番号10でA=10を実行します。変数Aに10を代入する命令ですが、変数Aは未だありませんので、変数Aの領域をメモリに確保して、10を設定します。
② 行番号20は、FOR文であり、そのループ変数がIであること、下限が1、上限が3であることが分かります。そこで、①と同じように変数Iを作りその中に値0を代入します。変数Iの内容が3より小さいので次の行(行番号30)を実行することになります。
③ 行番号30では、A=A+1であることから、変数Aの値を取り出し、その変数値に1を加えた結果を変数Aに格納します。
④ 行番号40では、Iの値に1を加え、行番号20に戻ります。
⑤ この動作を繰り返し、変数Iが3を超えるまで③④を繰り返します。Iが3を超えると行番号40の後の行を探しますが、行番号40に続く行が見つからないので終了となります。変数Aには、13となります。
このように、インタプリターでは、順にプログラムを実行するようになります。

2010年3月7日日曜日

なぜ -1 と-1 を掛けると +1 になる(その2)

このブログサイトで以前、-1×-1が+1になる理由について書いていました。そのときの解法は、乗算の分配の法則と交換を使ったものでした。
一週間ほど前から、図書館で「虚数の話」と言う本を借りて読んでいます。内容は、タイトルどおり虚数の話ですが、この中で、その理由が書かれていました。
虚数とは、二乗してマイナスとなるような数ですが、実は、虚数はそれだけではなく、数を直線上の点として表現するではなく、数を平面上の点として拡張して表現すること重要な要素であるそうです。
数を直線状の値ではなく、平面状の値でとすることで、ベクトルの加算や減算の定義は簡単ですが、乗算については、それぞれの線の長さを乗算した後、その方向を回転することで、従来の乗算と拡張みなすことが可能となりました。
マイナスとは、180°回転した方向を示すので、-1×-1では、先ず-1で180°回転し、次の-1でさらに、180°回転することから、元の位置に戻り0°である+1となります。


詳しくは、ポール・J・ナーイン氏の「虚数の話」をご覧頂くことが良いかと思います。少し難解なので根気強く読むことが必要かと思います。エッセンスが書いてあるのでは、Newtonの2008年12月号ですが、少し断片的過ぎる感じがします。みなさんも、もう一度見直すのも勉強になるかと思います。

2010年2月28日日曜日

プログラム言語について-C言語-

久しぶりです。今日は、C言語についてお話します。最近では、C言語やJavaでのプログラム開発が多くなっていますが、私が最初にC言語を使うようになったのは、約25年前でした。
当時、パソコンでのシステム開発用のプログラム言語としては、アセンブラの他には、PascalやPL/M(PL/I言語の中から、パソコンシステム開発向けに限定したもの)くらいしかありませんでした。共に、メモリの確保やポインターなどを利用したプログラミングが可能となる点が、利用されていた点かと思います。

しかしながら、Pascalではビット演算機能がなく、より高速な処理を行うには不向きでした。Pascalは、設計者であるヴィルトが本来プログラミングとは、事柄を抽象的に考えるべきであり、コンピュータ内のビット形式などを意図したコーディングはすべきでないとの考え方によるものだと思います。しかし、これが、実際のプログラミングでは、より高速に処理するために、コンピュータ内のビット形式を意図する必要がありました。

このような状況下で、使われるようになったのがC言語です。C言語では、PascalやPL/Mなどのようにメモリを動的に確保したり、ポインターが利用できること、CPU上のビット操作が簡単にできること、構文が単純でありコンパイル時間が高速であったことなどからパソコンシステム用のプログラム言語として利用されるようになったと思います。

なお、C言語は、もともとUnixオペレーティングシステムを開発するために設計されているために、面白い書き方があります。。Unixが稼動するミニコンがPDP-11であったことから、PDP-11が6ビットマシンであったころから、通常は、バイナリデータを表現する際に16進数で表現されたますが、8進数の表現のが簡単になっています
 ⇒(文字列の中に16進数表現で書き込むには、「\xAB」、8進数表現で書き込むには「\253」などと指定します)

詳しくは、パソコン昔話(なぜ1バイトは8ビット)をご覧ください。

2010年2月9日火曜日

Javaで構造体を扱う

Javaは、オブジェクト指向の言語と言われますが、いままでC言語などを使ってプログラミングしていた人にとっては、C言語の構造体のメンバーに関数が追加されたものがクラスと考えることができます。詳しくは、Cプログラマーから見たJavaをご覧下さい。
したがって、JavaでのC言語の構造体は、メソッドのないクラスを定義することで利用可能となります。Javaのクラス内で構造体を利用するには、クラスの内部に別ファイルで定義したClassを利用することで対応できますが、この方法だとファイルの数が増えてしまうことが欠点となります。
各クラスのロジックをコーディングする時に、一時的に構造体を使いたくても別にファイルを作成することが必要になってしまいます。
このような場合に、簡単に構造体を作る方法がわかりました。

このような場合、クラス定義のファイルに、一時的に利用する構造体を「final」を付けたクラスとして定義します。

final class xxx_item {
 double key;
 String value;

 public xxx_item (double p1, String p2) {
  key = p1;
  value = p2;
 }
}

public class xxx_list {
 ・・・・・・・
 メソッドなどで
 xxx_item temp = xxx_item(10,"aaaaa");  
 temp.key は、10
 temp.value は、"aaaaa"
 となります。
}

このようにすると、1つのクラス定義でも簡単に構造体を利用することが可能となります。
参考にしてやってみてください。