我正在尝试生成一个随机的 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/