Pythonの正規表現で漢字・ひらがな・カタカナ・英数字を判定・抽出・カウント

Posted: | Tags: Python, 文字列, 正規表現, Unicode

Pythonで正規表現を使って、漢字・ひらがな・カタカナ・英数字などの文字種を判定・抽出・カウントする方法について説明する。

はじめに文字種の判定・抽出・カウントについて説明し、そのあとで特定の文字種にマッチする正規表現パターンの作り方と具体例を紹介する。

  • 正規表現で文字種を判定
    • 文字列全体が特定の文字種か判定
    • 文字列が特定の文字種を含むか判定
  • 正規表現で特定の文字種を抽出、文字数をカウント
  • 特定の文字種にマッチする正規表現パターンの作り方
    • マッチさせたい文字を羅列
    • 範囲を指定
    • Unicodeプロパティを指定(regexを使用)
    • 複数の文字種にマッチ
  • 正規表現パターンの具体例
    • 英字(半角・全角・大文字・小文字)
    • 数字(半角・全角・漢数字など)
    • ASCII文字
    • 記号
    • ひらがな
    • カタカナ
    • 漢字
    • 絵文字・顔文字
  • 常用漢字にマッチする正規表現パターン

標準ライブラリの正規表現モジュールreの基本的な使い方については以下の記事を参照。

正規表現で文字種を判定

説明のための簡単な正規表現パターンとして[a-z]+を使う。1文字以上の連続する半角英字にマッチする。

様々な文字種にマッチする正規表現パターンについては後述。

文字列全体が特定の文字種か判定

文字列全体が特定の文字種か判定するにはfullmatch()を使う。

文字列全体が正規表現パターンにマッチするとマッチオブジェクトを、マッチしない部分が含まれているとNoneを返す。

import re

p = re.compile('[a-z]+')
print(p.fullmatch('abc'))
# <re.Match object; span=(0, 3), match='abc'>

print(p.fullmatch('abc123'))
# None

マッチオブジェクトはTrueNoneFalseと判定されるため、if文で条件分岐したい場合は、条件式としてそのままfullmatch()を使えばよい。

s = 'abc'

if p.fullmatch(s):
    print('match')
else:
    print('no match')
# match
s = 'abc123'

if p.fullmatch(s):
    print('match')
else:
    print('no match')
# no match

文字列が特定の文字種を含むか判定

文字列が特定の文字種を含むか判定するにはsearch()を使う。

文字列に正規表現パターンにマッチする部分が含まれているとマッチオブジェクトを、含まれていないとNoneを返す。

p = re.compile('[a-z]+')
print(p.search('123abcABC'))
# <re.Match object; span=(3, 6), match='abc'>

print(p.search('123ABC'))
# None

上の例と同じく、if文の条件式でそのまま使える。

正規表現で特定の文字種を抽出、文字数をカウント

文字列から特定の文字種の部分文字列を抽出するにはfindall()を使う。

正規表現パターンにマッチする部分文字列を要素とするリストを返す。

p = re.compile('[a-z]+')
result = p.findall('123abcABCxyz')
print(result)
# ['abc', 'xyz']

print(type(result))
# <class 'list'>

print(type(result[0]))
# <class 'str'>

マッチする部分がないと空のリストを返す。

print(p.findall('123ABC'))
# []

文字列を要素とするリストは文字列のメソッドjoin()でひとつの文字列に連結できる。

さらに組み込み関数len()でその文字数をカウントすることで、特定の文字種の文字数を取得できる。

s_result = ''.join(p.findall('123abcABCxyz'))
print(s_result)
# abcxyz

print(len(s_result))
# 6

これを利用すると元の文字列における特定の文字種(ひらがなや漢字など)の割合などの算出が可能。

なお、絵文字の中には、複数のUnicodeコードポイントで表現されている絵文字シーケンスと呼ばれるものがある。国旗や職業の絵文字などが該当する。Python3.7.3時点ではlen()はUnicodeコードポイントの個数を返すため、正しい文字数がカウントできない。

print(len('🇯🇵'))
# 2

ここではそのような絵文字は考慮しないが、そのようなことが起こり得るということは記しておく。

特定の文字種にマッチする正規表現パターンの作り方

マッチさせたい文字を羅列

正規表現パターン[...]は文字の集合を表し、括弧内の文字のいずれか1文字にマッチする。+は直前のパターンを1回以上繰り返したものにマッチする。したがって[...]+は、括弧内の文字のみで構成された1文字以上の文字列にマッチする。

