java - 在 Java 中生成 3 字节(0x800 到 0xffff)UTF-8 编码

标签 java unicode utf-8

我正在尝试生成一个随机的 Unicode 字符字符串。我想指定每个字符占用的字节数(1-4 个字节,因为我想最终将它们转换为 UTF-8 字节数组)以及字符数。例如,如果我指定 10 作为字符串中的字符数,3 作为每个字符的字节数,我应该得到一个字符串 str,当我调用

str.getBytes(StandardCharsets.UTF_8).length 

我应该得到 30 个字节。

我的代码使用 1、2 和 4 个字节生成正确的字符字符串。但是,对于从 0x800 到 0xffff 的代码点,当我对返回的字符串调用 getBytes 时,每次都会得到不同的字节数。任何想法为什么会发生这种情况?

private String generateRandomString(int numberOfCharacters, int bytesPerCharacter) {

        int start;
        int end;

        switch (bytesPerCharacter) {
            case 1:
                start = 0;
                end = 0x7f;
                break;
            case 2:
                start = 0x80;
                end = 0x7ff;
                break;
            case 3:
                start = 0x800;
                end = 0xffff;
                break;
            case 4:
                start = 0x10000;
                end = 0x10ffff;
                break;
            default:
                throw new ArgumentException("Invalid value for the bytes per character");
        }
        StringBuilder builder = new StringBuilder(numberOfCharacters);
        int count = 0;
        int range = end - start;
        for (int i = 0; i < numberOfCharacters; i++) {
            builder.appendCodePoint((int) (Math.random() * range + start));
        }
        return builder.toString();
}

最佳答案

非常有趣的问题

TL;博士

答案是一些生成的代码点不是有效的 Unicode,Java 知道这一点,在编码为 UTF-8 时用 ? 替换它们,这会导致计数错误,因为只有一个字节是这些代码点的输出,而不是三个。

说明

public static void main(String[] args) {
    int start = 0x800;
    int end   = 0xffff;
    int range = end-start;
    StringBuilder b = new StringBuilder();
    for (int i=0; i<20; i++)
    {
        int a = (int)(Math.random() * range + start);
        b.appendCodePoint(a);
        System.out.printf("Code point %5d length=%d\n", a, b.length());
    }
    byte[] result = b.toString().getBytes(StandardCharsets.UTF_8);
    System.out.println(result.length);
    for (byte x : result)
    {
        // newline before any byte matching 1110 xxxx (start of 3-byte UTF-8)
        if ((x & 0xF0) == 0xE0) System.out.println();
        System.out.printf("%02x ", x);
    }
    System.out.println();
}

在某些运行中,这会产生少于 60 个字节的输出,例如:

Code point 35798 length=1
Code point 30523 length=2
Code point 43674 length=3
Code point  2743 length=4
Code point 64416 length=5
Code point  2438 length=6
Code point 15808 length=7
Code point 56254 length=8
Code point 20690 length=9
Code point 48789 length=10
Code point 52635 length=11
Code point  9128 length=12
Code point  8445 length=13
Code point 27765 length=14
Code point 63710 length=15
Code point 53350 length=16
Code point 41031 length=17
Code point 25939 length=18
Code point 56414 length=19
Code point 46327 length=20
56

e8 af 96 
e7 9c bb 
ea aa 9a 
e0 aa b7 
ef ae a0 
e0 a6 86 
e3 b7 80 3f 
e5 83 92 
eb ba 95 
ec b6 9b 
e2 8e a8 
e2 83 bd 
e6 b1 b5 
ef a3 9e 
ed 81 a6 
ea 81 87 
e6 95 93 3f 
eb 93 b7 

请注意,UTF-8 的十六进制转储中只有 18 行,并且 0x3f = ?。查找第 8 和第 19 个位置生成的“代码点”,发现这些是无效的 Unicode 代码点。

结论

您无法生成随机整数值并期望它们都是有效的 Unicode。对包含此类代码点的 String 进行编码会将无效代码点编码为 0x3f ('?')。

关于java - 在 Java 中生成 3 字节(0x800 到 0xffff)UTF-8 编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47783583/

相关文章:

java - 如何使用 Swing 计时器 ActionListener

java - 捆绑 JRE 8 和 JavaFX 应用程序

java - CMS + Java 应用服务器集成架构

java - 包装类 - 为什么整型文字对 Long 失败但对任何更小的东西都有效

c - 如何在 Linux 上的 C 中获取文件中的字符数(不是字节数)

PHP JSON_encode() 得到 "Malformed UTF-8 characters, possibly incorrectly encoded"(错误)

java - 如何在Android中正确处理字符串长度和编码?

utf-8 - perl6 Malformed UTF-8 导致程序崩溃

JavaScript utf8 编码或支持 Unicode 字母的正则表达式模式?

python - 如何用非单词字符的替代单词替换完全匹配的印地语单词(如विषयों)