【发布时间】:2019-10-07 10:05:12
【问题描述】:
根据Java documentation for String.length:
公共整数长度()
返回此字符串的长度。
长度等于字符串中Unicode代码单元的个数。
指定者:
接口 CharSequence 中的长度
返回:
序列的长度 此对象表示的字符数。
但是我不明白为什么下面的程序 HelloUnicode.java 在不同的平台上会产生不同的结果。按照我的理解,Unicode码单元的个数应该是一样的,因为Java supposedly always represents strings in UTF-16:
public class HelloWorld {
public static void main(String[] args) {
String myString = "I have a ???? in my string";
System.out.println("String: " + myString);
System.out.println("Bytes: " + bytesToHex(myString.getBytes()));
System.out.println("String Length: " + myString.length());
System.out.println("Byte Length: " + myString.getBytes().length);
System.out.println("Substring 9 - 13: " + myString.substring(9, 13));
System.out.println("Substring Bytes: " + bytesToHex(myString.substring(9, 13).getBytes()));
}
// Code from https://stackoverflow.com/a/9855338/4019986
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
}
这个程序在我的 Windows 机器上的输出是:
String: I have a ???? in my string
Bytes: 492068617665206120F09F998220696E206D7920737472696E67
String Length: 26
Byte Length: 26
Substring 9 - 13: ????
Substring Bytes: F09F9982
我的 CentOS 7 机器上的输出是:
String: I have a ???? in my string
Bytes: 492068617665206120F09F998220696E206D7920737472696E67
String Length: 24
Byte Length: 26
Substring 9 - 13: ???? i
Substring Bytes: F09F99822069
我都使用 Java 1.8 运行。相同的字节长度,不同的字符串长度。为什么?
更新
通过替换“????”在带有“\uD83D\uDE42”的字符串中,我得到以下结果:
窗户:
String: I have a ? in my string
Bytes: 4920686176652061203F20696E206D7920737472696E67
String Length: 24
Byte Length: 23
Substring 9 - 13: ? i
Substring Bytes: 3F2069
CentOS:
String: I have a ???? in my string
Bytes: 492068617665206120F09F998220696E206D7920737472696E67
String Length: 24
Byte Length: 26
Substring 9 - 13: ???? i
Substring Bytes: F09F99822069
为什么 "\uD83D\uDE42" 在 Windows 机器上最终被编码为 0x3F 是我无法理解的......
Java 版本:
窗户:
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)
CentOS:
openjdk version "1.8.0_201"
OpenJDK Runtime Environment (build 1.8.0_201-b09)
OpenJDK 64-Bit Server VM (build 25.201-b09, mixed mode)
更新 2
使用.getBytes("utf-8"),带有“????”嵌入在字符串文字中,这里是输出。
窗户:
String: I have a ???? in my string
Bytes: 492068617665206120C3B0C5B8E284A2E2809A20696E206D7920737472696E67
String Length: 26
Byte Length: 32
Substring 9 - 13: ????
Substring Bytes: C3B0C5B8E284A2E2809A
CentOS:
String: I have a ???? in my string
Bytes: 492068617665206120F09F998220696E206D7920737472696E67
String Length: 24
Byte Length: 26
Substring 9 - 13: ???? i
Substring Bytes: F09F99822069
所以是的,它似乎是系统编码的差异。但这意味着字符串文字在不同平台上的编码方式不同?这听起来在某些情况下可能会出现问题。
另外...在 Windows 中代表笑脸的字节序列 C3B0C5B8E284A2E2809A 来自哪里?这对我来说没有意义。
为了完整起见,使用.getBytes("utf-16"),加上“????”嵌入在字符串文字中,这里是输出。
窗户:
String: I have a ???? in my string
Bytes: FEFF00490020006800610076006500200061002000F001782122201A00200069006E0020006D007900200073007400720069006E0067
String Length: 26
Byte Length: 54
Substring 9 - 13: ????
Substring Bytes: FEFF00F001782122201A
CentOS:
String: I have a ???? in my string
Bytes: FEFF004900200068006100760065002000610020D83DDE4200200069006E0020006D007900200073007400720069006E0067
String Length: 24
Byte Length: 50
Substring 9 - 13: ???? i
Substring Bytes: FEFFD83DDE4200200069
【问题讨论】:
-
请显示字节数组的确切内容(最好是十六进制)并使用
\uD83D\uDE42序列而不是????在代码中 -
@MichalKordas 感谢您的建议。我在对问题的更新中提到了他们。
-
你能用
getBytes("UTF-8")和getBytes("UTF-16")吗?还要确保 STDOUT 也使用 UTF-8(或者更好的是,改为使用指定的编码写入文件)。 -
"为什么 "\uD83D\uDE42" 在 Windows 机器上最终被编码为 0x3F 是我无法理解的......" 0x3f 是问号。 Java 在要求输出无效字符时将其放入。所以看起来它只是用 ? 替换了你的笑脸。因为您没有在
getBytes中指定Unicode,所以它默认为平台编码。 -
@Thilo 但它似乎已将 "\uD83D\uDE42" 替换为 "?"在解释字符串文字时,而不是通过
getBytes转换为字节时。似乎 Windows Java 只是不知道如何处理“\uD83D\uDE42”。