例えば、0から9までの半角数字にマッチするパターンは以下のように書ける。

p = re.compile('[0123456789]+')
print(p.fullmatch('123'))
# <re.Match object; span=(0, 3), match='123'>

何の工夫もない方法だが、対象の文字種に含まれる文字をすべて羅列すれば、その文字種にマッチする正規表現パターンを作成できる。

場合によっては有効な手段。テキストファイルからすべての常用漢字の文字を抽出しパターンを作成する例を最後に示す。

なお、[^...]のように先頭に^をつけると括弧内の文字以外の文字にマッチする。特定の文字種以外にマッチさせたい場合に便利。

p = re.compile('[^0123456789]+')
print(p.fullmatch('123'))
# None

print(p.fullmatch('abc'))
# <re.Match object; span=(0, 3), match='abc'>

範囲を指定

Unicodeコードポイントが連続する文字の範囲は[]内で-を使って指定できる。

これを利用すると0から9までの半角数字にマッチするパターンは以下のように書ける。

p = re.compile('[0-9]+')
print(p.fullmatch('123'))
# <re.Match object; span=(0, 3), match='123'>

Unicodeコードポイントの順番が合っていないとエラーとなる。0U+00309U+0039なので09の順番でないとダメ。

# p = re.compile('[9-0]+')
# error: bad character range 9-0 at position 1

この範囲指定を利用するとUnicodeのブロックにマッチする正規表現パターンを作成できる。

Unicodeのブロックは連続するコードポイントの範囲で、一意の名前が付けられている。

例えばHiraganaという名前のブロックはU+3040からU+309Fまでの範囲に割り当てられている。

したがって、このブロックにマッチする正規表現パターンは以下のようになる。U+3040は未割り当てなのでU+3041からにしている。文字列中では16進数のUnicodeコードポイントを\xXX, \uXXXX, \UXXXXXXXXのように書ける。

p = re.compile('[\u3041-\u309F]+')
print(p.fullmatch('あいうえおぁぃぅぇぉわをん'))
# <re.Match object; span=(0, 13), match='あいうえおぁぃぅぇぉわをん'>

\uXXXX表記ではなく、その文字自体を使ってももちろん問題ない。U+3041U+309F

p = re.compile('[ぁ-ゟ]+')
print(p.fullmatch('あいうえおぁぃぅぇぉわをん'))
# <re.Match object; span=(0, 13), match='あいうえおぁぃぅぇぉわをん'>

こののように、日常的に使われていない文字が含まれている場合もある。WikipediaやUnicodeコンソーシアムのページなどで実際にどのような文字が含まれているかを確認してから目的に合った範囲を指定すればよい。以下の記事を参照。

なお、raw文字列の場合、\uXXXXが1文字ではなくそのままの並びとして認識される。raw文字列と組み合わせたい場合はformat()を使う。

print('[\u3041-\u309F]+')
# [ぁ-ゟ]+

print(r'[\u3041-\u309F]+')
# [\u3041-\u309F]+

print(r'[{}-{}]+'.format('\u3041', '\u309F'))
# [ぁ-ゟ]+

Unicodeプロパティを指定(regexを使用)

Unicodeには一般カテゴリ(General_Category)やスクリプト(Script)など様々なプロパティが設定されており、上述のブロック(Block)もその中のひとつ。

標準ライブラリのreモジュールではUnicodeのプロパティを利用できないが、サードパーティライブラリのregexでは利用できる。pip(またはpip3)でインストールする必要がある。

regexはreに対して後方互換性を有しており、reのメソッドなどがそのまま使える。

regexでは\p{property=value}の形でプロパティの値を指定し、それに対応する文字にマッチするパターンを作成できる。例えば、ブロック(Block)がHiraganaのパターンは上述のような範囲指定ではなく以下のように書ける。

import regex

p = regex.compile(r'\p{Block=Hiragana}+')
print(p.fullmatch('あいうえおぁぃぅぇぉわをんゟ'))
# <regex.Match object; span=(0, 14), match='あいうえおぁぃぅぇぉわをんゟ'>

スクリプト(Script)にもHiraganaという名前の値があり、そちらに対応させたい場合は以下の通り。

p = regex.compile(r'\p{Script=Hiragana}+')
print(p.fullmatch('あいうえおぁぃぅぇぉわをんゟ🈀'))
# <regex.Match object; span=(0, 15), match='あいうえおぁぃぅぇぉわをんゟ🈀'>

