2010-02-11 81 views
10

我有一個字符串,我想根據字符位置獲得操作的長度等等。問題是,第一個是計數兩次,或者我猜'是在位置0和'是在位置1.Python在使用特殊字符時返回錯誤的字符串長度

Python中是否有任何可能的方式有一個字符像?代表1?

我正在使用UTF-8編碼實際的代碼和它被輸出到的網頁。

編輯:只是一些背景,爲什麼我需要這樣做。我正在致力於一個將英語翻譯成塞內加(一種美洲原住民語言形式)的項目,並顯示出相當多的內容。某些詞的某些重寫規則需要了解字母位置(本身和周圍字母)以及其他特徵(如口音和其他變音符號)的知識。

+0

這是很容易在Perl做了'的Unicode :: GCString'模塊,其方法包括像'length','substr'和'index'這樣的標準東西,它們都以字符而不是代碼點或代碼單元進行操作。它甚至包含一個'columns'方法,這對於東亞寬/全字符以及結合字符等特別有用。這真的是你想要的,以及這些東西應該工作的方式。但是,我還沒有發現在Python中的等價物。 Perl比Python有更靈活和更強大的Unicode支持,並且在字符串方面很出色,所以你可能會考慮它。 – tchrist 2013-01-10 00:56:54

回答

17

UTF-8是一種對特殊字符使用多於一個字節的unicode編碼。如果您不想要編碼字符串的長度,請簡單解碼並在unicode對象(而不是str對象!)上使用len()

下面是一些例子:

>>> # 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 

當然,您也可以訪問單個字符在unicode對象就像你在一個str對象做(他們都是從basestring繼承,因此有相同的方法):

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

如果開發本地化的應用程序,它通常是一個好主意,在內部使用僅unicode -objects,通過你得到所有的輸入解碼。工作完成後,您可以再次將結果編碼爲「UTF-8」。如果你把這個原則,你永遠不會看到,因爲任何內部UnicodeDecodeError是你可能會得到,否則你的服務器崩潰;)

PS:請注意,該strunicode數據類型已經在Python 3顯著變化在Python 3只有unicode字符串和純字節字符串不能再混合。這應該有助於避免常見的陷阱有統一的處理...

問候, 克里斯托夫

+0

+++ 1 :-) aus .at – Flavius 2010-02-11 20:33:08

+0

我認爲這個答案突出了問題 - 'ea'上的口音與問題中的口音不同:) – 2010-02-11 20:33:47

+0

哦,你說得對。我想我在複製時失去了角色......對不起。不幸的是,似乎沒有可以表示口音的單個unicode字符。從來沒有見過類似的東西(至少我認識的德語變音可以寫成兩種方式,如單個和組合字符) – tux21b 2010-02-11 20:51:02

1

你能做的最好是用unicodedata.normalize()分解字符,然後篩選出口音。

不要忘記在代碼中使用unicode和unicode文字。

5

的問題是,所述第一E是被計算兩次,或我想E是在位置0和'是在位置1

是。這就是代碼點由Unicode定義的方式。在一般情況下,你可以要求Python來轉換一個字母和一個獨立的「結合」語音標記像U + 0301使用Unicode正常化組合重音符:

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

然而,在Unicode中沒有任何單個字符爲「e爲diaeresis和尖銳的口音「,因爲世界上沒有語言曾經使用過字母'ë'。(拼音音譯「有分音和尖銳的口音」,但不是'e'。)因此字體支持很差;它在許多情況下呈現得非常糟糕,並且在我的網絡瀏覽器上是一個雜亂的斑點。

要計算出Unicode碼點字符串中'可編輯點'的位置是一項棘手的工作,需要相當多的語言領域知識。這是「複雜文本佈局」問題的一部分,這個區域還包括諸如雙向文本和上下文glpyh整形和連字等問題。要做複雜的文本佈局,你需要一個庫,例如Windows上的Uniscribe,或者普遍的Pango(爲此有一個Python接口)。

如果,另一方面,你只是想完全忽略所有的組合字符做計數時,你可以擺脫他們很輕鬆地:

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此答案適用。請注意,代碼部分中的ë顯示錯誤,但我認爲這只是一個字體/瀏覽器問題。 – 2010-02-11 21:18:03

+0

這不是一個通用的解決方案。你需要一種方法來獲取字符,而不僅僅是代碼點,並且轉換爲NFC對於一般情況來說不夠好。在Perl中這很容易實現,Unicode :: GCString類支持substr(),index()等基本操作,它們都可以在字形上使用。然後組合角色並不重要,而且這一切都是正確的。但是,據我所知,Python沒有這樣的模塊可用。 – tchrist 2013-01-10 00:53:21

-1

其中Python版本您使用的? Python 3.1沒有這個問題。

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

問候 Djoudi

0

你說:我有我想要得到的長度字符串ëaúlt一個操縱基於字符的位置等。問題是第一個數字被計數兩次,或者我猜'ë位於第0位,'位於第1位。

處理任何Unicode問題的第一步是確切知道數據中的內容;不要猜測。在這種情況下,你的猜測是正確的。它並不總是如此。

「你的數據究竟是什麼」:使用repr()內置函數(除了unicode之外還有更多的東西)。在你的問題中顯示repr()輸出的一個有用的優點是,回答者確實擁有你所擁有的。請注意,您的文本僅以四個位置顯示,而不是一些瀏覽器/字體顯示爲五個 - 「e」及其變音符號和「a」在一個位置上被拼湊在一起。

您可以使用unicodedata.name()函數來告訴您每個組件是什麼。

下面是一個例子:

# 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 

結果:

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 

現在讀@ bobince的答案:-)