2012-10-16 32 views
13

考慮在IPython的以下對話:的Python:當它含有得到正確的字符串長度代理對

In [1]: s = u'華袞與縕同歸' 

In [2]: len(s) 
Out[2]: 8 

正確的輸出應該是7,但由於這七個中國文字的第五具有較高的Unicode碼點,它由UTF-8中的「代理對」表示,而不僅僅是一個簡單的代碼點,因此Python認爲它是兩個字符而不是一個。

即使我使用unicodedata,它正確地返回代理對作爲單個碼點(\U00026177),傳遞給len()當錯誤的長度仍然返回:

In [3]: import unicodedata 

In [4]: unicodedata.normalize('NFC', s) 
Out[4]: u'\u83ef\u889e\u8207\u7dfc\U00026177\u540c\u6b78' 


In [5]: len(unicodedata.normalize('NFC', s)) 
Out[5]: 8 

如果不採取激烈步驟等重新編譯的Python UTF-32,是否有一種簡單的方法在這種情況下獲得正確的長度?

我在IPython 0.13,Python 2.7.2,Mac OS 10.8.2。

+0

The discussion [here](http://stackoverflow.com/questions/9934752/platform-specific-unicode-semantics-in-python-2-7)和[here](http://stackoverflow.com/問題/ 6922480/how-to-get-a-reliable-unicode-character-in-python)似乎相關。 – DSM

+0

@DSM:感謝您挖掘這些。你的第一個鏈接顯示了爲UTF-32編譯的Python(「寬版」),這是我在我的問題中排除的。在第二部分中,wberry的回覆顯示了一段精確的代碼來實際計算真實的字符。我的默認解決方法就像後者,但我希望存在一些內置的,更直接的東西。 – brannerchinese

+0

我無法在這裏重現你的結果(Ubuntu盒子,python 2.7.2)。對於unicode u'\ u83ef \ u7207 \ u7dfc \ u00026177 \ u540c \ u7b78'我得到的長度都是len(s)和len(unicode.normalize('NFC',s))7 – Vicent

回答

7

我認爲這已經固定在3.3。請參閱:

http://docs.python.org/py3k/whatsnew/3.3.html
http://www.python.org/dev/peps/pep-0393/(搜索wstr_length

+0

是的。但在2.7中,我們顯然是獨立存在的,除非我們正在使用廣泛的構建。不幸的是,我可能會搬到Py3。 – brannerchinese

+1

我在2月份搬到了Py3,並且(除了當我被NLTK等圖書館強制回到2.7時),代理對的麻煩就結束了。這現在確實是最好的解決方案。 – brannerchinese

6

我做一個函數來做到這Python的2:

SURROGATE_PAIR = re.compile(u'[\ud800-\udbff][\udc00-\udfff]', re.UNICODE) 
def unicodeLen(s): 
    return len(SURROGATE_PAIR.sub('.', s)) 

通過使用單個字符替換代理對,我們「修復」的len功能。在普通字符串上,這應該是非常有效的:由於模式不匹配,原始字符串將不加修改地返回。它也應該在寬的(32位)Python版本上工作,因爲代理對編碼不會被使用。

+0

這不適用於4字節的unicode字符,例如 – wojcikstefan

+0

@wojcikstefan它應該這樣做,你爲什麼這麼說?代理對機制編碼任何不適合UTF-16的內容;例如D83D DCAA。 –

+0

我期望一個bicep char(就像上面的那個)返回長度爲'1',但'unicodeLen(u'\ U0001f4aa \ U0001f3ff')'返回'2'。我的期望不正確@chrispy? – wojcikstefan

相關問題