上述の通り、ブロックは連続するコードポイントの範囲だが、スクリプトは離れたコードポイントも含まれる。例えばスクリプトHiraganaに含まれる文字の一覧は以下。ブロックHiraganaには含まれていない古形(変体仮名)や🈀といった文字が含まれている。

\p{property=value}ではなく\p{value}と書くことも可能。この場合、General_Category, Script, Block, binary property(YesNoの値を持つプロパティ)の順で値valueがチェックされる。

例えば\p{Hiragana}の場合はScriptBlockに値が存在するが、Scriptのほうが優先される。

p = regex.compile(r'\p{Hiragana}+')
print(p.fullmatch('あいうえおぁぃぅぇぉわをんゟ🈀'))
# <regex.Match object; span=(0, 15), match='あいうえおぁぃぅぇぉわをんゟ🈀'>

間違いを防ぐためには\p{property=value}と明示したほうがいい。

なお、regexはすべてのUnicodeプロパティに対応しているわけではない模様。バージョン2019.6.5時点ではsubheadを指定するとエラーになった。

# p = regex.compile(r'\p{subhead=Hiragana_letters}+')
# error: unknown property at position 28

複数の文字種にマッチ

例えば、半角英字小文字を表すa-zと半角英字大文字を表すA-Zの両方にマッチさせるパターンを作成したい場合は、単純に[]内に連結して書けばよい。

p = re.compile('[a-zA-Z]+')
print(p.fullmatch('abcABC'))
# <re.Match object; span=(0, 6), match='abcABC'>

個別の文字にマッチさせたい場合も同様。[]内に追加すればOK。

正規表現のメタ文字として使われている記号はパターン文字列中でバックスラッシュ\でエスケープする必要があるが、[]内ではそのまま書いてよい。[]内では]-のみエスケープする必要あり(-[]内の先頭か末尾にある場合はエスケープしなくてもよい)。

半角英字小文字大文字に加えて-[, ]にもマッチさせたい場合の例は以下の通り。

p = re.compile('[a-zA-Z\-[\]]+')
print(p.fullmatch('abc-[ABC]'))
# <re.Match object; span=(0, 9), match='abc-[ABC]'>

regexの場合も同じ。スクリプトHiraganaに加えて、スクリプトKatakanaと長音符(伸ばし棒)、半角英字小文字a-zにマッチさせたい場合。

p = regex.compile(r'[\p{Script=Hiragana}\p{Script=Katakana}ーa-z]+')
print(p.fullmatch('あーいアイウabc🈀'))
# <regex.Match object; span=(0, 10), match='あーいアイウabc🈀'>

なお、これまでのように[]内に追加した場合、それぞれの文字種が混ざってマッチする。それぞれの文字種ごとにマッチさせたい場合は|で複数のパターンのいずれかにマッチするように指定すればよい。A|B|Cのように3つ以上のパターンも指定可能。

p = re.compile('[a-zA-Z]+')
print(p.findall('abcABCxyzXYZ'))
# ['abcABCxyzXYZ']

p = re.compile('[a-z]+|[A-Z]+')
print(p.findall('abcABCxyzXYZ'))
# ['abc', 'ABC', 'xyz', 'XYZ']

正規表現パターンの具体例

ここからは特定の文字種にマッチする正規表現パターンの具体例を紹介する。上述のように、スクリプトなどの正規表現のプロパティを使う場合はregexのインストールが必要。

上のひらがなの例からも分かるように、Unicodeには日常ではほとんど使わないような文字も多数含まれている。どこまでの厳密さが必要かはアプリケーションによって異なる。以下はあくまでも一例であり、抜け漏れがあっても当サイトは責任を負わないので、間違いが許されないようなクリティカルな用途の場合は十分注意されたい。

Unicodeのブロックやスクリプトにどういった文字が含まれているかを調べたい場合は以下の記事を参照。

英字(半角・全角・大文字・小文字)

英字(アルファベット)は半角・全角、大文字・小文字がそれぞれ連続したコードポイントの範囲に配置されている。

p = re.compile('[a-z]+')
print(p.fullmatch('abc'))
# <re.Match object; span=(0, 3), match='abc'>

p = re.compile('[A-Z]+')
print(p.fullmatch('ABC'))
# <re.Match object; span=(0, 3), match='ABC'>

p = re.compile('[a-z]+')
print(p.fullmatch('abc'))
# <re.Match object; span=(0, 3), match='abc'>

p = re.compile('[A-Z]+')
print(p.fullmatch('ABC'))
# <re.Match object; span=(0, 3), match='ABC'>

大文字と小文字、半角と全角は連続していないので注意。すべてにマッチさせたい場合は以下のようにそれぞれの範囲を連結する。

