home changes contents help options

029:特定の文字コードで正規表現マッチを行う

ユニコード文字列の使用

レシピの主旨と反しますが。通常、 Python でマルチバイト文字列を扱う時には、いったんユニコード文字列に変換してから行います(なお、ユニコード文字列の実装は CPython? では UCS-2 か UCS-4 です)。 re モジュールはユニコード文字列を正しく扱うことができます。

入力をユニコードへ変換する

ファイルなどからの入力は通常、バイト列なので decode メソッドでユニコード文字列に変換します。

>>> b = open(u'029_特定の文字コードで正規表現マッチを行う.txt', 'r').read()
>>> b[:16]
'029:\x93\xc1\x92\xe8\x82\xcc\x95\xb6\x8e\x9a\x83R'
>>> s = b.decode('shift-jis')
>>> s[:16]
u'029:\u7279\u5b9a\u306e\u6587\u5b57\u30b3\u30fc\u30c9\u3067\u6b63\u898f\u8868'
>>> print s[:16]
029:特定の文字コードで正規表

ユニコード文字列で正規表現マッチを行う

パターン文字列もユニコード文字列で与えるようにすれば OK です。

>>> import re
>>> re.search(u'文字コード', s)
<_sre.SRE_Match object at 0x00A54528>

ponyguruma (正規表現ライブラリ「鬼車」のラッパー)

では、変換をはさまずに正規表現マッチを行うにはどのようにしたらよいでしょうか。Python の re モジュールはこのような状況を想定してはいないので、別のライブラリを導入する必要があります。

ここでは正規表現ライブラリである 鬼車 およびそのラッパーである ponyguruma を用いてみます。 鬼車 では正規表現オブジェクトごとに異なるエンコーディングを指定することができます。

>>> from ponyguruma import *
>>> from ponyguruma.constants import ENCODING_UTF8
>>>
>>> s = u'特定の文字コードで正規表現マッチを行う'.encode('utf-8')
>>> ss = u'マッチ'.encode('utf-8')
>>> r = Regexp(ss, encoding=ENCODING_UTF8)
>>> r.search(s)
<Match groups: 0, span: (39, 48)>

エンコーディングを正しく指定しているならば、次のような誤判定もおこりません。

>>> re.search(u'錦'.encode('euc-jp'), u'橋囮'.encode('euc-jp'))
<_sre.SRE_Match object at 0x00A54528>
>>>
>>> from ponyguruma import *
>>> from ponyguruma.constants import ENCODING_EUC_JP
>>> r = Regexp(u'錦'.encode('euc-jp'), encoding=ENCODING_EUC_JP)
>>> r.search(u'橋囮'.encode('euc-jp'))
>>>

JIS 漢字コードのならびに依存した正規表現でも、エンコーディング次第で使うことができます。 [亜-腕]? は JIS 漢字コード由来のエンコーディングで用いると、 JIS 第一水準の範囲の漢字にマッチするという正規表現です。なお、「阿」は第一水準、「弌」は第二水準の漢字です。

from ponyguruma import *
from ponyguruma.constants import ENCODING_SJIS, ENCODING_UTF8

r = Regexp(u'[亜-腕]'.encode('shift-jis'), encoding=ENCODING_SJIS)
m = r.search(u'阿'.encode('shift-jis'))
print repr(m)
r = Regexp(u'[亜-腕]'.encode('shift-jis'), encoding=ENCODING_SJIS)
m = r.search(u'弌'.encode('shift-jis'))
print repr(m)

r = Regexp(u'[亜-腕]'.encode('utf-8'), encoding=ENCODING_UTF8)
m = r.search(u'阿'.encode('utf-8'))
print repr(m)
r = Regexp(u'[亜-腕]'.encode('utf-8'), encoding=ENCODING_UTF8)
m = r.search(u'弌'.encode('utf-8'))
print repr(m)

出力は次のようになります。 Shift-JIS では意図した動作となっていることが確認できます。

<Match groups: 0, span: (0, 2)>
None
None
<Match groups: 0, span: (0, 3)>