3.2. キーボードから文字列([a..zA..Z0..9])を入力し、これらの文字列を昇順・降順にソートする【JIS X 0213:2004(JIS2004)対応】。
JIS X 0213:2004(JIS2004) での問題
Windows Vista で採用された, JIS X 0213:2004 の文字集合すべてに対応させようと思うと, 上記のコードは正常に動作しません.
JIS X 0213:2004 に含まれる文字のうち, 303文字は Java の内部エンコーディングである UTF-16 において, 16bit + 16bit で表現されます(サロゲート・ペア).
Java の char は 16bit であるため, String#length() など, char を直接扱う API は正常に動作しなくなっています.
JavaSE5.0 で新たに追加された API を使用することで対応できるのですが, 少々面倒です...
/* * $Id: Example3_1_2.java 1641 2007-08-19 20:16:10Z nanasess $ */ package jp.examples; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Example3_1_2 * * <p> * 3.1 Accepting series of numbers, strings from keyboard and sorting them ascending order. * Supplementary Char Support. * </p> * * @author $Author: nanasess $ * @version $Revision: 1641 $ $Date: 2007-08-20 05:16:10 +0900 (月, 20 8 2007) $ * @see http://www.javainthebox.net/laboratory/J2SE1.5/MiscAPI/SupplementaryChar/SupplementaryChar.html */ public class Example3_1_2 { /** * 入力した文字列を昇順にソートし, 表示する(サロゲート・ペア対応). * * <pre> * JIS X 0213:2004 で定義された文字集合のうち, 303文字は, * UTF-16 において 16bit + 16bit で表現される(サロゲート・ペア). * サロゲート・ペアの文字列を正常に扱うためには, JavaSE 5.0 で追加された API を使う必要がある. * </pre> * * @throws Exception エラーが発生した場合 */ public static void main(String[] args) throws Exception { String s = "おえういあ"; // サロゲート・ペアの文字列 char[] ch = {(char)0xD801, (char)0xDC01, (char)0xD801, (char)0xDC00}; s = new String(ch) + s; // 文字列を List へ追加 List<String> list = new ArrayList<String>(); for (int i = 0; i < s.length(); i++) { /* * サロゲート・ペア以外の char と上位サロゲートは, コードポイントが返されるが, * 下位サロゲートは char が返されるため,下位サロゲートを除外し, * コードポイントのみ String を生成する. */ int codePoint = s.codePointAt(i); if (!Character.isLowSurrogate((char) codePoint)) { list.add(new String(new int[]{codePoint}, 0, 1)); } } // 昇順にソート Collections.sort(list); // List の文字列を1文字ずつ表示 int i = 1; StringBuilder sb = new StringBuilder(); for (String str : list) { sb.append(str); System.out.println(i + ": " + str); i++; } // サロゲート・ペアがソートされているか確認してみる String expected = "あいうえお"; char[] expectedChar = {(char)0xD801, (char)0xDC00, (char)0xD801, (char)0xDC01}; expected = expected + new String(expectedChar); // expected の順になっているはず. for (int j = 0; j < sb.length(); j++) { char c1 = expected.charAt(j); char c2 = sb.charAt(j); System.out.print(j + ":"); System.out.print(" expected: " + Integer.toHexString(c1)); System.out.print(" actual: " + Integer.toHexString(c2)); System.out.println(" is " + (c1 == c2)); } } }
サロゲート・ペアの文字列を正常に入出力できる環境は少ないので, 直接 char の配列を生成して試してみました.
文字列のコードポイントを取得してチェックするのですが, サロゲート・ペアの文字列の場合のみ, 下位サロゲートが出現するので, これを利用してみます.
String#codePointAt(int) を使うと, 下記の int が返されます.
上記のコードでは, 下位サロゲートの char をチェックして, コードポイントを使って String を生成しています.
これを実行すると, 下記のような出力になります.
1: あ 2: い 3: う 4: え 5: お 6: ? 7: ? 0: expected: 3042 actual: 3042 is true 1: expected: 3044 actual: 3044 is true 2: expected: 3046 actual: 3046 is true 3: expected: 3048 actual: 3048 is true 4: expected: 304a actual: 304a is true 5: expected: d801 actual: d801 is true 6: expected: dc00 actual: dc00 is true 7: expected: d801 actual: d801 is true 8: expected: dc01 actual: dc01 is true
サロゲート・ペアである文字列は文字化けしていますが, char の順番を見てみると, 正常にソートされているのが解ります.
しかし, 大変ですね...(苦笑)