HOME PIC VC++ JAVA |
文字あるいは文字列を処理する場合、サロゲートペアを考慮したプログラミングをする必要があります。それを怠ると、おかしな結果が得られることがあります。サロゲートペアについては、ユニコード(文字コード規格)を参照ください。
目次
以下のプログラムを実行します。
public class test {
public static void main(String args[]) {
String a;
int i, j;
a = "あ";
i = a.length();
a = "𠮷";
j = a.length();
System.out.println(i+ "\n" + j);
}
}
実行結果は
1
2
となります。
public class test {
public static void main(String args[]) {
String a;
int i, j;
a = "あ𠮷";
i = a.length();
j = a.codePointCount(0, i);
System.out.println(j);
}
}
実行結果は
2
となります。
このプログラムでは、StringクラスのcodePointCountメソッド
public int codePointCount(int beginIndex, int endIndex)
を使用しています。
ここで、Index(インデックス)について説明します。文字列を最初から2バイトごとに分割して、0から始まり、1づつ増える通し番号を付けます。この通し番号がインデックスです。下の図を見てください。
この図から文字数を得るには、コードポイントが幾つあるか数えれば(Countすれば)良いことが分かります。codePointCountは、そのためのメソッドです。このメソッドの引数で、どのインデックス(beginIndex)からどのインデックス(endIndex)の間の文字数(コードポイントの数)を数えるかを指定します。上のプログラムでは、文字列”あ𠮷”の文字数を求めるのが目的ですから、上の図から、インデックス0からインデックス3まで、コードポイントを数えれば良いことになります。そこで、beginIndex=0、そしてendIndex=3とします。ところで、endIndexの3は、lengthメッソドで得ることができますので、上記プログラムのように
i = a.length();
j = a.codePointCount(0, i);
とすれば、jに”aの文字数”が入ります。
public class test {
public static void main(String args[]) {
String a;
int i,j,k,l;
a = "あ𠮷";
i = a.offsetByCodePoints(0, 1);
j = a.offsetByCodePoints(0, 2);
k = a.offsetByCodePoints(3, -1);
l = a.offsetByCodePoints(2, -1);
System.out.println(i+" "+j+" "+k+" "+l);
}
}
実行結果は
1 3 1 1
となります。
以下の図において、矢印の始まりが起点となるインデックスで、矢印の先は、起点となるインデックスからオフセット分だけ離れたインデックスです。例えば、offsetByCodePoints(0, 1)を説明します。起点をインデックス0として、そこから、1コードポイント離れたインデックスを求めています。答えは、図の@の矢印の先のインデックス1です。offsetByCodePoints(0, 2)は、起点をインデックス0として、そこから、2コードポイント離れたインデックスを求めています。答えは、図のAの矢印の先のインデックス3です。同様に、offsetByCodePoints(3, -1)は、起点がインデックス3で、そこから、−1コードポイント離れたインデックスを求めています。答えは、図のBの矢印の先のインデックス1です。
さて、offsetByCodePoints(2, -1)が問題です。サロゲートペアの途中であるインデックス2を起点としています。そこから、−1コードポイント離れたインデックスを求めるということになります。このように、起点をサロゲートペアの途中に設定すると、−1コードポイントとしても、Cのように、サロゲートペアの半分だけ(2バイト)を1コードポイントとしてしまいます。結局、求まるインデックスは1となります。この様な使い方はしないと思いますが、念のため説明しました。
public class test {
public static void main(String[] args) {
String a;
int i,j,k;
a = "あ𠮷";
i=a.charAt(0);
j=a.charAt(1);
k=a.charAt(2);
System.out.println(Integer.toHexString(i));
System.out.println(Integer.toHexString(j));
System.out.println(Integer.toHexString(k));
}
}
実行結果は
3042
d842
dfb7
となります。
これまでの図を見れば明らかだと思います。注意としては、charAt(3);はエラーとなることです。インデックス3から始まるバイトはありません。
public class test{
public static void main(String[] args) {
String a;
int i,j,k;
a = "あ𠮷";
i=a.codePointAt(0);
j=a.codePointAt(1);
k=a.codePointAt(2);
System.out.println(Integer.toHexString(i));
System.out.println(Integer.toHexString(j));
System.out.println(Integer.toHexString(k));
}
}
実行結果は
3042
20bb7
dfb7
となります。
これまでの図を見れば明らかだと思います。注意としては、codePointAt(2);のように、サロゲートペアの途中のインデックスを引数とすると、サロゲートペアの後半の2バイト分しか返ってこないことになります。ただ、このような使い方はしないと思いますが、念のため。
public class test {
public static void main(String[] args) {
String a;
int i,j,k;
a = "あ𠮷";
i=a.codePointBefore(1);
j=a.codePointBefore(2);
k=a.codePointBefore(3);
System.out.println(Integer.toHexString(i));
System.out.println(Integer.toHexString(j));
System.out.println(Integer.toHexString(k));
}
}
実行結果は
3042
d842
20bb7
となります。
これまでの図を見れば明らかだと思います。注意としては、a.codePointBefore(2);のように、サロゲートペアの途中のインデックスを引数とすると、サロゲートペアの前半の2バイト分しか返ってこないことになります。ただ、このような使い方はしないと思いますが、念のため。
public class test {
public static void main(String[] args) {
String a, b, c;
a = "あ𠮷";
b = a.substring(1, 3);
c = a.substring(2, 3);
System.out.println(b);
System.out.println(c);
}
}
実行結果は
𠮷
?
となります。
これまでの図を見れば明らかだと思います。注意としては、substring(2, 3);のように、サロゲートペアの途中のインデックスを引数とすると、?が返ってきます。ただ、このような使い方はしないと思いますが、念のため。
上記の例では、インデックスを指定していますが、これは面倒です。実用的には、以下の例を見てください。
public class test {
public static void main(String[] args) {
int i, j;
String a, b;
a = "あ𠮷いう";
// 文字列の始めを起点として、1コードポイント(1文字分)
// だけオフセットさせたインデックスを求める
i = a.offsetByCodePoints(0, 1);
// さらに、2コードポイント(2文字分)オフセットさせた
// インデックスを求める
j = a.offsetByCodePoints(0, 1 + 2);
b = a.substring(i, j);// "𠮷い"の2文字が取り出される
System.out.println(b);
}
}
実行結果は
𠮷い
となります。
StringBuilderクラスはStringクラスよりも、文字を操作するには便利です。以上に書いてきたStringクラスのメソッドはすべて、StringBuilderクラスでも使用できます。
StringBuilderクラスのメソッド
public class test {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("あ𠮷");
sb.append("い");
System.out.println(sb);
}
}
実行結果は
あ𠮷い
となります。
public class test {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("あ𠮷");
sb.insert(2,"い");
System.out.println(sb);
}
}
実行結果は
あ?い?
となります。
このように、サロゲートペアの途中のインデックスを引数とすると、?が返ってきます。このような使い方はしないと思いますが、注意が必要です。insertの第1引数を0,1,3(サロゲートペアの途中でないインデックス)にすると意図した結果が得られます。
public class test {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("あ𠮷");
sb.appendCodePoint(0x20bb7);
System.out.println(sb);
}
}
実行結果は
あ𠮷𠮷
となります。