如何强制 Java 对 Unicode 字符(例如 'ł')每个字符仅使用 2 个字节?
How to force Java to use only 2 bytes per character for Unicode characters (eg. 'ł')?
我正在努力将哈希算法从 C# 转换为 Java,它需要使用字符串的字节数组。问题是在处理像 'ł' 和 'ą' 这样的字符时,Java 将这些字母转换为 2 个字符,从而给我 4 个字节而不是我期望的 2 个字节。
我尝试使用 string.codePointAt()
而不是 string.charAt()
,但它继续将这些字母处理为 2 个字符而不是 1 个。我认为 Java 使用与 C# 相同的 16 位 Unicode & VB 但是当 C# 和 VB 能够将这些字母转换为 2 个字节时,为什么这个字母需要 4 个字节。
C# 和 VB 将 'ł' 的字节读取为:[66, 1](下面的代码)
bytes = Encoding.Unicode.GetBytes("ł");
Console.WriteLine(string.Join(",", bytes));
Java 将 'ł' 的字节读取为:[-59, 0, 26, 32](下面的代码)
String str = "ł";
byte[] B = str.getBytes(Charset.forName("UTF-16LE"));
System.out.println(Arrays.toString(B));
我什至也尝试过使用 StandardCharsets,但仍然是同样的问题。
有没有办法让 Java 将这些字母作为单个 UTF-16 字符处理,而不是将其分成 2 个字符?
PS:我也不能重构算法,因为它已经在使用了,而且它也必须在我们的新 Java 中完成。
PPS:我尝试规范化字符串,但仍然存在差异,当 C# 为字符 [=15 输出 [230,0] 时,字符“æ”用 [-26,0] 读取=]
我发现了问题,正如 Ralf Kleberhoff 所猜测的那样,我没有使用 Java 编译器期望的正确文件编码。我的文件使用的是 UTF-16LE 编码,所以我在编译文件时只传递了 -encoding "UTF-16"。
javac -encoding "UTF-16" HashBrowns.java
此外,正如 Samuel Hunter 所建议的,我将这些值转换为正数以确保我得到的值与我在 C# 和 VB6 中得到的值完全相同。
private int[] convertSignedBytesToUnsignedint(byte[] b)
{
int[] intArr = new int[b.length];
for (int i = 0; i < b.length; i++) {
intArr[i] = b[i] & 0xff;
}
return intArr;
}
我不确定哪个代码更优化,但我只是想 post 在这里,这样我就可以分享适合我的情况的代码。
虽然 Java 的内部字符编码是 UTF-16BE,但 String#codePointAt(int)
和 String#getBytes()
(没有提供参数)都使用默认字符编码,这取决于 Java 实现及其所在的平台。您使用 String.getBytes(Charset.forName("UTF-16LE"))
的想法是正确的,但我建议您改用 String.getBytes(StandardCharsets.UTF_16LE)
。
C# 返回 [230,0]
而 Java returns [-26, 0]
的第二个问题:从技术上讲,它们在位方面是相同的。但是,C# 的字节数组保存无符号字节,而 Java 的数组保存有符号字节。即使 Java 和 C# 都提供相同的字节模式,如果您 真的 想要表达一个正值,您可以将它们存储在 short
数组中:
String str = "æ";
byte[] byteArray = "æ".getBytes(StandardCharsets.UTF_16LE);
short[] newByteArray = new short[byteArray.length];
for (int i = 0; i < byteArray.length; i++) {
byte c = byteArray[i];
newByteArray[i] = (c >= 0) ? c : (short)(c + 256);
}
System.out.println(Arrays.toString(byteArray));
// => [-26, 0]
System.out.println(Arrays.toString(newByteArray));
// => [230, 0]
FWIW,用 ł
替换 æ
给我 [66, 1]
字节数组和短数组。
尽管代码将数组“转换”为无符号“字节”,如果可以的话我建议不要这样做,因为字节数组给出与 C# 相同的模式,并承诺相同的数字大小。
我正在努力将哈希算法从 C# 转换为 Java,它需要使用字符串的字节数组。问题是在处理像 'ł' 和 'ą' 这样的字符时,Java 将这些字母转换为 2 个字符,从而给我 4 个字节而不是我期望的 2 个字节。
我尝试使用 string.codePointAt()
而不是 string.charAt()
,但它继续将这些字母处理为 2 个字符而不是 1 个。我认为 Java 使用与 C# 相同的 16 位 Unicode & VB 但是当 C# 和 VB 能够将这些字母转换为 2 个字节时,为什么这个字母需要 4 个字节。
C# 和 VB 将 'ł' 的字节读取为:[66, 1](下面的代码)
bytes = Encoding.Unicode.GetBytes("ł");
Console.WriteLine(string.Join(",", bytes));
Java 将 'ł' 的字节读取为:[-59, 0, 26, 32](下面的代码)
String str = "ł";
byte[] B = str.getBytes(Charset.forName("UTF-16LE"));
System.out.println(Arrays.toString(B));
我什至也尝试过使用 StandardCharsets,但仍然是同样的问题。
有没有办法让 Java 将这些字母作为单个 UTF-16 字符处理,而不是将其分成 2 个字符?
PS:我也不能重构算法,因为它已经在使用了,而且它也必须在我们的新 Java 中完成。
PPS:我尝试规范化字符串,但仍然存在差异,当 C# 为字符 [=15 输出 [230,0] 时,字符“æ”用 [-26,0] 读取=]
我发现了问题,正如 Ralf Kleberhoff 所猜测的那样,我没有使用 Java 编译器期望的正确文件编码。我的文件使用的是 UTF-16LE 编码,所以我在编译文件时只传递了 -encoding "UTF-16"。
javac -encoding "UTF-16" HashBrowns.java
此外,正如 Samuel Hunter 所建议的,我将这些值转换为正数以确保我得到的值与我在 C# 和 VB6 中得到的值完全相同。
private int[] convertSignedBytesToUnsignedint(byte[] b)
{
int[] intArr = new int[b.length];
for (int i = 0; i < b.length; i++) {
intArr[i] = b[i] & 0xff;
}
return intArr;
}
我不确定哪个代码更优化,但我只是想 post 在这里,这样我就可以分享适合我的情况的代码。
虽然 Java 的内部字符编码是 UTF-16BE,但 String#codePointAt(int)
和 String#getBytes()
(没有提供参数)都使用默认字符编码,这取决于 Java 实现及其所在的平台。您使用 String.getBytes(Charset.forName("UTF-16LE"))
的想法是正确的,但我建议您改用 String.getBytes(StandardCharsets.UTF_16LE)
。
C# 返回 [230,0]
而 Java returns [-26, 0]
的第二个问题:从技术上讲,它们在位方面是相同的。但是,C# 的字节数组保存无符号字节,而 Java 的数组保存有符号字节。即使 Java 和 C# 都提供相同的字节模式,如果您 真的 想要表达一个正值,您可以将它们存储在 short
数组中:
String str = "æ";
byte[] byteArray = "æ".getBytes(StandardCharsets.UTF_16LE);
short[] newByteArray = new short[byteArray.length];
for (int i = 0; i < byteArray.length; i++) {
byte c = byteArray[i];
newByteArray[i] = (c >= 0) ? c : (short)(c + 256);
}
System.out.println(Arrays.toString(byteArray));
// => [-26, 0]
System.out.println(Arrays.toString(newByteArray));
// => [230, 0]
FWIW,用 ł
替换 æ
给我 [66, 1]
字节数组和短数组。
尽管代码将数组“转换”为无符号“字节”,如果可以的话我建议不要这样做,因为字节数组给出与 C# 相同的模式,并承诺相同的数字大小。