字串型別存在缺陷

周昌鴻發表於2013-12-03

我的前一篇文章《我們不需要字串型別》引起了不少爭議,反饋帶來了許多不同的觀點,通常大家都認為字串型別是一個有用的特性。經過更多地研究,我可以確定一個事情:目前大多數的字串型別實現都存在缺陷。

大家都有種錯覺:字串能處理比它自身提供得多的功能。我們使用它的時候,並沒有深究它是否可靠。這會導致使用這些字串程式設計的程式出現問題,尤其是在國際化的問題上。大多情況,如果我們不是依賴字串型別,我們可以做到更好(避免這些問題)。

 

衡量字串的實現

我考察了一下字串在一些常用場合的表現。我會闡述各種場合下,期望的返回和一些實際返回值。我原本想使用一個表格來展示這些不同結果,不過所有測試的語言表現都很糟糕,使得這樣的對比缺乏意義。

noël

當我使用一個編碼為“noe\u0308l”的unicode字串代表“noël”,我測試瞭如下內容:

  1. 是否列印正確?是的,大多數語言都能正確處理這個列印。不過ideone.com在這個問題上處理有些問題(所以,應該仔細地測試你的字串型別)。
  2. 字串反轉是什麼值?“lëon”,對嗎?大多數語言都處理失敗。得到的結果通常都是“l̈eon”(雙引號在‘I’上,而不是在‘e’上)。由於沒有使用特定的字串處理類,僅僅是對編碼字元陣列進行了反轉。
  3. 前三個字元是什麼?多數語言的得到的是”noe”,這和期望的“noë”是不同的。這容易導致在確定字元時出現大問題,我想大多數人都不希望得到這個結果。這再次表明字串型別僅僅只是被當作編碼元素陣列處理。
  4. 字串長度是多少?基本上返回都是5。再一次,顯示字串型別僅僅只是把它當成陣列處理,而不是真正的字串文字。

上述的這些問題,試想你正在你喜歡的編輯器中編寫這些文字會出現什麼現象。我期望‘ë’被當作一個字元來處理。我不喜歡當我進行回退/刪除操作時,僅僅刪除了其部分字元。我希望拷貝前三個字元的時候應該包含’e’上的雙點。

 

□□

(譯註:由於WordPress系統的原因,原文中的小標題表情在本站也無法顯示,所以乾脆就用兩個□替代了)

上面的字元在unicode碼中比較少見,顯示示兩隻貓(我希望你有相關的字型——如果沒有看到的話,上面的標題時一隻開心的貓和一隻不高興的貓,是unicode表情字元的一部分。)挑出上面的字元是因為這些字元在基本多語言之外(BMP)。而這會導致那些使用UTF-16字元編碼的語言出現問題(Java,C#,JavaScript)

  1. 長度?Python使用unicode返回正確的2,其他UTF-16語言都返回4:這些字元需要使用字元代理。
  2. 減去第一個字元是什麼?Python正確返回傷心的貓表情,其他UTF-16語言返回一個無效字元和傷心的貓字元的一半。
  3. 反轉字元。Python返回正常的貓表情,其他UTF-16語言返回錯誤的字串,在ideone中我可能發現了C#的一個缺陷,它甚至都沒有返回亂碼,而是是什麼也沒輸出。([ideone defect])

其他使用字串函式庫的語言,像C++,perl和Python2字串庫也處理失敗。它們都假定只是一個字元陣列,而忽略了字元編碼。Python3使用了unicode作為內建編碼,所以解決了該問題。Perl使用UTF-8模式可以正確處理這兩隻貓,不過在前面的例子卻處理失敗(“noël”字串)。

bae

這個字串包含了一個疊加字元,“ffl”是單個的Unicode字元,存在主要是為了相容性。但是這個例子可以很好測試字元轉換。

變換大小寫產生的是什麼?我沒有找到一個語言的列印不是“BAfflE”。請注意這裡的疊加字元還是保持小寫,而期望的值應該是“BAFFLE”。

Unicod有一個特殊的大小寫轉換:這個疊加字元被處理為三個編碼元素。由於沒有準守這個額外的規則,語言處理大寫轉換函式導致了一個有趣的現象:字串被大寫仍然保留了小寫的字元。

再測試一下noël

讓我們測試一下兩個邏輯上相同的字元的不同組合形式。這裡“noël”使用了預構字元“ë”。

  1. 預構字串是否和非預構字串相等?所有的測試都是失敗的。不過有些語言提供了Unicode標準化處理函式庫。在這些語言中,字串的標準化結果是相等的。JavaScript沒有這樣的函式庫,這顯得有些詫異,因為它是一門專門為UI準備的語言,而這更需要處理Unicode編碼。

儘管人們會認為這些規範化處理和字串操作不是典型的基本字元,但是這些基本操作都包含在文書處理中,如果這些內容不被包含,那麼我們需要字串的目的是什麼呢?

字串存在缺陷

我期望你們去測試一下你常用的語言。如果你的工作和國際化相關,那麼這就對你很重要了,你需要知道你的字串型別怎麼工作的。一旦你測試了,你就會認識到字串型別究竟是怎麼處理的。在我看來這些實現都存在缺陷。

我承認這些問題並不明顯,字串處理是個複雜的主題,不過最低限度我們應該包含相關的字符集(一些字串型別會提供相關的功能函式,比如Perl的GCString)。不過這不在這篇文章的討論範圍。不過這是一個很好的相關字串型別。

我前述文章的觀點顯得更為深刻了。我寧願使用字元陣列而不是一個存在缺陷的字串型別。我不會假定字元陣列會有一些錯誤的返回:上述結果的測試返回對於字元陣列都是邏輯上合理的。事實上,如果使用unicode字元陣列,那麼這些測試結果會比那些實現字串型別的語言好得多。

相關文章