p = re.compile('[a-zA-Za-zA-Z]+')
print(p.fullmatch('abcABCabcABC'))
# <re.Match object; span=(0, 12), match='abcABCabcABC'>

より広範囲なラテン文字というくくりは、Unicodeプロパティのスクリプトの値Latinでマッチできる。アクセントやウムラウト付きの文字や合字が含まれる。

p = regex.compile(r'\p{Script=Latin}+')
print(p.fullmatch('AÁÀÂÄÆ'))
# <regex.Match object; span=(0, 6), match='AÁÀÂÄÆ'>

数字(半角・全角・ローマ数字・漢数字など)

半角全角のアラビア数字はそれぞれ連続したコードポイントの範囲に配置されている。

p = re.compile('[0-9]+')
print(p.fullmatch('123'))
# <re.Match object; span=(0, 3), match='123'>

p = re.compile('[0-9]+')
print(p.fullmatch('123'))
# <re.Match object; span=(0, 3), match='123'>

ローマ数字や漢数字などの数値を表す文字は、UnicodeプロパティのNumeric_Typeの値Numericでマッチできる。10以上の数値が括弧や丸に囲まれた文字も含まれるが、十進のアラビア数字(0-9)は含まれないので注意。

p = regex.compile(r'\p{Numeric_Type=Numeric}+')
print(p.fullmatch('一二三ⅠⅡⅢ百万億⑩⑽'))
# <regex.Match object; span=(0, 11), match='一二三ⅠⅡⅢ百万億⑩⑽'>

print(p.fullmatch('123'))
# None

ローマ数字は連続したコードポイントの範囲に配置されているので範囲指定可能。

p = re.compile('[\u2160-\u217F]+')
print(p.fullmatch('ⅠⅡⅢ'))
# <re.Match object; span=(0, 3), match='ⅠⅡⅢ'>

上のNumeric_Type=Numeric一覧のリンクから分かるように、漢数字はコードポイントがバラバラなので、必要な文字を羅列してパターンを作る必要がある。Numeric_Type=Numericにはといった文字も含まれているが、どこまで含むかは用途次第。

p = re.compile('[〇一二三四五六七八九十百千万億兆]+')
print(p.fullmatch('三十五億'))
# <re.Match object; span=(0, 4), match='三十五億'>

ASCII文字

ASCII文字(半角英数、記号、制御文字など)は連続したコードポイントの範囲に配置されている。

p = re.compile('[\u0000-\u007F]+')
print(p.fullmatch('(abc)!_(123)?'))
# <re.Match object; span=(0, 13), match='(abc)!_(123)?'>

記号

上のASCII文字一覧のリンクからも分かるように、ASCII文字の記号はブロック内にバラバラに配置されているのでそれぞれ指定する必要がある。

p = re.compile('[\u0020-\u002F\u003A-\u0040\u005B-\u0060\u007B-\u007E]+')
print(p.fullmatch('!_? ()[]'))
# <re.Match object; span=(0, 8), match='!_? ()[]'>

マッチさせたい記号のみ羅列してももちろんOK。前述のように[]内では]-のみバックスラッシュ\でエスケープする必要がある。

p = re.compile(r'[,.!?[\]()]+')
print(p.fullmatch(',.!?[]()'))
# <re.Match object; span=(0, 8), match=',.!?[]()'>

ASCII記号の全角版は別ブロックに配置されている。このブロックには日本語の記号の半角版(カギカッコの半角など)も含まれている。

p = re.compile('[\uFF01-\uFF0F\uFF1A-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65]+')
print(p.fullmatch('!?()[]「」'))
# <re.Match object; span=(0, 8), match='!?()[]「」'>

CJK(Chinese, Japanese, Korean)の記号はブロックにまとまっている。

日本語の記号をマッチさせたい場合、他の言語の記号が混ざっていることを気にしなければこの範囲を指定すればよい。ASCII記号の全角版()は含まれていないので注意。

p = re.compile('[\u3000-\u303F]+')
print(p.fullmatch('、。「」【】'))
# <re.Match object; span=(0, 6), match='、。「」【】'>

ASCII記号の全角版および日本語の記号の半角版と合わせると以下の通り。

p = re.compile('[\uFF01-\uFF0F\uFF1A-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\u3000-\u303F]+')
print(p.fullmatch('!?()[]「」、。「」【】'))
# <re.Match object; span=(0, 14), match='!?()[]「」、。「」【】'>

