2010-02-11 38 views
10

Tôi có một chuỗi ký tự mà tôi muốn có được độ dài thao tác dựa trên vị trí ký tự, v.v. Vấn đề là lần đầu tiên được tính hai lần, hoặc tôi đoán là ở vị trí 0 và ´ là ở vị trí 1.Python trả về độ dài chuỗi sai khi sử dụng các ký tự đặc biệt

Có cách nào có thể trong Python để có một ký tự như được biểu diễn là 1 không?

Tôi đang sử dụng mã hóa UTF-8 cho mã thực tế và trang web mà nó đang được xuất ra.

chỉnh sửa: Chỉ một số thông tin cơ bản về lý do tôi cần thực hiện việc này. Tôi đang làm việc trên một dự án dịch tiếng Anh sang Seneca (một dạng của ngôn ngữ bản địa Mỹ) và ́́ xuất hiện khá một chút. Một số quy tắc viết lại cho các từ nhất định đòi hỏi kiến ​​thức về vị trí thư (bản thân và các chữ cái xung quanh) và các đặc điểm khác, chẳng hạn như dấu trọng âm và các dấu khác.

+0

này rất dễ dàng để làm trong Perl với 'Unicode của nó :: GCString' mô-đun, có các phương thức bao gồm các thứ tiêu chuẩn như 'length',' substr' và 'index', tất cả đều hoạt động trên đồ thị không phải là điểm mã hoặc đơn vị mã. Nó thậm chí còn bao gồm một phương thức 'cột', đặc biệt hữu ích với các ký tự rộng/đầy đủ của Đông Á và với các ký tự kết hợp và như vậy. Đây thực sự là những gì bạn muốn, và cách thức những điều này sẽ làm việc. Tuy nhiên, tôi đã không tìm thấy tương đương trong Python. Perl có hỗ trợ Unicode linh hoạt hơn và mạnh mẽ hơn Python, và rất giỏi trong chuỗi, vì vậy bạn có thể xem xét nó. – tchrist

Trả lời

17

UTF-8 là mã hóa unicode sử dụng nhiều hơn một byte cho các ký tự đặc biệt. Nếu bạn không muốn độ dài của chuỗi được mã hóa, hãy giải mã đơn giản và sử dụng len() trên đối tượng unicode (và không phải đối tượng str!).

Dưới đây là một số ví dụ:

>>> # creates a str literal (with utf-8 encoding, if this was 
>>> # specified on the beginning of the file): 
>>> len('ë́aúlt') 
9 
>>> # creates a unicode literal (you should generally use this 
>>> # version if you are dealing with special characters): 
>>> len(u'ë́aúlt') 
6 
>>> # the same str literal (written in an encoded notation): 
>>> len('\xc3\xab\xcc\x81a\xc3\xbalt') 
9 
>>> # you can convert any str to an unicode object by decoding() it: 
>>> len('\xc3\xab\xcc\x81a\xc3\xbalt'.decode('utf-8')) 
6 

Tất nhiên, bạn cũng có thể truy cập vào các nhân vật duy nhất trong một đối tượng unicode như bạn sẽ làm gì trong một đối tượng str (họ đều kế thừa từ basestring và do đó có những phương pháp tương tự):

>>> test = u'ë́aúlt' 
>>> print test[0] 
ë 

Nếu bạn phát triển các ứng dụng cục bộ, nó thường là một ý tưởng tốt để sử dụng chỉ unicode -objects nội bộ, bằng cách giải mã tất cả các đầu vào bạn nhận được. Sau khi hoàn thành công việc, bạn có thể mã hóa kết quả lại thành 'UTF-8'. Nếu bạn giữ nguyên tắc này, bạn sẽ không bao giờ thấy máy chủ của bạn rơi vì bất kỳ UnicodeDecodeError s nội bạn có thể nhận thức khác;)

PS: Xin lưu ý, rằng strunicode datatype đã thay đổi đáng kể trong Python 3. Trong Python 3 chỉ có chuỗi unicode và chuỗi byte đơn giản mà không thể trộn lẫn được nữa. Điều đó sẽ giúp tránh cạm bẫy phổ biến với xử lý unicode ...

Kính trọng, Christoph

+0

+++ 1 :-) aus .at – Flavius

+0

Tôi nghĩ câu trả lời này làm nổi bật vấn đề - các điểm nhấn trên 'ea' khác với những câu hỏi trong câu hỏi :) –

+0

Ồ, bạn nói đúng. Tôi nghĩ rằng tôi bị mất nhân vật trong khi sao chép nó ... xin lỗi vì điều đó. Thật không may dường như không có ký tự đơn unicode nào có thể đại diện cho dấu trọng âm. Chưa bao giờ thấy một cái gì đó như thế trước đây (ít nhất là các âm sắc tiếng Đức mà tôi biết có thể được viết theo cả hai cách, như là một charecter đơn và kết hợp) – tux21b

1

Điều tốt nhất bạn có thể làm là sử dụng unicodedata.normalize() để phân hủy các nhân vật và sau đó lọc ra các điểm nhấn.

Đừng quên sử dụng unicode và các ký tự unicode trong mã của bạn.

5

