字节索引的字符索引

时间:2014-12-25 23:27:50

标签: java unicode character-encoding

我知道如何使用特定编码将字符串转换为字节数组,但如何将字符索引转换为字节索引(在Java中)?

例如,在UTF-32中,字符索引i是字节索引4 * i,因为每个UTF-32字符都是4字节宽。但是在UTF-8中,大多数英文字符是1字节宽,大多数其他脚本中的字符是2或3字节宽,少数是4字节宽。对于给定的字符串和编码,我如何获得每个字符的起始字节索引数组?

这是我的意思的一个例子。 UTF-8中的字符串"Hello مرحبا こんにちは"具有以下索引:[0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 17, 20, 23, 26, 29]因为拉丁字符各为1个字节,阿拉伯字符各为2个字节,日语字符各为3个字节。 (在累积和之前,数组为[1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 3, 3, 3, 3, 3]。)

Java中是否有库函数来计算这些索引位置?它需要高效,所以我不应该将每个字符转换为单独的字节数组,只是为了查询它的长度。根据Unicode的一些知识,有没有一种简单的方法可以自己计算它?通过识别指示下一个字符宽度的特殊字节,应该可以一次完成。

1 个答案:

答案 0 :(得分:5)

我认为这可以做你想要的:

static int[] utf8ByteIndexes(String s) {
    int[] byteIndexes = new int[s.length()];
    int sum = 0;
    for (int i = 0; i < s.length(); i++) {
        byteIndexes[i] = sum;
        int c = s.codePointAt(i);
        if (Character.charCount(c) == 2) {
            i++;
            byteIndexes[i] = sum;
        }
        if (c <=     0x7F) sum += 1; else
        if (c <=    0x7FF) sum += 2; else
        if (c <=   0xFFFF) sum += 3; else
        if (c <= 0x1FFFFF) sum += 4; else
        throw new Error();
    }
    return byteIndexes;
}

给定一个Java字符串,它返回一个与String中每个char对应的UTF-8字节索引的数组。

System.out.println(Arrays.toString(utf8ByteIndexes("Hello مرحبا こんにちは")));

输出:

[0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 17, 20, 23, 26, 29]

U + FFFF之上的异常Unicode字符,那些不适合Java的16位字符类型的字符,有点令人讨厌。例如,圣诞树表情符号U + 1F384()使用两个Java&#34; chars&#34;进行编码。对于那些,上面的函数为两个字符返回相同的字节索引:

System.out.println(Arrays.toString(utf8ByteIndexes("xy")));

输出:

[0, 1, 1, 5]

但总体累积字节数是正确的(如果以UTF-8编码,表情符号需要4个字节)。