2012-10-16 33 views
13

Cân nhắc việc trao đổi sau đây trên IPython:Python: nhận được chiều dài chuỗi chính xác khi nó chứa cặp thay thế

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

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

Kết quả chính xác nên đã 7, nhưng vì thứ năm của bảy chữ Hán có một mã Unicode cao -point, nó được biểu diễn bằng UTF-8 bởi một "cặp thay thế", chứ không phải chỉ là một codepoint đơn giản, và kết quả là Python nghĩ rằng nó là hai ký tự chứ không phải một.

Thậm chí nếu tôi sử dụng unicodedata, mà trả về cặp thay thế một cách chính xác như một điểm mã duy nhất (\U00026177), khi truyền cho len() chiều dài sai vẫn trả về:

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 

Nếu không tiến hành các bước quyết liệt như biên dịch lại Python cho UTF-32, có cách nào đơn giản để có được độ dài chính xác trong các tình huống như thế này không?

Tôi đang sử dụng IPython 0.13, Python 2.7.2, Mac OS 10.8.2.

+0

Các cuộc thảo luận [tại đây] (http://stackoverflow.com/questions/9934752/platform-specific-unicode-semantics-in-python-2-7) và [tại đây] (http://stackoverflow.com/ câu hỏi/6922480/how-to-get-a-đáng tin cậy-unicode-character-count-in-python) có vẻ có liên quan. – DSM

+0

@DSM: Cảm ơn bạn đã đào bới. Liên kết đầu tiên của bạn cho thấy Python được biên dịch cho UTF-32 ("xây dựng rộng"), một cái gì đó tôi loại trừ trong câu hỏi của tôi. Trong lần thứ hai, câu trả lời của wberry cho thấy một đoạn mã phức tạp để thực sự đếm các ký tự thật. Cách giải quyết mặc định của tôi giống như sau, nhưng tôi hy vọng có một cái gì đó được xây dựng và trực tiếp hơn. – brannerchinese

+0

Tôi không thể sao chép kết quả của bạn ở đây (hộp Ubuntu, python 2.7.2). Đối với unicode u '\ u83ef \ u889e \ u8207 \ u7dfc \ U00026177 \ u540c \ u6b78' Tôi có độ dài bảy với cả len (s) và len (unicode.normalize ('NFC', s)) – Vicent

Trả lời

7

Tôi nghĩ rằng điều này đã được khắc phục trong 3.3. Xem:

http://docs.python.org/py3k/whatsnew/3.3.html
http://www.python.org/dev/peps/pep-0393/ (tìm kiếm wstr_length)

+0

Có. Nhưng trong 2.7, chúng tôi dường như là của riêng mình, trừ khi chúng tôi đang sử dụng một bản dựng rộng. Nó sẽ là một lúc trước khi tôi có thể di chuyển đến Py3, thật không may. – brannerchinese

+1

Tôi chuyển đến Py3 vào tháng Hai, và (trừ khi tôi bị buộc trở lại 2.7 bởi các thư viện như NLTK) những rắc rối của tôi với các cặp thay thế kết thúc. Đây thực sự là giải pháp tốt nhất. – brannerchinese

3

Bạn có thể ghi đè lên các chức năng len trong Python (xem: How does len work?) và thêm một câu lệnh if trong nó để kiểm tra sự unicode dài thêm.

6

tôi thực hiện một chức năng để làm điều này trên Python 2:

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

Bằng cách thay thế cặp thay thế với một nhân vật duy nhất, chúng tôi sửa chữa 'các len chức năng. Trên các chuỗi bình thường, điều này sẽ khá hiệu quả: vì mẫu không khớp, chuỗi gốc sẽ được trả về mà không sửa đổi. Nó cũng hoạt động trên các bản dựng Python (32-bit) rộng, vì mã hóa cặp thay thế sẽ không được sử dụng.

+0

Điều này sẽ không hoạt động với các ký tự unicode 4 byte, ví dụ: – wojcikstefan

+0

@wojcikstefan Nó nên làm gì, tại sao bạn nói như vậy? Cơ chế cặp thay thế mã hóa mọi thứ không phù hợp với UTF-16; ví dụ như DAAD DCAA. –

+0

Tôi mong đợi một bicep char đơn (giống như trên) trả về độ dài '1', nhưng' unicodeLen (u '\ U0001f4aa \ U0001f3ff') 'trả về' 2'. Mong đợi của tôi không chính xác @chrispy? – wojcikstefan

Các vấn đề liên quan