日本語の記号のみをマッチさせたい場合は、上の「CJKの記号及び句読点」一覧のリンクを参考に必要な範囲を指定すればよい。枡記号などが最後の方にあるので注意。

ひらがな

基本的なひらがなはブロックにまとまっている。

p = re.compile('[\u3041-\u309F]+')
print(p.fullmatch('あいうえおぁぃぅぇぉ'))
# <re.Match object; span=(0, 10), match='あいうえおぁぃぅぇぉ'>

p = re.compile('[ぁ-ゟ]+')
print(p.fullmatch('あいうえおぁぃぅぇぉ'))
# <re.Match object; span=(0, 10), match='あいうえおぁぃぅぇぉ'>

さらに広範囲なひらがなはUnicodeプロパティのScriptの値Hiraganaでマッチできる。古形(変体仮名)や🈀といった文字が含まれている。

カタカナ

基本的な全角・半角のカタカナはそれぞれブロックにまとまっている。

p = re.compile('[\u30A1-\u30FF]+')
print(p.fullmatch('アイウエオァィゥェォ'))
# <re.Match object; span=(0, 10), match='アイウエオァィゥェォ'>

p = re.compile('[\ァ-ヿ]+')
print(p.fullmatch('アイウエオァィゥェォ'))
# <re.Match object; span=(0, 10), match='アイウエオァィゥェォ'>

p = re.compile('[\uFF66-\uFF9F]+')
print(p.fullmatch('アイウエオァィゥェォ'))
# <re.Match object; span=(0, 10), match='アイウエオァィゥェォ'>

p = re.compile('[ヲ-゚]+')
print(p.fullmatch('アイウエオァィゥェォ'))
# <re.Match object; span=(0, 10), match='アイウエオァィゥェォ'>

さらに広範囲なカタカナはUnicodeプロパティのScriptの値Katakanaでマッチできる。丸囲み文字や単位などが含まれている。

漢字

漢字の数は膨大。

regexが使えるのであればUnicodeプロパティのScriptの値Hanでマッチできる。

p = regex.compile(r'\p{Script=Han}+')
print(p.fullmatch('漢字'))
# <regex.Match object; span=(0, 2), match='漢字'>

UnicodeプロパティのScript_ExtensionsにもHanという値がある。Script=Hanよりも範囲が広く、, , といった文字にもマッチする。という文字はScript=HanではマッチしないがScript_Extensions=Hanではマッチする。

p = regex.compile(r'\p{Script_Extensions=Han}+')
print(p.fullmatch('漢字〆㈠㈱㊊㏩'))
# <regex.Match object; span=(0, 7), match='漢字〆㈠㈱㊊㏩'>

なお、上述のように、\p{Han}と書くと\p{Script=Han}と認識されるので注意。

Unicodeプロパティが使えない場合は範囲で指定する必要がある。

厳密には上のScript=HanまたはScript_Extensions=Hanの一覧のリンクからブロックを確認して範囲を指定する必要があるが、とりあえず使用頻度の高い文字だけマッチさせたいという場合は以下のブロックがメインとなる。

さらに拡張や互換も含みたい場合は以下のブロック。

これらに含まれていないがよく使われる文字として々, 〆, 〇U+3005 - U+3007)がある。

以上をすべて組み合わせると以下の通り。これはあくまでも一例。用途によって適宜追加、削減すればOK。

p = re.compile('[\u2E80-\u2FDF\u3005-\u3007\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\U00020000-\U0002EBEF]+')
print(p.fullmatch('漢字'))
# <re.Match object; span=(0, 2), match='漢字'>

8桁の16進数のコードポイントは\uではなく\Uを使う必要があるので注意。

絵文字・顔文字

絵文字はregexが使えるのであればUnicodeプロパティのEmojiEmoji_Presentationでマッチできる。いずれもバイナリ(二値: YesNo)のプロパティ。

上の一覧のリンクで確認できるように、Emoji=Yesには普通の数字も含まれるので注意。

p = regex.compile(r'\p{Emoji=Yes}+')
print(p.fullmatch('💯123'))
# <regex.Match object; span=(0, 4), match='💯123'>

p = regex.compile(r'\p{Emoji_Presentation=Yes}+')
print(p.fullmatch('💯'))
# <regex.Match object; span=(0, 1), match='💯'>

なお、Basic_Emojiというプロパティもあるが、regexのバージョン2019.6.5時点では対応していない模様。

# p = regex.compile(r'\p{Basic_Emoji=Yes}+')
# error: unknown property at position 19

絵文字は多くのブロックに存在している。

