Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
11
Help us understand the problem. What is going on with this article?
@msakamoto_sf

JavaでUnicodeのサロゲートペアを扱う練習

More than 3 years have passed since last update.

文字コードの復習をちょっとしてたら、Unicodeのサロゲートペアって今まで気にしたこと無かったことに気が付きました。
ということで、お仕事でも扱うJavaで、サロゲートペアの文字列を組み立てたり、文字数をカウントしたりする練習をしました。

Java 1.4まではサロゲートペアは考慮されてませんでしたが、1.5になり、サロゲートペアを考慮したAPIが追加されました。そこで、上記テストコードでは1.4系までのAPIと、1.5で追加されたAPIを呼び出してみて、挙動を比べてます。

まず char 型でサロゲートペアを表現してみます。上位サロゲート、下位サロゲートをそれぞれ別のchar型変数として分け、配列に組み込んでます。

char c1 = '\u3042'; // HIRAGANA LETTER A, cp=12354
char c2 = '\uD842'; // tuchi-yoshi (high), cp=134071
char c3 = '\uDFB7'; // tuchi-yoshi (low), cp=134071
char c4 = '\u30D5'; // katakana fu, cp=12501
char c5 = '\u309A'; // handakuten, cp=12442
char c6 = '\uD842'; // kuchi + shichi (high), cp=134047
char c7 = '\uDF9F'; // kuchi + shichi (low), cp=134047
String s = new String(new char[] { c1, c2, c3, c4, c5, c6, c7 });
assertEquals(s, "\u3042\uD842\uDFB7\u30D5\u309A\uD842\uDF9F");

続いてサロゲートペアを考慮しない String.length()String.charAt() を使って文字列をコピーしてみます。最後の assertEquals() を見ると、サロゲートペアが分割された状態の int[] から生成した文字列と一致しています。上位サロゲート・下位サロゲートをそれぞれ独立した一文字として扱い、コピーしている様子が確認できます。

int len = s.length();
assertEquals(len, 7); // ignores surrogate pair :P
int[] actualCps = new int[len];
for (int i = 0; i < len; i++) {
    char c = s.charAt(i);
    actualCps[i] = (int) c;
}
// Ignores surrogate pairs... :(
// BUT JavaScript unicode escape in browser accepts this format...:(
assertEquals(actualCps, new int[] { 0x3042, 0xD842, 0xDFB7, 0x30D5, 0x309A, 0xD842, 0xDF9F });

今度はサロゲートペアを考慮する String.codePointCount()String.codePointAt() を使ってみます。最後の assertEquals() を見ると、サロゲートペア対象文字をUnicodeコードポイントの16進数で表現した文字列と同じになります。サロゲートペアを1文字としてカウントし、扱えている様子が確認できます。

int countOfCp = s.codePointCount(0, len);
assertEquals(countOfCp, 5); // GOOD.

actualCps = new int[countOfCp];
for (int i = 0, j = 0, cp; i < len; i += Character.charCount(cp)) {
    cp = s.codePointAt(i);
    actualCps[j++] = cp;
}
// GOOD.
assertEquals(actualCps, new int[] { 0x3042, 0x20BB7, 0x30D5, 0x309A, 0x20B9F });

参考:

11
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
msakamoto_sf
PHP/Java/Groovy Programmer

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
11
Help us understand the problem. What is going on with this article?