2

Python は \uxxxx を文字列リテラル内の Unicode 文字エスケープとして扱います (たとえば、u"\u2014" は Unicode 文字 U+2014 として解釈されます)。しかし、標準の正規表現モジュールが \uxxxx をユニコード文字として扱わないことを発見しました (Python 2.7)。例:

codepoint = 2014 # Say I got this dynamically from somewhere

test = u"This string ends with \u2014"
pattern = r"\u%s$" % codepoint
assert(pattern[-5:] == "2014$") # Ends with an escape sequence for U+2014
assert(re.search(pattern, test) != None) # Failure -- No match (bad)
assert(re.search(pattern, "u2014")!= None) # Success -- This matches (bad)

明らかに、正規表現パターンを文字列リテラルとして指定できる場合、正規表現エンジン自体が \uxxxx エスケープを理解した場合と同じ効果が得られます。

test = u"This string ends with \u2014"
pattern = u"\u2014$"
assert(pattern[:-1] == u"\u2014") # Ends with actual unicode char U+2014
assert(re.search(pattern, test) != None)

しかし、パターンを動的に構築する必要がある場合はどうでしょうか?

4

2 に答える 2

4

unichr()関数を使用して、コードポイントから Unicode 文字を作成します。

pattern = u"%s$" % unichr(codepoint)
于 2013-05-14T11:17:21.560 に答える
1

1 つの可能性は、re メソッドを直接呼び出すのではなく、代わりに \u エスケープを理解できるものでそれらをラップすることです。このようなもの:

def my_re_search(pattern, s):
    return re.search(unicode_unescape(pattern), s)

def unicode_unescape(s):
        """
        Turn \uxxxx escapes into actual unicode characters
        """
        def unescape_one_match(matchObj):
                escape_seq = matchObj.group(0)
                return escape_seq.decode('unicode_escape')
        return re.sub(r"\\u[0-9a-fA-F]{4}", unescape_one_match, s)

動作例:

pat  = r"C:\\.*\u20ac" # U+20ac is the euro sign
>>> print pat
C:\\.*\u20ac

path = ur"C:\reports\twenty\u20acplan.txt"
>>> print path
C:\reports\twenty€plan.txt

# Underlying re.search method fails to find a match
>>> re.search(pat, path) != None
False

# Vs this:
>>> my_re_search(pat, path) != None
True

decode("unicode_escape") のアイデアを指摘してくれた Python の文字列でのエスケープ シーケンスの処理に感謝します。

ただし、decode("unicode_escape") を介してパターン全体をスローすることはできないことに注意してください。場合によっては機能しますが (ほとんどの正規表現の特殊文字はバックスラッシュを前に置いても意味が変わらないため)、一般的には機能しません。たとえば、ここで decode("unicode_escape") を使用すると、正規表現の意味が変わります。

pat = r"C:\\.*\u20ac" # U+20ac is the euro sign
>>> print pat
C:\\.*\u20ac # Asks for a literal backslash

pat_revised  = pat.decode("unicode_escape")
>>> print pat_revised
C:\.*€ # Asks for a literal period (without a backslash)
于 2013-05-14T11:14:11.987 に答える