String <=> byte配列 の際のエンコード(続)

このQ&Aのポイント
  • Stringのエンコードに関する勘違いについて質問があります。
  • サーブレットでの日本語リクエストパラメータのエンコーディングについて誤解があった。
  • request.getParameter("test")の結果はブラウザのエンコーディングでバイト列を用い、値はそのままでエンコード名だけが指定されたものとなる。
回答を見る
  • ベストアンサー

String <=> byte配列 の際のエンコード(続)

前回の質問(4つほど前の)の続きですが、 8859-1が日本語を扱うことができるという勘違いは、 サーブレットで日本語のリクエストパラメータを使用する際の、 次のようなコードに起因してます。 String param = new String(request.getParameter("test")).getBytes("8859_1"), "JISAutoDetect"); ここで、request.getParameter("test") から返ってくる String は 8859-1エンコーディングされたものだと単純に考えていたのですが、 今回、あらためてこれについて考えてみました。 request.getParameter("test").getBytes("8859_1") で、 ブラウザのエンコーディングを用いた、パラメータを表現するバイト列が ちゃんと取得できています。 では、request.getParameter("test") の結果返ってくる String は ブラウザのエンコードでパラメータを表現するバイト列を用い、 値はそのままで、エンコード名だけを8859-1として構築されたもの、 になると思うのですが、 そういう認識で正しいのでしょうか。 また、それで正しいのなら、それと同じことを自分で行うには どうすればよいのでしょうか。 とても気になります。 もしわかる方がいらっしゃったら、是非回答お願いします。

  • Java
  • 回答数4
  • ありがとう数8

質問者が選んだベストアンサー

  • ベストアンサー
  • ranx
  • ベストアンサー率24% (357/1463)
回答No.3

Unicodeと8859-1の対応表を見つけることができました。 参考URLを見て下さい。1バイトで表現できるすべてのコードについて 対応づけがされていますので、8859-1からUnicodeへの変換は常に 1対1にできます。コードも、8ビットを16ビットに拡張しただけで、 値としては同じものです。 Harryさんの元のご質問は、こういう状態になっているかどうかを 確認したかったのではないでしょうか。 少し補足しておく必要がありそうですね。 Stringは常にUnicodeで表現されます。一つの文字は常に固有のコード で表され、同じコードでも解釈によって別の文字になるということが ありません。つまり、「エンコード」という概念の入り込む余地が 無いのです。 String str1 = new String(b, "8859_1"); は、8859-1でエンコーディングされた文字列ではありません。 バイト列bを8859-1というエンコーディングによって解釈された 文字列です。気をつけておかなくてはならないのは、任意のバイト列は 任意のエンコーディングによって解釈できるというものではないと いうことです。例えば0x80というコード1バイトだけのデータは、 SJISでは解釈のしようがありません。つまり、Stringに変換することが できないわけです。Big5等の他のエンコーディングを介在させたときに 元の文字列に戻らなかったというのはそのためだと思います。 8859-1の場合は、1バイトのコードをすべて1文字として解釈できます から、データの欠落が起きなかったわけです。 前回の回答で未定義部分があると書いたのは不十分な情報でした。 お詫びします。ただ、元々のISOによる規格を私は確認できていませんし、 Unicode Consortiumによる参考URLでも、いくつかのコントロールコード として対応づけられている部分が含まれています。これらは非表示文字 として扱われますので、Harryさんの試行で"?"と表示されたのはその ためだと思われます。次のようなやり方で1文字ずつコードの内容を 確認できると思います。  for (int i=0 ; i<str1.length ; i++)   System.out.print(" "+Integer.toHexString((int)str1.charAt(i)));  System.out.println(); なお、前回の回答で「たまたま」という表現を使ったのは、「たまたま Harryさんのプログラムでは」という意味ではなく、「たまたまサン・ マイクロシステムズ社等による現在のJavaの実装では」という意味で 使ったつもりでした。規格と実装が一致しているとは限らないと警告した つもりでしたが、Unicode Consortiumによる対応表が見つかりましたので、 今回の件については、これは無視して下さい。

