2012-10-16 13 views
13

ipython aşağıdaki alışverişini düşünün içerdiğinde:Python: Doğru dize uzunluğunu almak o vekil çiftleri

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

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

doğru çıktı 7 olmalıydı, ama bu yedi Çince karakter beşinci yüksek Unicode koduna sahip olduğundan -point, UTF-8'de sadece bir basit kod noktasından ziyade bir "temsilci çift" ile temsil edilir ve sonuç olarak Python bunun bir değil iki karakter olduğunu düşünür. len() geçirilen zaman

Tek bir kod noktasıyla (\U00026177) olarak doğru yedek çifti döndüren unicodedata, kullansanız bile, yanlış uzunluk hala döndürülür:

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 

için Python yeniden derlenmesine gibi köklü adımlar atılmadan UTF-32, bunun gibi durumlarda doğru uzunluğu elde etmek için basit bir yol var mı?

IPython üzerinde çalışıyorum 0.13, Python 2.7.2, Mac OS 10.8.2.

+0

Tartışmalar [burada] (http://stackoverflow.com/questions/9934752/platform-specific-unicode-semantics-in-python-2-7) ve [burada] (http://stackoverflow.com/ sorular/6922480/nasıl yapılır-güvenilir-unicode-karakter-say-in-python) ilgili görünüyor. – DSM

+0

@DSM: Bunları kazandığınız için teşekkür ederiz. İlk bağlantınız, Python'u UTF-32 ("geniş yapılı") için derlediğimi gösteriyor. İkincisi, gerçekte doğru karakterleri saymak için böğürtlenin cevabı ayrıntılı bir kod parçası gösterir. Benim varsayılan geçici çözümüm, ikincisine benzer, ama daha çok doğrudan ve yerleşik bir şey olduğunu umuyorum. – brannerchinese

+0

Sonuçlarınızı burada yeniden oluşturamıyorum (Ubuntu box, python 2.7.2). Unicode için u '\ u83ef \ u889e \ u8207 \ u7dfc \ U00026177 \ u540c \ u6b78' Ben hem len (ler) hem de len (unicode.normalize ('NFC', s)) ile yedi bir uzunluk elde ediyorum – Vicent

cevap

7

Bu bence 3.3'te düzeldi. Bkz: İçinde deyimi ekstra uzun unicode kontrol etmek ve eğer eklemek bir:

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

+0

Evet. Fakat 2.7'de geniş bir yapı kullanmıyorsak, görünüşe göre kendi başımıza çıkıyor. Maalesef Py3'e taşınmadan önce bir süre olacak. – brannerchinese

+1

Şubat ayında Py3'e taşındım ve (NLTK gibi kütüphaneler tarafından 2.7'ye geri zorlandığım zaman hariç) vekil çiftlerle olan sıkıntılarım bitti. Bu gerçekten en iyi çözümdür. – brannerchinese

6

Ben bir işlev Python 2 bunu yapmak olun:

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

tek karakteri ile vekil çiftleri değiştirerek, biz len fonksiyonunu 'düzeltmek'. Normal dizgelerde, bu oldukça verimli olmalı: desen eşleşmeyeceğinden, orijinal dize değiştirilmeden döndürülecektir. Birden fazla (32 bit) Python üzerinde çalışılmalı, aynı zamanda çift çift kodlama kullanılmayacaktır.

+0

Bu, 4 baytlık unicode karakterleriyle çalışmaz, ör. – wojcikstefan

+0

@wojcikstefan Yapmalı, neden böyle söylüyorsunuz? Vekil çift mekanizması, UTF-16'ya uymayan her şeyi kodlar; örneğin D83D DCAA'dır. –

+0

Tek bir ponponun (yukarıdaki gibi) '1' uzunluğunu döndürmesini beklerdim, ancak 'unicodeLen (u' \ U0001f4aa \ U0001f3ff ') '' 2' döndürür. Benim beklentim yanlış @chrispy mi? – wojcikstefan