Vấn đề là e đầu tiên đang được tính hai lần, hoặc tôi đoán E là ở vị trí 0 và 'là ở vị trí 1.

Yes. Đó là cách các điểm mã được định nghĩa bởi Unicode. Nói chung, bạn có thể yêu cầu Python để chuyển đổi một bức thư và một riêng biệt 'kết hợp' dấu phụ như U + 0301 kết hợp ACCENT CẤP TÍNH sử dụng bình thường Unicode:

>>> unicodedata.normalize('NFC', u'a\u0301') 
u'\xe1' # single character: á 

Tuy nhiên, không có nhân vật duy nhất trong Unicode cho “e với sự phân biệt và cấp tính ”vì không có ngôn ngữ nào trên thế giới từng sử dụng chữ 'ë́'.(Chuyển ngữ bính âm có "u với dấu móc và dấu trọng âm", nhưng không phải là "e".) Do đó hỗ trợ phông chữ kém; nó ám ảnh thực sự tồi tệ trong nhiều trường hợp và là một đốm màu lộn xộn trên trình duyệt web của tôi.

Để tìm ra ‘điểm có thể chỉnh sửa’ trong chuỗi mã Unicode là một công việc phức tạp đòi hỏi khá nhiều kiến ​​thức về ngôn ngữ. Đó là một phần của vấn đề "bố cục văn bản phức tạp", một khu vực cũng bao gồm các vấn đề như văn bản hai chiều và hình dạng và hình chữ nhật glpyh theo ngữ cảnh. Để làm bố cục văn bản phức tạp, bạn sẽ cần một thư viện như Uniscribe trên Windows, hoặc Pango nói chung (có giao diện Python).

Nếu, mặt khác, bạn chỉ đơn thuần muốn hoàn toàn bỏ qua tất cả các nhân vật kết hợp khi thực hiện một số, bạn có thể loại bỏ chúng một cách dễ dàng đủ:

def withoutcombining(s): 
    return ''.join(c for c in s if unicodedata.combining(c)==0) 

>>> withoutcombining(u'ë́aúlt') 
'\xeba\xfalt' # ëaúlt 
>>> len(_) 
5 
+0

+1 Câu trả lời này hoạt động. Lưu ý rằng ́ trong phần mã hiển thị sai, nhưng tôi tin rằng đó chỉ là vấn đề về phông chữ/trình duyệt. –

+0

Đây không phải là giải pháp tổng quát.Bạn cần một cách để có được đồ thị, không chỉ là điểm mã, và chuyển đổi sang NFC chỉ đơn giản là không đủ tốt cho trường hợp chung. Điều này rất dễ làm trong Perl, nơi mà lớp Unicode :: GCString hỗ trợ các hoạt động cơ bản như substr(), index() và tương tự, tất cả đều hoạt động trên đồ thị. Sau đó, kết hợp các ký tự không quan trọng và tất cả đều hoạt động đúng. Tuy nhiên, Python với kiến ​​thức của tôi không có sẵn một mô-đun như vậy. – tchrist

-1

mà Python phiên bản bạn đang sử dụng? Python 3.1 không có vấn đề này.

>>> print(len("ë́aúlt")) 
6 

Trân Djoudi

0

Bạn nói: Tôi có một ëaúlt chuỗi mà tôi muốn để có được chiều dài của một thao tác dựa trên vị trí ký tự và vân vân. Vấn đề là lần đầu tiên được tính hai lần, hoặc tôi đoán là ở vị trí 0 và ´ đang ở vị trí 1.

Bước đầu tiên trong bất kỳ vấn đề Unicode nào là biết chính xác dữ liệu của bạn là gì; đừng đoán. Trong trường hợp này, đoán của bạn là chính xác; nó sẽ không phải lúc nào.

"Chính xác những gì có trong dữ liệu của bạn": sử dụng hàm được tích hợp sẵn (cho nhiều thứ khác ngoài unicode). Một lợi thế hữu ích của việc hiển thị đầu ra repr() trong câu hỏi của bạn là người trả lời có chính xác những gì bạn có. Lưu ý rằng văn bản của bạn chỉ hiển thị trong bốn vị trí thay vì 5 với một số trình duyệt/phông chữ - chữ 'e' và dấu phụ của nó và 'a' được xâu chuỗi với nhau ở một vị trí.

Bạn có thể sử dụng hàm unicodedata.name() để cho bạn biết mỗi thành phần là gì.

Dưới đây là một ví dụ:

# coding: utf8 
import unicodedata 
x = u"ë́aúlt" 
print(repr(x)) 
for c in x: 
    try: 
     name = unicodedata.name(c) 
    except: 
     name = "<no name>" 
    print "U+%04X" % ord(c), repr(c), name 

Kết quả:

u'\xeb\u0301a\xfalt' 
U+00EB u'\xeb' LATIN SMALL LETTER E WITH DIAERESIS 
U+0301 u'\u0301' COMBINING ACUTE ACCENT 
U+0061 u'a' LATIN SMALL LETTER A 
U+00FA u'\xfa' LATIN SMALL LETTER U WITH ACUTE 
U+006C u'l' LATIN SMALL LETTER L 
U+0074 u't' LATIN SMALL LETTER T 

Bây giờ đọc câu trả lời @ bobince của :-)

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