プログラマメモ2 - programmer no memo2

[java]サロゲートペアからコードポイントをもとめる 2010/11/13

Javaです。文字です。ユニコードです。えーとサロゲートペアとコードポイントです。
コード書いて手を動かさないといまいちイメージがわかないんですよね。ということでサロゲートペアのデータからコードポイントを求めるのと、その逆を行ってみます。
この記事はmacosx上のJava1.6を使用したものをベースとしてます。

javaの用意されているapiを使うのと、自分実装です。

参考



サロゲートペアとは、BMPの中の文字の割り当てのない符号位置2つを用いて、BMP外の面の符号位置を指すものです。プログラマのための文字コード技術入門 (WEB+DB PRESS plus) (WEB+DB PRESS plusシリーズ)


この本一人一冊で!!
プログラマのための文字コード技術入門 (WEB+DB PRESS plus) (WEB+DB PRESS plusシリーズ)
矢野 啓介
477414164X

実装
/**
* コードポイントからサロゲートペアバイト配列にしてます。
* チェック処理してない
* @param codepoint
* @return
*/
static byte[] surrogate(int codepoint) {
int c = codepoint;// 21bit
c -= 0x10000;
int c1 = (c >>> (10));// 10bit
int c2 = 0x3FF & c;// 10bit
byte[] high = bytes(c1 + 0xD800);
byte[] low = bytes(c2 + 0xDC00);
byte[] bs = mergeLowerBytes(high, low);
return bs;
}

/**
* サロゲート文字からコードポイントを求める。
* チェック処理してない
* @param surrogate
* @return
*/
static int codepoint(int surrogate) {
int c = surrogate;// 32bit
// high
int high = 0xffff0000 & c;
int low = 0x0000ffff & c;
high >>>= 16;
high -= 0xD800;
low -= 0xDC00;
high <<= 10;
int merge = (high + low) + 0x10000;
return merge;
}




長いけどソース

実行結果は


package a;

import java.nio.ByteBuffer;

public class TestUnicode_surrogatepair {

/**
* バイトの配列を16進数表現で標準出力するよ
* @param bs
*/
static void printHex(String title, byte[] bs) {
System.out.print(title+"[");
for (byte b : bs) {
System.out.printf("0x%H ", (int) b & 0xff);
}
System.out.println("]");
}

/**
* バイトの配列を16進数表現で標準出力するよ
* @param bs
*/
static void printHex(String title, char[] cs) {
System.out.print(title+"[");
for (char c : cs) {
System.out.printf("0x%H ", (int) c);
}
System.out.println("]");
}

static void printCodePoint(int codePoint){
System.out.printf("Character:[%c]%n", codePoint);
}

/**
* intをバイト配列にします。
* @param a
* @return
*/
static byte[] bytes(int a) {
byte[] bs = new byte[4];
bs[3] = (byte) (0x000000ff & (a));
bs[2] = (byte) (0x000000ff & (a >> 8));
bs[1] = (byte) (0x000000ff & (a >> 16));
bs[0] = (byte) (0x000000ff & (a >> 24));
return bs;
}

/**
* バイトの配列をintにします。
* @param bs
* @return
*/
static int INT(byte[] bs) {
return ByteBuffer.wrap(bs).asIntBuffer().get();
}


/**
*
* @param bs1
* @param bs2
* @return
*/
static byte[] mergeLowerBytes(byte[] bs1, byte[] bs2) {
byte[] bs = new byte[4];

System.arraycopy(bs1, 2, bs, 0, 2);
System.arraycopy(bs2, 2, bs, 2, 2);
return bs;
}

/**
* コードポイントからサロゲートペアバイト配列にしてます。
* チェック処理してない
* @param codepoint
* @return
*/
static byte[] surrogate(int codepoint) {
int c = codepoint;// 21bit

c -= 0x10000;
int c1 = (c >>> (10));// 10bit
int c2 = 0x3FF & c;// 10bit

byte[] high = bytes(c1 + 0xD800);
byte[] low = bytes(c2 + 0xDC00);

byte[] bs = mergeLowerBytes(high, low);
return bs;
}

/**
* サロゲート文字からコードポイントを求める。
* チェック処理してない
* @param surrogate
* @return
*/
static int codepoint(int surrogate) {
int c = surrogate;// 32bit
// high
int high = 0xffff0000 & c;
int low = 0x0000ffff & c;

high >>>= 16;
high -= 0xD800;
low -= 0xDC00;

high <<= 10;

int merge = (high + low) + 0x10000;
return merge;
}

/**
* テストメイン
*
*
* @param args
*/
public static void main(String[] args) {
a();
}

static void a() {
// (1)Javaですでに用意されてるAPIを使ってみる
// (2)自分実装で変換してみる

// サロゲートペア文字
int c = 0xD867DE15;
char[] CS ={0xD867, 0xDE15};
{
/*
* (1)
*/
// コードポイント
int codepoint = Character.toCodePoint(CS[0], CS[1]);
System.out.printf("codepoint:[u+%h]%n", codepoint);
printCodePoint(codepoint);
// コードポイントからutf-16配列を得る
char[] cs = Character.toChars(codepoint);
printHex("UTF-16:", cs);
}
System.out.println("====================");
{
/*
* (2)
*/
int codepoint = codepoint(c);
System.out.printf("codepoint:[u+%h]%n", codepoint);
printCodePoint(codepoint);
byte[] bs = surrogate(codepoint);
printHex("UTF-16:", bs);

}
}

}

: