2012-10-05 38 views
9

我試圖在Python 2.7.3中使用re模塊,並使用Unicode編碼的Devnagari文本。我已將from __future__ import unicode_literals添加到我的代碼的頂部,因此所有字符串文字都應該是unicode對象。Python unicode正則表達式匹配失敗,一些unicode字符-bug或錯誤?

但是,我遇到了Python的正則表達式匹配的一些奇怪的問題。例如,考慮這個名字:「किशोरी」。這是由我的一位用戶輸入的(拼寫錯誤)印地文名稱。任何印地語讀者都會認識到這是一個詞。

下返回匹配,因爲它應該:

re.search("^[\w\s][\w\s]*","किशोरी",re.UNICODE)

但這並不:

re.search("^[\w\s][\w\s]*$","किशोरी",re.UNICODE)

一些洞穴探險發現,只有一個在這串字符,字符0915 (क),被認爲屬於\ w字符類。這是不正確的,因爲Unicode字符數據庫file on "derived core properties"列出了這個字符串中的其他字符(我沒有檢查全部)作爲字母 - 就像它們一樣。

這只是Python實現中的一個錯誤嗎?我可以通過手動定義所有Devnagari字母數字字符作爲字符範圍來解決這個問題,但這會很痛苦。或者我做錯了什麼?

回答

7

它是一種bug in the re module並且它被固定在regex module

# -*- coding: utf-8 -*- 
from __future__ import unicode_literals 
import unicodedata 
import re 
import regex # $ pip install regex 

word = "किशोरी" 


def test(re_): 
    assert re_.search("^\\w+$", word, flags=re_.UNICODE) 

print([unicodedata.category(cp) for cp in word]) 
print(" ".join(ch for ch in regex.findall("\\X", word))) 
assert all(regex.match("\\w$", c) for c in ["a", "\u093f", "\u0915"]) 

test(regex) 
test(re) # fails 

輸出顯示有6碼點在"किशोरी",但只有3用戶感知的字符(擴展字形簇)。 如果在的字符中打破這個詞是錯誤的。Unicode Text Segmentation說:

字邊界,線邊界,和句子邊界不應 發生字形簇內:換句話說,一個字形簇 應的原子單元相對於所述過程確定 這些其他邊界。

這裏進一步強調的是礦

詞邊界\b被定義爲過渡從\w\W(或反向)的the docs

注意正式,\ b被定義爲\ w和一個W字符(反之亦然)之間的邊界,或\ w和 字符串的開始/結束之間的邊界,...

因此,要麼形成單個字符的所有編碼點是\w或它們都是\W。 在這種情況下,"किशोरी"匹配^\w{6}$


the docs for \w in Python 2

如果UNICODE被設置,這將匹配字符[0-9_]加 任何被分類爲在Unicode字符 屬性數據庫字母數字。

Python 3

匹配的Unicode字符字;此包括大多數字符,其中 可以是任何語言以及數字和 下劃線中的單詞的一部分。

regex文檔:

'字' 字符(issue #1693050)的定義:

一個 '字' 字的定義已經擴展對Unicode。它現在符合Unicode規範 http://www.unicode.org/reports/tr29/。這適用於\ w,\ W,\ b和 \ B。

根據unicode.org U+093F (DEVANAGARI VOWEL SIGN I)可以是alnum和字母等等regex也是正確的考慮\w即使我們遵循並非基於單詞邊界定義。

+0

是的,可以確認正則表達式模塊的工作。 [[:alnum:]] POSIX字符類也適用於正則表達式模塊。 – ShankarG

+0

@ShankarG:'perl'同意:'echoकिशोरी| perl -CS -ne'print if/^ \ w + $ /''(假設utf-8 io)。 – jfs

+0

將我的「已接受」標記更改爲此答案,因爲這實際上是正確的答案 - 確實是re模塊中的一個錯誤。 – ShankarG

3

從字符映射表:

ि

U + 093F DEVANAGARI VOWEL SIGN我

通用字符屬性

在統一的時間:1.1 的Unicode類別:馬克,間距組合

因此,從技術上說,這不是一封信,即使使用re.UNICODE也不屬於\w。您可以嘗試使用帶有Unicode字符屬性的regex來代替這些類型的字符。

+0

根據[鏈接到上述派生代碼屬性的列表](http://www.unicode.org/Public/UNIDATA/DerivedCoreProperties.txt)093F確實被分類爲字母字符。不知道發生了什麼事。在任何情況下,這些都必須被認爲是這樣的 - 這些字符從來不會獨立存在,它們是對現有字符的修改以指示特定元音的聲音(在這種情況下,字符指示後續的「ka」應該被讀作「き「)。 – ShankarG

+0

如果有人將這些和類似的字符排除爲字母字符,那麼使用它的任何語言(印地語,孟加拉語,馬拉地語等)中的任何一種語言都不會被認爲是按字母順序排列的。 – ShankarG

+0

「093E..0940; Alphabetic#Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II」Mc。 ** **了Mc。 –

2

我測試了以下:

import unicodedata 
for c in "किशोरी": 
    print unicodedata.category(c) 
    print unicodedata.name(c) 

其顯示在我的情況:

Lo 
DEVANAGARI LETTER KA 
Mc 
DEVANAGARI VOWEL SIGN I 
Lo 
DEVANAGARI LETTER SHA 
Mc 
DEVANAGARI VOWEL SIGN O 
Lo 
DEVANAGARI LETTER RA 
Mc 
DEVANAGARI VOWEL SIGN II 

Unicode的東西是很難調試,因爲複製粘貼可以胡來了數據,我不知道印地語。但是在某些語言中,你可以用unicode以不同的方式編碼字符。是否有可能,你必須在匹配前以某種方式規範你的字符串?對我來說,看起來好像是一個元音符號不匹配\w

+0

請參閱Ignacio的答案下面的評論。儘管如此,通過標準化你的意思是什麼?也許這會做到這一點。 – ShankarG

+0

我不記得我頭上的確切細節,但有一個字符存在於他們自己的,也可以是一個cominbation。例如德語'ä'。這是一個單一的字符,但據我所知,有可能將它編碼爲''+'標記,以便將這些點放在它上面。兩個版本之間都有轉換。對不起,我目前無法查看詳細信息。 – Achim

相關問題