主なブロックは「その他の記号及び絵記号(Miscellaneous Symbols and Pictographs)」(U+1F300 - U+1F5FF)。

また、基本的な顔文字は「顔文字(Emoticons)」(U+1F600 - U+1F64F)にある(ほかのブロックにもある)。

p = re.compile('[\U0001F300-\U0001F5FF]+')
print(p.fullmatch('💯'))
# <re.Match object; span=(0, 1), match='💯'>

p = re.compile('[\U0001F600-\U0001F64F]+')
print(p.fullmatch('😀'))
# <re.Match object; span=(0, 1), match='😀'>

なお、カウントの節に書いたように、絵文字の中には、複数のUnicodeコードポイントで表現されている絵文字シーケンスと呼ばれるものがある(国旗や職業の絵文字など)。

ここでは深追いしないが、厳密に処理を行うにはもろもろ考慮する必要がある(らしい)ので注意。

常用漢字にマッチする正規表現パターン

Unicodeコードポイントがバラバラの文字をひとつの文字種としてパターン化したい場合、該当する文字の一覧から作成可能。

ここでは常用漢字を例とする。

文化庁による常用漢字表はpdfで扱いにくいので、以下のサイトのテキストファイルを使わせていただく。

pandasを使ってテキストファイルを読み込む。

import re
import pandas as pd

url = 'https://raw.githubusercontent.com/cjkvi/cjkvi-tables/15569eaae99daef9f99f0383e9d8efbec64a7c5a/joyo2010.txt'

df = pd.read_csv(url, header=None, skiprows=1, delimiter='\t')

print(df.shape)
# (2136, 6)

print(df.head())
#    0    1   2   3       4              5
# 0  亜    亞   7  7S     NaN              ア
# 1  哀  NaN   9  7S     NaN  アイ、あわ-れ、あわ-れむ
# 2  挨  NaN  10  7S  2010.0             アイ
# 3  愛  NaN  13   4     NaN             アイ
# 4  曖  NaN  17  7S  2010.0             アイ

print(df.tail())
#       0    1   2   3       4        5
# 2131  脇  NaN  10  7S  2010.0       わき
# 2132  惑  NaN  12  7S     NaN  ワク、まど-う
# 2133  枠  NaN   8  7S  1981.0       わく
# 2134  湾    灣  12  7S     NaN       ワン
# 2135  腕  NaN  12  7S     NaN    ワン、うで

文字列のメソッドjoin()で列の要素をひとつの文字列に連結。

kanji = ''.join(df.iloc[:, 0])