Harry_
質問者

お礼

おはようございます。URL見ました。 >Harryさんの元のご質問は、こういう状態になっているかどうかを >確認したかったのではないでしょうか。 「確認」という言葉を使えるほど、 こういう状態であることを予測してませんでした。 String型が8859-1も内部的な文字表現手段として利用してるのではないか、 という方向にばかり考えが向いていたので・・・ そうではなく、そもそも8859-1がUnicodeのサブセットのようなコード構成 になっていたのですね。 あるバイト列を8859-1エンコードで解釈した結果、「?」と表示されてしまう 未定義(と思っていた)部分は、それがUnicodeのコードに変換されるときに、 すべて、同一のコードにマッピングされて、復元不可能になってしまうのでは ないか、 という疑問があったのですが、これも、これらが未定義ではなく制御文字、 しかもやはり1対1対応、ということで氷解しました。 とてもすっきりしました。 本当にどうもありがとうございます。 ところで、URLの完全一対一の対応表を見て、 ちょっとこれらの文字コードの成り立ちに興味が湧いてきました。 この辺もうすこし自分で調べてみようと思います。

その他の回答 (3)

  • ranx
  • ベストアンサー率24% (357/1463)
回答No.4

ご免なさい。参考URLを忘れていました。

参考URL:
http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT
  • ranx
  • ベストアンサー率24% (357/1463)
回答No.2

SJISは文字を1~2バイトのコードで表します。したがって、UnicodeのString 型に変換すると、コードに応じて1バイトが1文字になったり2バイトが1文字に なったりします。一方8859-1は常に1文字を1バイトで表しますから、String型 に変換しても、そのバイト数に応じた文字数になります。 したがって、 byte[] b = "あいうえお".getBytes("SJIS"); のbは10バイトで5文字を表すコードになります。ところが、 String str1 = new String(b, "8859_1"); とした場合のstr1は、元の"あいうえお"ではなく、10文字の文字列となります。 String str2 = new String(str1.getBytes("8859_1"), "SJIS"); では10バイトのコードを介してもとの"あいうえお"を復元していることになります。 例えばSJISの"あ"が8859-1では別の2文字を表し、それをSJISに戻すとまた"あ"に なる・・・ということだと思うのですが、実際のところ、私が参照したコード表では、 SJISで表された"あいうえお"にはISO-8859-1には定義されていないコードが含まれる ことになっていますので、上記の変換ができたのは、たまたま現在の実装がそうなって いただけのことかもしれません。いずれにせよ、一つのエンコーディングで処理 されたバイト列をそのまま他のエンコーディングで解釈するというのは、かなり危険な ものと言え、できれば避けるべきだと思います。

Harry_
質問者

お礼

回答ありがとうございます。 さっきの質問補足で書いたコードでは省きましたが、 str1 の内容も、もちろん出力して確認はしていました。 大部分が、「?」だったので、それが、ranxさんのおっしゃっている 定義されていないコードなのだと思います。 回答を読ませてもらいましたが疑問はまだ消えません。 もしString型が内部でUnicodeを間にかませてしまうと、 『 ???¢?????¨ 』という8859-1エンコードの文字列(str1)が Unicodeに変換された時点でバイトデータは破壊されてしまうと 思うんです。8859-1でないエンコードを使った場合、先のコードと 同様の変換ができないというのも腑に落ちません。 コンストラクタ String(byte[] bytes, String encode) が バイト列を指定のエンコードでエンコーディングされたものとして理解する だけなら、指定エンコードの種類を問わず、先のコードは必ず元の 文字列を復元できるはずです。内部でUnicodeに変換されたらもちろん バイトデータは破壊されますが、8859_1 の場合、実際の動作を見る限り それがあてはまりません。 先のコードで正しい変換ができたのは、 決して"たまたま"ではないと思います。 なぜなら、質問中にふれたサーブレットのコードも、Propertiesクラス利用 の際も同様の処理を行っているからです。 でも危険なものであるという点は私も同じ意見です。 実際、質問No,235091 のような文字化けの問題などがあるわけですから。 (このNo,235091 の質問の意味が最近やっとわかりました。)

Harry_
質問者

補足

一部訂正します。 文字化けの問題はないです。 あれは、Propertiesクラスがわざわざ内部でエスケープ処理を してくれているのが裏目にでてるだけのようです。

  • ranx
  • ベストアンサー率24% (357/1463)
回答No.1

> では、request.getParameter("test") の結果返ってくる String は > ブラウザのエンコードでパラメータを表現するバイト列を用い、 > 値はそのままで、エンコード名だけを8859-1として構築されたもの、 > になると思うのですが、 > そういう認識で正しいのでしょうか。 質問の意味がよく分からないのですが、String型はつねにUnicodeで表現 されますから、バイト列そのままということはないですよね。もっとも、 0x0020~0x007eの分については、ASCIIそのままですから、そういう意味で あれば値はそのままとも言えます。もちろん、漢字のコードなどが入り 込んでいればそういうわけにはいきませんし、円通貨記号のように、日本で 通常ASCIIとして扱っているものでも8859-1とは異なるものもありますね。 というわけで、ご質問の文章、何だかおかしいという感じですが、よく 分からないので、明確にして頂けると回答しやすいと思います。

Harry_
質問者

お礼

レスありがとうございます。 私は、自分の上記の認識であってるという自信がありません。 もし正しくないのであれば、つまるところ、質問は、 String#getBytes("8859_1") の結果、日本語が返ってくるというのは 一体どういうことか、ということになります。 byte[] b = "あいうえお".getBytes("SJIS"); String str1 = new String(b, "8859_1"); String str2 = new String(str1.getBytes("8859_1"), "SJIS"); System.out.println(str2); このコードが、正しく"あいうえお"を出力するのは確認しました。 そこで、二行目の str1 が、バイト列 b の元の値と、現在のエンコードが 8859_1 である、という情報とを両方保持してなければ、これらの 一連の変換の後、もとの文字列が復元できることが説明できない、 と思ったのが、質問で書いた"認識"の詳しい内容です。 ところで、ranxさんのレスのString型は常にUnicode...のところを読んで、 はっ、としました。言われてみればそうだった、と思ったんですが、 それを考えると、上のコードが元の文字列を出力するのが何故なのか、 全くわからなくなりました。 で、色々試してると、上のコードの 8859_1 の部分を二箇所とも BIG5 とか 別のエンコードに変えると、うまく変換できなくなることに気づきました。 それで勝手な推測なんですが、 String型は文字列表現にUnicodeだけでなく、8859_1も 利用できる、ということはないですか。

