根据Java documentation for String.length:
public int length()
返回此字符串的长度。
长度等于字符串中Unicode代码单元的数量。
指定者:
接口CharSequence中的length
返回:
序列的长度 此对象代表的字符。
但是我不明白为什么下面的程序HelloUnicode.java在不同的平台上产生不同的结果。根据我的理解,自Java supposedly always represents strings in UTF-16起,Unicode代码单元的数量应该相同:
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”,我得到以下结果:
Windows:
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超出了我的范围...
Windows:
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)
使用.getBytes("utf-8")
,并在字符串文字中嵌入“”,这是输出。
Windows:
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")
,并在字符串文字中嵌入“”,以下是输出。
Windows:
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
答案 0 :(得分:4)
您必须小心指定编码:
getBytes()
,并且再次使用了环境或特定于平台的编码。因此,它也被破坏了(用问号代替难以置信的笑脸)。您需要调用getBytes("UTF-8")
来独立于平台。所以要回答提出的具体问题:
相同的字节长度,不同的字符串长度。为什么?
因为字符串文字是由Java编译器编码的,并且默认情况下Java编译器通常在不同的系统上使用不同的编码。这可能会导致每个Unicode字符使用不同数量的字符单元,从而导致不同的字符串长度。在各个平台上传递带有相同选项的-encoding
命令行选项将使它们一致地进行编码。
为什么“ \ uD83D \ uDE42”最终在Windows计算机上被编码为0x3F超出了我的范围...
字符串中未将其编码为0x3F。 0x3f是问号。当要求通过System.out.println
或getBytes
输出无效字符时,Java会将其放入其中,这是在将字符串形式的UTF-16文字表示形式编码为具有不同编码的字符串后尝试打印的情况到控制台,然后getBytes
。
但这是否意味着字符串文字在不同平台上的编码方式不同?
默认是。
还...字节序列C3B0C5B8E284A2E2809A从哪里来代表Windows中的笑脸?
这很令人费解。 “”字符(Unicode代码点U + 1F642)使用字节序列F0 9F 99 82以UTF-8编码存储在Java源文件中。然后,Java编译器使用平台默认编码Cp1252(Windows)读取源文件。 -1252),因此它将这些UTF-8字节视为Cp1252字符,通过将每个字节从Cp1252转换为Unicode来构成4个字符的字符串,从而得到U + 00F0 U + 0178 U + 2122 U + 201A。然后,getBytes("utf-8")
调用通过将4个字符的字符串编码为utf-8来将其转换为字节。由于字符串的每个字符都高于十六进制7F,因此每个字符都转换为2个或更多的UTF-8字节。因此,结果字符串是如此之长。该字符串的值不重要;这只是使用错误编码的结果。
答案 1 :(得分:1)
您没有考虑到,getBytes()以平台的默认编码返回字节。在Windows和centOS上是不同的。
另请参见How to Find the Default Charset/Encoding in Java?和String.getBytes()上的API文档。