字串相關工具
- 中文處理需求
- 正則表示式處理需求
議題:中文UNICODE非0字面
目前的Unicode字元分為17組編排,每組稱為平面(Plane),而每平面擁有65536(即216)個代碼點。然而目前只用了少數平面1。在中文世界的應用,可能會處理第二字面(表意文字補充平面)及15字面(私人使用區)。
在 Java 中,String保存的就是字元的Unicode碼,以前使用的是UCS-2編碼方案儲存。UCS-2固定使用兩個Byte來編碼一個字元,因此它只能編碼BMP(基本多語言平面,即0×0000-0xFFFF)範圍內的字元。雖然後來發現BMP範圍內的字符不夠用,但是出於內存消耗和兼容性的考慮,Java並沒有升到UCS-4,而是採用了UTF-16,char類型可看作其代碼單元2 。
這個做法導致了一些麻煩,如果所有字符都在BMP範圍內還沒事,若有BMP外的字符,就不再是一個代碼單元對應一個字元。一些與長度有關的方法,就出現了錯誤:length方法返回的是代碼單元的個數,而不是字符的個數;charAt方法返回的自然也是一個代碼單元而不是一個字元。建構時,也不能直接用\u的脫逸宣告方式對字串設值。
- 以下示範原生 API 有異常的使用情境。
// JDK String 處理第二字面的文字測試:
final String sampleText = "𠀗𠀖";
final String str1 = new String(new int[] { 0x20016, 0x20017 }, 0, 2);
final String str2 = "\u20016\u20017"; // 無效的宣告方式,得到的結果是 0x2001, 6, 0x2001, 7
LOGGER.info("str1:{}, 應為𠀗𠀖", str1); // str1:𠀖𠀗, 應為𠀗𠀖
LOGGER.info("str2:{}, 應為𠀗𠀖", str2); // str2: 6 7, 應為𠀗𠀖
LOGGER.info("length:{}, 應為2", sampleText.length()); // length:4, 應為2
LOGGER.info("substring(1):{}, 應為𠀖", sampleText.substring(1));// substring(1):?𠀖, 應為𠀖
LOGGER.info("charAt(0):{}, 應為𠀗", sampleText.charAt(0)); // charAt(0):?, 應為𠀗
LOGGER.info("charAt(1):{}, 應為𠀖", sampleText.charAt(1)); // charAt(1):?, 應為𠀖
LOGGER.info("indexOf(𠀖):{}, 應為1", sampleText.indexOf("𠀖")); // indexOf(𠀖):2, 應為1
1. https://zh.wikipedia.org/wiki/Unicode字符平面映射 ↩
2. 從Java String實例來理解ANSI、Unicode、BMP、UTF等編碼概念 - http://www.codeceo.com/article/java-string-ansi-unicode-bmp-utf.html ↩