[英]Why is Java String.length inconsistent across platforms with unicode characters?
根據String.length的Java文檔 :
public int length()
返回此字符串的長度。
長度等於字符串中Unicode代碼單元的數量。
具體說明:
接口CharSequence中的長度
返回:
此對象表示的字符序列的長度。
但后來我不明白為什么以下程序HelloUnicode.java在不同平台上產生不同的結果。 根據我的理解,Unicode代碼單元的數量應該相同,因為Java應該總是代表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 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")
,字符串文字中嵌入“🙂”,這里是輸出。
視窗:
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
所以是的,它似乎是系統編碼的差異。 但那意味着字符串文字在不同平台上的編碼方式不同? 聽起來在某些情況下可能會出現問題。
另外......字節序列C3B0C5B8E284A2E2809A
來自哪里代表Windows中的笑臉? 這對我來說沒有意義。
為了完整性,使用.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
你必須小心指定編碼:
getBytes()
並再次使用環境或特定於平台的編碼。 所以它也被打破了(用問號代替不可解碼的表情符號)。 您需要將getBytes("UTF-8")
調用為與平台無關的。 所以回答提出的具體問題:
相同的字節長度,不同的字符串長度 為什么?
因為字符串文字是由java編譯器編碼的,並且java編譯器通常默認在不同的系統上使用不同的編碼。 這可能導致每個Unicode字符的字符單元數不同,從而導致不同的字符串長度。 在平台上使用相同選項傳遞-encoding
命令行選項將使它們一致地編碼。
為什么“\\ uD83D \\ uDE42”最終被編碼為Windows機器上的0x3F超出了我...
它不是在字符串中編碼為0x3F。 0x3f是問號。 當Java被要求通過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字節; 因此產生的字符串很長。 該字符串的值不重要; 這只是使用不正確編碼的結果。
你沒有考慮到,getBytes()返回平台默認編碼中的字節。 這在windows和centOS上有所不同。
另請參見如何在Java中查找默認字符集/編碼? 和String.getBytes()上的API文檔。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.