print(kanji)
# 亜哀挨愛曖悪握圧扱宛嵐安案暗以衣位囲医依委威為畏胃尉異移萎偉椅彙意違維慰遺緯域育一壱逸茨芋引印因咽姻員院淫陰飲隠韻右宇羽雨唄鬱畝浦運雲永泳英映栄営詠影鋭衛易疫益液駅悦越謁閲円延沿炎怨宴媛援園煙猿遠鉛塩演縁艶汚王凹央応往押旺欧殴桜翁奥横岡屋億憶臆虞乙俺卸音恩温穏下化火加可仮何花佳価果河苛科架夏家荷華菓貨渦過嫁暇禍靴寡歌箇稼課蚊牙瓦我画芽賀雅餓介回灰会快戒改怪拐悔海界皆械絵開階塊楷解潰壊懐諧貝外劾害崖涯街慨蓋該概骸垣柿各角拡革格核殻郭覚較隔閣確獲嚇穫学岳楽額顎掛潟括活喝渇割葛滑褐轄且株釜鎌刈干刊甘汗缶完肝官冠巻看陥乾勘患貫寒喚堪換敢棺款間閑勧寛幹感漢慣管関歓監緩憾還館環簡観韓艦鑑丸含岸岩玩眼頑顔願企伎危机気岐希忌汽奇祈季紀軌既記起飢鬼帰基寄規亀喜幾揮期棋貴棄毀旗器畿輝機騎技宜偽欺義疑儀戯擬犠議菊吉喫詰却客脚逆虐九久及弓丘旧休吸朽臼求究泣急級糾宮救球給嗅窮牛去巨居拒拠挙虚許距魚御漁凶共叫狂京享供協況峡挟狭恐恭胸脅強教郷境橋矯鏡競響驚仰暁業凝曲局極玉巾斤均近金菌勤琴筋僅禁緊錦謹襟吟銀区句苦駆具惧愚空偶遇隅串屈掘窟熊繰君訓勲薫軍郡群兄刑形系径茎係型契計恵啓掲渓経蛍敬景軽傾携継詣慶憬稽憩警鶏芸迎鯨隙劇撃激桁欠穴血決結傑潔月犬件見券肩建研県倹兼剣拳軒健険圏堅検嫌献絹遣権憲賢謙鍵繭顕験懸元幻玄言弦限原現舷減源厳己戸古呼固股虎孤弧故枯個庫湖雇誇鼓錮顧五互午呉後娯悟碁語誤護口工公勾孔功巧広甲交光向后好江考行坑孝抗攻更効幸拘肯侯厚恒洪皇紅荒郊香候校耕航貢降高康控梗黄喉慌港硬絞項溝鉱構綱酵稿興衡鋼講購乞号合拷剛傲豪克告谷刻国黒穀酷獄骨駒込頃今困昆恨根婚混痕紺魂墾懇左佐沙査砂唆差詐鎖座挫才再災妻采砕宰栽彩採済祭斎細菜最裁債催塞歳載際埼在材剤財罪崎作削昨柵索策酢搾錯咲冊札刷刹拶殺察撮擦雑皿三山参桟蚕惨産傘散算酸賛残斬暫士子支止氏仕史司四市矢旨死糸至伺志私使刺始姉枝祉肢姿思指施師恣紙脂視紫詞歯嗣試詩資飼誌雌摯賜諮示字寺次耳自似児事侍治持時滋慈辞磁餌璽鹿式識軸七𠮟失室疾執湿嫉漆質実芝写社車舎者射捨赦斜煮遮謝邪蛇尺借酌釈爵若弱寂手主守朱取狩首殊珠酒腫種趣寿受呪授需儒樹収囚州舟秀周宗拾秋臭修袖終羞習週就衆集愁酬醜蹴襲十汁充住柔重従渋銃獣縦叔祝宿淑粛縮塾熟出述術俊春瞬旬巡盾准殉純循順準潤遵処初所書庶暑署緒諸女如助序叙徐除小升少召匠床抄肖尚招承昇松沼昭宵将消症祥称笑唱商渉章紹訟勝掌晶焼焦硝粧詔証象傷奨照詳彰障憧衝賞償礁鐘上丈冗条状乗城浄剰常情場畳蒸縄壌嬢錠譲醸色拭食植殖飾触嘱織職辱尻心申伸臣芯身辛侵信津神唇娠振浸真針深紳進森診寝慎新審震薪親人刃仁尽迅甚陣尋腎須図水吹垂炊帥粋衰推酔遂睡穂随髄枢崇数据杉裾寸瀬是井世正生成西声制姓征性青斉政星牲省凄逝清盛婿晴勢聖誠精製誓静請整醒税夕斥石赤昔析席脊隻惜戚責跡積績籍切折拙窃接設雪摂節説舌絶千川仙占先宣専泉浅洗染扇栓旋船戦煎羨腺詮践箋銭潜線遷選薦繊鮮全前善然禅漸膳繕狙阻祖租素措粗組疎訴塑遡礎双壮早争走奏相荘草送倉捜挿桑巣掃曹曽爽窓創喪痩葬装僧想層総遭槽踪操燥霜騒藻造像増憎蔵贈臓即束足促則息捉速側測俗族属賊続卒率存村孫尊損遜他多汰打妥唾堕惰駄太対体耐待怠胎退帯泰堆袋逮替貸隊滞態戴大代台第題滝宅択沢卓拓託濯諾濁但達脱奪棚誰丹旦担単炭胆探淡短嘆端綻誕鍛団男段断弾暖談壇地池知値恥致遅痴稚置緻竹畜逐蓄築秩窒茶着嫡中仲虫沖宙忠抽注昼柱衷酎鋳駐著貯丁弔庁兆町長挑帳張彫眺釣頂鳥朝貼超腸跳徴嘲潮澄調聴懲直勅捗沈珍朕陳賃鎮追椎墜通痛塚漬坪爪鶴低呈廷弟定底抵邸亭貞帝訂庭逓停偵堤提程艇締諦泥的笛摘滴適敵溺迭哲鉄徹撤天典店点展添転塡田伝殿電斗吐妬徒途都渡塗賭土奴努度怒刀冬灯当投豆東到逃倒凍唐島桃討透党悼盗陶塔搭棟湯痘登答等筒統稲踏糖頭謄藤闘騰同洞胴動堂童道働銅導瞳峠匿特得督徳篤毒独読栃凸突届屯豚頓貪鈍曇丼那奈内梨謎鍋南軟難二尼弐匂肉虹日入乳尿任妊忍認寧熱年念捻粘燃悩納能脳農濃把波派破覇馬婆罵拝杯背肺俳配排敗廃輩売倍梅培陪媒買賠白伯拍泊迫剝舶博薄麦漠縛爆箱箸畑肌八鉢発髪伐抜罰閥反半氾犯帆汎伴判坂阪板版班畔般販斑飯搬煩頒範繁藩晩番蛮盤比皮妃否批彼披肥非卑飛疲秘被悲扉費碑罷避尾眉美備微鼻膝肘匹必泌筆姫百氷表俵票評漂標苗秒病描猫品浜貧賓頻敏瓶不夫父付布扶府怖阜附訃負赴浮婦符富普腐敷膚賦譜侮武部舞封風伏服副幅復福腹複覆払沸仏物粉紛雰噴墳憤奮分文聞丙平兵併並柄陛閉塀幣弊蔽餅米壁璧癖別蔑片辺返変偏遍編弁便勉歩保哺捕補舗母募墓慕暮簿方包芳邦奉宝抱放法泡胞俸倣峰砲崩訪報蜂豊飽褒縫亡乏忙坊妨忘防房肪某冒剖紡望傍帽棒貿貌暴膨謀頰北木朴牧睦僕墨撲没勃堀本奔翻凡盆麻摩磨魔毎妹枚昧埋幕膜枕又末抹万満慢漫未味魅岬密蜜脈妙民眠矛務無夢霧娘名命明迷冥盟銘鳴滅免面綿麺茂模毛妄盲耗猛網目黙門紋問冶夜野弥厄役約訳薬躍闇由油喩愉諭輸癒唯友有勇幽悠郵湧猶裕遊雄誘憂融優与予余誉預幼用羊妖洋要容庸揚揺葉陽溶腰様瘍踊窯養擁謡曜抑沃浴欲翌翼拉裸羅来雷頼絡落酪辣乱卵覧濫藍欄吏利里理痢裏履璃離陸立律慄略柳流留竜粒隆硫侶旅虜慮了両良料涼猟陵量僚領寮療瞭糧力緑林厘倫輪隣臨瑠涙累塁類令礼冷励戻例鈴零霊隷齢麗暦歴列劣烈裂恋連廉練錬呂炉賂路露老労弄郎朗浪廊楼漏籠六録麓論和話賄脇惑枠湾腕