関連するQ&A

  • 困ってます。

    文字化けについて分からない事があります。 環境は RedHatLinux7.2, Postgresql7.1, Tomcat3.2.4, JDK1.4.0 です。 HTMLフォームで入力されたパラメータをサーブレットで受け取り、そのパラメータをDBのテーブルにSQLのINSERT文でデータを追加する際に日本語の部分(例:氏名)が文字化けしてしまいます。 文字化けの防止策として、 (1)String KANJINAME = new String(request.getParameter("KANJINAME").getBytes("8859_1"), "JISAutoDetect"); (2)String KANJINAME = new String(request.getParameter("KANJINAME").getBytes("8859_1"), "EUC-JP"); としても、文字化けしてしまいました。 今までサーブレット、JSP等で上記の(1)、(2)のような方法で文字化けを防いできたのですが、今回のようにDBのテーブルにInsert文で日本語を含んだデータを追加しようとすると文字化けしてしまいました。 これはコーディングの問題なのでしょうか?それともTomcat,Linux,Postgresqlに何らかの設定等が必要なのでしょうか? もし何か分かることがありましたら、ご教授お願い致します。

  • JBuilderの文字化け

    JBuilderでサーブレットでパラメータを渡す際に日本語が文字化けしてしまいます。 Enumeration enum=request.getParameterNames(); while(enum.hasMoreElements()){ String key=(String)enum.nextElement(); String value=new String(request.getParameter(key).getBytes("8859_1"),"JISAutoDetect"); out.println(value); } ソースは上記のようになっていますが日本語を表示できません。直す方法がわかる方どうかよろしくお願いします。

  • 文字コード

    少し、聞かせてください。 今、WEBアプリケーションを作っています。 HTMLからのリクエスト文字を受け取りServletで処理を考えています。 そこで、 String para = request.getParameter(para); String param = new String(para.getBytes("8859_1","JISAutoDetect"); で、8859_1,Shift_JIS,EUC-JP,ISO-2022-JP の Unicode 変換することが できるのは、分かりました。 では、この HTML からのリクエスト文字が、どのタイプの文字コード でかかれているかを検出できるのでしょうか? 自分では、バイト列をつかさどる方法が浮かぶのですが・・・。 HPでもかまいませんので、教えてください。

    • ベストアンサー
    • Java
  • サーブレットでデータベース操作。

    サーブレットを使ってMySQL内のテーブルにレコードを追加、変更、削除できるようなプログラムを作っています。流れとしましては『HTML画面→サーブレット起動→レコード追加、変更、削除』といった具合です。追加、変更ができてなぜか削除だけできません。SQL文を書き換えるだけのはずなのですが・・。どなたか教えて下さい。windows2000Professional, j2sdk1.4.0_01, Tomcat3.3.1,MySQL3.23.47 略 . . Connection con = null; Statement stmt = null; try { con = DriverManager.getConnection( "jdbc:mysql://localhost/db_ichat", "root", "pen3"); stmt = con.createStatement(); String seqno = request.getParameter("seqno"); String name = request.getParameter("name"); String namejp = new String(name.getBytes("8859_1"), "JISAutoDetect"); String psw = request.getParameter("psw"); String pswjp = new String(psw.getBytes("8859_1"), "JISAutoDetect"); String email = request.getParameter("email"); StringBuffer buf = new StringBuffer(); buf.append("delete from t_id "); buf.append("where seqno = "); buf.append(seqno); stmt.executeUpdate(buf.toString()); try { stmt.close(); } catch (Exception e) {} try { con.close(); } catch (Exception e) {} . . 略。

    • ベストアンサー
    • Java
  • テキストBOXから取得した文字コードの扱いについて

    JSPで記述したページのテキストBOXに入力されたSJIS形式の日本語文字データをサーブレットに送信してそれを取得して、文字のエンコードを行う際に気がついたのですが String text = request.getParameter("text"); byte[] bytes =text.getBytes("ISO8859_1"); text=new String(bytes, "SJIS"); のようにすればサーブレットで扱う時に正しく日本語表示できるのですが String text = request.getParameter("text"); byte[] bytes =text.getBytes("SJIS"); text=new String(bytes, "SJIS"); とすると正しく表示されません。 text.getBytes("SJIS");で取得格納されるbyteのデータに違いがあるようです。この場合取得されるbyteの配列は3つになっています。 ISO8859_1で取得するとbyte配列は2つになっています。このISO8859_1のbyte配列のデータと同じ中身のものを単にString(byteデータ,文字コード)のコンストラクタに入れるとSJISの文字コードで正しく変換出来ています。 要するにもともとSJISの文字データをgetByte("SJIS")で変換したものがなんで、もとに戻せないのかということです。また、違う文字コードのISO8859_1ではSJISに対応したbyteの配列になるのかということです。 ひどい乱文ですみませんが、よろしくお願いします。

  • 困ってます

    HTMLファイルのフォームのパラメータをサーブレットに引渡し、サーブレット内で、ファイルに出力(CSV形式)したいのですが、日本語が含まれていると文字化けしてしまいます。 文字化け防止の為に下記のようなコードを書いているのですが、うまくいきませんでした。(フォームの"name"というパラメータをサーブレットに渡します。) ----------------------------------------------------- (サーブレットのファイルより一部抜粋) //変数&定数の宣言 private static final String csvFile = "/home/csvFile.csv"; String name; FileWriter fw = new FileWriter(csvFile); PrintWriter pw = new PrintWriter(fw); //☆文字化け防止コード name = request.getParameter("name"); name = new String(name.getBytes("8859_1"),"SJIS"); //ファイルに出力 pw.print(name + ","); pw.close(); ----------------------------------------------------- 上記のようなコードの書き方だと駄目でしょうか? ちなみに"SJIS"を"euc-jp"、"JisAutoDetect"に変えてもうまくいきませんでした。 実行方法は (1).windowsでサーブレットをコンパイルし、 (2).(1)で出来たクラスファイルをLinuxに転送し、 (3).Linux上でTomcatの再起動をして実行しています。 もし何か分かることがありましたらご教授お願い致します。

  • バイト配列 unicode

    java初心者です。 以下のソースコードの 「new String(strVal.getBytes("ISO-8859-1"),"JISAutoDetect」 の部分の意味が分からなくこまっています。 <%@ page import="java.io.*" %> <%! public String strEncode(String strVal) throws UnsupportedEncodeingExcception{ if(strVal==null){ return null; }else{ return new String(strVal.getBytes("ISO-8859-1"),"JISAutoDetect")); } } %> そもそも 「バイト配列とは何か」 「なんで必要なのか?」 など、わかりやすく説明していただけると幸いです。

    • ベストアンサー
    • Java
  • 文字エンコードについて

    文字エンコードについてなのですが、 コンピュータでは文字1つ1つに何バイトかの値が割り振られていて、 それを翻訳的なことをして表示していることがわかりました。 で、エンコードによって同じ文字でも内部の値が異なっているということもわかりました。 そこで質問なのですが、私はPHPを勉強しています。 それで文字列のエンコードを変更する関数があるのですが、それで文字列をUnicodeに変更したとします。 でも保存するときにANSIを選択した・・・これってどういうことなんでしょう? しかも、IE等のブラウザ上からも「表示」→「エンコード」→「Shift_JIS」と選択できます。 プログラムの上からのエンコーディング テキストエディタからのエンコーディング これはどちらが優先されているのでしょう。 エンコーディングについて詳しいサイトなどありましたらお願いします。

  • 文字化け

    JSPで取ったデータをServletがGetParameterしています。 文字化け防止のコーディングは以下のようにしています。 String adr = request.getParameter("adr"); //住所 if(adr !=null && adr.equals("")==false){ //文字化け防止(日本語変換) adr = new String(adr.getBytes("8859_1"), "JISAutoDetect"); }else{ adr = ""; } が、全角のハイフン(-)だけが 文字化けしてしまいます。 ?になってしまいます。 なにかいいページかヒントがありましたら 教えてください。

  • エンコード指定でコンパイルエラー

    以下のソースで、UnsupportedEncodingExceptionと、 コンパイルエラーが発生します。 byte[] sjisBytes; // 適当なバイト列を入れる String string = new String(sjisBytes, "Shift_JIS"); // エラー String s; // 適当な文字列を入れる byte[] b = s.getBytes( "Shift_JIS" ); // エラー サポートされている他のエンコーディング(UTF-8など)を入れても、 同じエラーになります。 原因がわかる方いらっしゃいますでしょうか?

    • ベストアンサー
    • Java