2010年7月4日日曜日

コンピュータ昔話(PC起動をなぜブートと言うか)

みなさんは、PCを立ち上げる際にブート(Boot)するといいますが、なぜかご存知でしょうか?昔のコンピュータでは、ハードウェアリソースが限られており、そのためにシステムを立ち上げるために必要なプログラムをROMに書き込んでおくことができませんでした。
そこで、最低限の入出力を行う処理だけがコンピュータハードウェアとして用意されており、PCを立ち上げるプログラムをメモリにロードする手順(ブートストラップローダー)を行った後、立ち上げ少しずつ機能を加えながら立ち上げを繰り返しました。まるで、靴紐を編み上げるようにコンピュータを起動するために、コンピュータの立ち上げをBootStrap(靴紐)と言いました。ブートストラップからストラップが無くなり、現在では、PCを立ち上げるのにブートと言うようになりました。


サーブレットからクライアントブラウザを知る方法

お久しぶりです。一ヶ月以上空いてしまいました。
サーブレットは、クライアントブラウザからに表示指示に対して、クライアントブラウザにHTMLページを送信します。しかし、クライアントブラウザの表示機能には差があるため、クライアントブラウザによっては、出力するHTMLページを変更する必要があります。
サーブレットを構築するためのTomcatでは、リクエストを発行したブラウザを検知する方法があります。
クライアントからブラウザ表示指示を送信すると、サーブレット上のdoGetやdoPostメソッドが呼び出されます。この際のパラメータであるHttpServletRequest内のヘッダーからユーザーエージェント文字列を取得してブラウザを判別することが可能です。
世の中にある各ブラウザのユーザーエージェント文字列は、userAgent一覧のように沢山ありブラウザを特定するプログラムを作成するのは大変面倒です。

このようなとき、私は、JavaのStringTokenizerを使って対応しています。
StringTokennizerは、文字列内の空白など文字列を分割する文字(区切り文字)を指定します。すると、この文字が発見されると同時に、分割することができます。

例えば、
    StringTokenizer st = new StringTokenizer(myString, " /");
    while(st.hasMoreTokens()) {
      String s = st.nextToken();
    }
とすると、区切り文字が空白と「/」なので、myString文字列が、"aaaa/bbbb"の場合には、"aaaa"と"bbbb"に分割されます。これをnextTokenメソッドとhasMoreTokenメソッドを使って取り出します。

このメソッドを使って、ユーザーエージェント文字列を解析し、ブラウザを判断することができます。さらに、ブラウザのバージョン情報などの取得も可能です。

    StringTokenizer st = new StringTokenizer(user_agent, " ();,/");
    while(st.hasMoreTokens()) {
      String s = st.nextToken();
      if(0 <= s.indexOf("MSIE")) {
        browser = IEブラウザ
        break;
      }
      if(0 <= s.indexOf("Chrome")) {
        browser =Chromeブラウザ
        break;
      }
      ......
      ......
    }

皆さんも、使って頂ければと思います。

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月号ですが、少し断片的過ぎる感じがします。みなさんも、もう一度見直すのも勉強になるかと思います。