print(len(kanji))
# 2136

print(kanji[:30])
# 亜哀挨愛曖悪握圧扱宛嵐安案暗以衣位囲医依委威為畏胃尉異移萎偉

print(kanji[-30:])
# 廉練錬呂炉賂路露老労弄郎朗浪廊楼漏籠六録麓論和話賄脇惑枠湾腕

[...]+の中に文字列を埋め込んで正規表現パターン化。

p = re.compile('[{}]+'.format(kanji))
print(p.fullmatch('常用漢字'))
# <re.Match object; span=(0, 4), match='常用漢字'>

旧字体も追加したい場合は該当の列を追加する。旧字体の列は欠損値NaNを含むのでdropna()で取り除く必要がある。

kanji_ex = ''.join(df.iloc[:, 0]) + ''.join(df.iloc[:, 1].dropna())

print(len(kanji_ex))
# 2500

print(kanji_ex[-30:])
# 亂覽欄龍隆虜兩獵綠淚壘類禮勵戾靈齡曆歷戀練鍊爐勞郞朗廊樓錄灣

p_ex = re.compile('[{}]+'.format(kanji_ex))
print(p_ex.fullmatch('常用漢字'))
# <re.Match object; span=(0, 4), match='常用漢字'>

例えば野村證券の字にもマッチさせたい場合は旧字体を含む後者のパターンを使う。

print(p.fullmatch('野村證券'))
# None

print(p_ex.fullmatch('野村證券'))
# <re.Match object; span=(0, 4), match='野村證券'>

なお、𠮟U+20B9F)はU+53F1)で代用されることが多い。にもマッチさせたい場合はさらに追加すればよい。

print('叱' in kanji_ex)
# False

print('𠮟' in kanji_ex)
# True

kanji_ex2 = kanji_ex + '叱'

p_ex2 = re.compile('[{}]+'.format(kanji_ex2))
print(p_ex2.fullmatch('常用漢字野村證券叱𠮟'))
# <re.Match object; span=(0, 10), match='常用漢字野村證券叱𠮟'>

このように、該当の文字をすべて含む文字列を用意できれば、任意の文字の集合にマッチする正規表現パターンを作成できる。

関連カテゴリー

関連記事