プロが教えるわが家の防犯対策術!

前回の質問(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として構築されたもの、
になると思うのですが、
そういう認識で正しいのでしょうか。
また、それで正しいのなら、それと同じことを自分で行うには
どうすればよいのでしょうか。


とても気になります。
もしわかる方がいらっしゃったら、是非回答お願いします。

A 回答 (4件)

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による対応表が見つかりましたので、
今回の件については、これは無視して下さい。
    • good
    • 0
この回答へのお礼

おはようございます。URL見ました。

>Harryさんの元のご質問は、こういう状態になっているかどうかを
>確認したかったのではないでしょうか。

「確認」という言葉を使えるほど、
こういう状態であることを予測してませんでした。
String型が8859-1も内部的な文字表現手段として利用してるのではないか、
という方向にばかり考えが向いていたので・・・
そうではなく、そもそも8859-1がUnicodeのサブセットのようなコード構成
になっていたのですね。

あるバイト列を8859-1エンコードで解釈した結果、「?」と表示されてしまう
未定義(と思っていた)部分は、それがUnicodeのコードに変換されるときに、
すべて、同一のコードにマッピングされて、復元不可能になってしまうのでは
ないか、
という疑問があったのですが、これも、これらが未定義ではなく制御文字、
しかもやはり1対1対応、ということで氷解しました。

とてもすっきりしました。
本当にどうもありがとうございます。


ところで、URLの完全一対一の対応表を見て、
ちょっとこれらの文字コードの成り立ちに興味が湧いてきました。
この辺もうすこし自分で調べてみようと思います。

お礼日時:2002/03/26 10:31

ご免なさい。

参考URLを忘れていました。

参考URL:http://www.unicode.org/Public/MAPPINGS/ISO8859/8 …
    • good
    • 0

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には定義されていないコードが含まれる
ことになっていますので、上記の変換ができたのは、たまたま現在の実装がそうなって
いただけのことかもしれません。いずれにせよ、一つのエンコーディングで処理
されたバイト列をそのまま他のエンコーディングで解釈するというのは、かなり危険な
ものと言え、できれば避けるべきだと思います。

この回答への補足

一部訂正します。

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

補足日時:2002/03/25 13:34
    • good
    • 0
この回答へのお礼

回答ありがとうございます。

さっきの質問補足で書いたコードでは省きましたが、
str1 の内容も、もちろん出力して確認はしていました。
大部分が、「?」だったので、それが、ranxさんのおっしゃっている
定義されていないコードなのだと思います。

回答を読ませてもらいましたが疑問はまだ消えません。
もしString型が内部でUnicodeを間にかませてしまうと、
『 ???¢?????¨ 』という8859-1エンコードの文字列(str1)が
Unicodeに変換された時点でバイトデータは破壊されてしまうと
思うんです。8859-1でないエンコードを使った場合、先のコードと
同様の変換ができないというのも腑に落ちません。

コンストラクタ String(byte[] bytes, String encode) が
バイト列を指定のエンコードでエンコーディングされたものとして理解する
だけなら、指定エンコードの種類を問わず、先のコードは必ず元の
文字列を復元できるはずです。内部でUnicodeに変換されたらもちろん
バイトデータは破壊されますが、8859_1 の場合、実際の動作を見る限り
それがあてはまりません。


先のコードで正しい変換ができたのは、
決して"たまたま"ではないと思います。
なぜなら、質問中にふれたサーブレットのコードも、Propertiesクラス利用
の際も同様の処理を行っているからです。
でも危険なものであるという点は私も同じ意見です。
実際、質問No,235091 のような文字化けの問題などがあるわけですから。
(このNo,235091 の質問の意味が最近やっとわかりました。)

お礼日時:2002/03/25 13:23

> では、request.getParameter("test") の結果返ってくる String は


> ブラウザのエンコードでパラメータを表現するバイト列を用い、
> 値はそのままで、エンコード名だけを8859-1として構築されたもの、
> になると思うのですが、
> そういう認識で正しいのでしょうか。

質問の意味がよく分からないのですが、String型はつねにUnicodeで表現
されますから、バイト列そのままということはないですよね。もっとも、
0x0020~0x007eの分については、ASCIIそのままですから、そういう意味で
あれば値はそのままとも言えます。もちろん、漢字のコードなどが入り
込んでいればそういうわけにはいきませんし、円通貨記号のように、日本で
通常ASCIIとして扱っているものでも8859-1とは異なるものもありますね。

というわけで、ご質問の文章、何だかおかしいという感じですが、よく
分からないので、明確にして頂けると回答しやすいと思います。
    • good
    • 0
この回答へのお礼

レスありがとうございます。

私は、自分の上記の認識であってるという自信がありません。
もし正しくないのであれば、つまるところ、質問は、
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も
利用できる、ということはないですか。

お礼日時:2002/03/25 11:47

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!