自分がたまに使う割に、一部忘れたりするのでまとめてみることにしました。
文字コード系の調査とかトラブルがあった時に、知っておくと調査しやすいかも?といった内容です。
文字列(String)から、Unicodeのコードポイントを知りたい
Javaの内部的な文字の表現方法は、Unicode(正確にはUTF-16ですが…)です。なので、char1文字はサロゲートペアが登場しなければ、そのままUnicodeコードポイントを指します。
Stringをcharの配列に変換してから、文字列にしたりフォーマットしたりするとよいと思います。もちろん、いきなりcharから始めても構いませんが。
String str = "あいうえおabcde"; for (char c : str.toCharArray()) { System.out.println(c + " => 0x" + Integer.toHexString(c)); System.out.printf("%s => %#x%n", c, (int)c); System.out.printf("%s => %#X%n", c, (int)c); }
このコードの実行結果。
あ => 0x3042 あ => 0x3042 あ => 0X3042 い => 0x3044 い => 0x3044 い => 0X3044 う => 0x3046 う => 0x3046 う => 0X3046 え => 0x3048 え => 0x3048 え => 0X3048 お => 0x304a お => 0x304a お => 0X304A a => 0x61 a => 0x61 a => 0X61 b => 0x62 b => 0x62 b => 0X62 c => 0x63 c => 0x63 c => 0X63 d => 0x64 d => 0x64 d => 0X64 e => 0x65 e => 0x65 e => 0X65
System.out.printfの方は、%#xか%#Xかで結果が変わります。まあ、大文字になりますよ、と。
文字の特定のエンコーディングでのバイト値表現を知りたい
文字列または文字をいったんバイト配列に落として、そこからInteger.toHexStringで文字にして出力、という方法を自分はよく採ります。
今回は、文字エンコーディングとしては「UTF-8」を使っています。
try { String str = "あいうえおabcde"; for (char c : str.toCharArray()) { byte[] bytes = new String(new char[] {c}).getBytes("UTF-8"); System.out.print(c + "=> "); for (byte b : bytes) { System.out.print("0x" + Integer.toHexString(b & 0xFF) + " "); } System.out.println(); } } catch (java.io.UnsupportedEncodingException e) { }
Integer.toHexStringをかける時に、上位ビットを落としているところがポイントです。
実行結果。
あ=> 0xe3 0x81 0x82 い=> 0xe3 0x81 0x84 う=> 0xe3 0x81 0x86 え=> 0xe3 0x81 0x88 お=> 0xe3 0x81 0x8a a=> 0x61 b=> 0x62 c=> 0x63 d=> 0x64 e=> 0x65
String#getBytesに指定する文字エンコーディングの値を変えると(例えば、「Shift_JIS」、「Windows-31J」など)、この結果も変わります。
Unicodeのコードポイントから、文字に変換したい
与えられる文字列中に基数が含まれているかどうかで、ちょっと変わります。まあ、10進数で与えられるなら、どちらでもいいと思いますが…。
ここでは、16進数で与えられると仮定します。
文字列自体に、「0x」のような基数表現が書かれている場合は、Integer.decodeを使います。
String[] specHexUnicodeCodePoints = { "0x3042", // 「あ」の16進数でのUnicode表現 "0x3044", "0x3046", "0x3048", "0x304A", "0x61", // 「a」の16進数でのUnicode表現 "0x62", "0x63", "0x64", "0x65" }; for (String s : specHexUnicodeCodePoints) { char c = (char)Integer.decode(s).intValue(); System.out.printf("%s => %s%n", s, c); }
16進数であることがわかっているけれども、文字列中にそれが書かれていない場合は、Integer.parseIntの基数が指定できる方を使います。
String[] hexUnicodeCodePoints = { "3042", // 「あ」の16進数でのUnicode表現 "3044", "3046", "3048", "304A", "61", // 「a」の16進数でのUnicode表現 "62", "63", "64", "65" }; for (String s : hexUnicodeCodePoints) { char c = (char)Integer.parseInt(s, 16); System.out.printf("%s => %s%n", s, c); }
結果は、それぞれこうなります。
--- Integer.decode --- 0x3042 => あ 0x3044 => い 0x3046 => う 0x3048 => え 0x304A => お 0x61 => a 0x62 => b 0x63 => c 0x64 => d 0x65 => e --- Integer.parseInt --- 3042 => あ 3044 => い 3046 => う 3048 => え 304A => お 61 => a 62 => b 63 => c 64 => d 65 => e
まあ、もともとの文字列に「0x」が入っているかどうかの差しかないわけですが…。
特定のエンコーディングでのバイト配列表現から文字列を作りたい
特定のエンコーディングでのバイト値を表したものから、文字列を作成します。
今回は、「UTF-8」でのバイト配列表現を例としました。
Stringクラスのコンストラクタを使用します。まあ、これは普通ですかね…。
byte[][] bytes = { {(byte)0xE3, (byte)0x81, (byte)0x82}, // 「あ」のUTF-8でのバイト値表現 {(byte)0xE3, (byte)0x81, (byte)0x84}, {(byte)0xE3, (byte)0x81, (byte)0x86}, {(byte)0xE3, (byte)0x81, (byte)0x88}, {(byte)0xE3, (byte)0x81, (byte)0x8A}, {(byte)0x61}, // 「a」のUTF-8でのバイト値表現 {(byte)0x62}, {(byte)0x63}, {(byte)0x64}, {(byte)0x65} }; try { for (byte[] bs : bytes) { String s = new String(bs, "UTF-8"); String byteString = ""; for (byte b : bs) byteString += String.format("0x%s ", Integer.toHexString(b & 0xFF)); System.out.printf("%s=> %s%n", byteString, s); } } catch (java.io.UnsupportedEncodingException e) { }
実行結果。
0xe3 0x81 0x82 => あ 0xe3 0x81 0x84 => い 0xe3 0x81 0x86 => う 0xe3 0x81 0x88 => え 0xe3 0x81 0x8a => お 0x61 => a 0x62 => b 0x63 => c 0x64 => d 0x65 => e
文字が特定のエンコーディングの範囲内に収まっているかどうか知りたい
sun.ioパッケージ内のクラスを使用すると、文字(char)が特定のエンコーディングにマップ可能かどうか(要はバイト表現に変換できるか)を判定することができます。
もちろん、sun.ioパッケージなのでご利用には注意してくださいね。
ここでは、Shift_JISでよく問題になる機種依存文字をJISx0208、MS932(Windows-31J)、SJIS(Shift_JIS)の範囲で判定しています。
判定には、CharToByteXXXクラスのcanConvertメソッドを使用します。
String s = "あ㈱1①"; // 機種依存文字入り sun.io.CharToByteJIS0208 jis0208 = new sun.io.CharToByteJIS0208(); sun.io.CharToByteMS932 ms932 = new sun.io.CharToByteMS932(); sun.io.CharToByteSJIS sjis = new sun.io.CharToByteSJIS(); System.out.println("----- JISx0208判定 -----"); for (char c : s.toCharArray()) { System.out.printf("%s => %b%n", c, jis0208.canConvert(c)); } System.out.println("----- MS932(Windows-31J)判定 -----"); for (char c : s.toCharArray()) { System.out.printf("%s => %b%n", c, ms932.canConvert(c)); } System.out.println("----- SJIS(Shift_JIS)判定 -----"); for (char c : s.toCharArray()) { System.out.printf("%s => %b%n", c, sjis.canConvert(c)); }
※ 機種依存文字が表示できない環境向けに
String s = "あ㈱1①"; // 機種依存文字入り
は、
String s = "あ(株)1(1)"; // 機種依存文字入り
のような、機種依存文字の丸株と丸1です。
実行結果はこちら。
----- JISx0208判定 ----- あ => true ㈱ => false 1 => true ① => false ----- MS932(Windows-31J)判定 ----- あ => true ㈱ => true 1 => true ① => true ----- SJIS(Shift_JIS)判定 ----- あ => true ㈱ => false 1 => true ① => false
機種依存文字に対して結果がtrueになるのは、MS932だけですね。
これらのクラスは、以下のJARファイルに入っています。
$JAVA_HOME/jre/lib/charsets.jar
*UTF系はありませんよ
普段使っている文字エンコーディングに関するクラスがここにあるはずなので(名前は見つけにくいかもしれませんが)、知っていると役に立つことがあるかも?