note.nkmk.me

Pythonで文字列を比較(完全一致、部分一致、大小関係など)

Date: 2019-06-12 / tags: Python, 文字列操作, 正規表現

Pythonで文字列同士を比較して判定する方法について説明する。

  • 完全一致(等価): ==, !=
  • 部分一致: in, not in
  • 前方一致・後方一致(先頭・末尾): startswith(), endswith()
  • 文字列の大小関係(順番): <, <=, >, >=
  • 大文字小文字を区別せずに比較
  • 正規表現パターンにマッチ: re.search(), re.fullmatch()

文字列を検索して一致する部分の位置を取得したり、個数をカウントしたりしたい場合は以下の記事を参照。

スポンサーリンク

完全一致(等価): ==, !=

数値などと同じく、2つの文字列が完全一致(等価)かどうかの判定には==演算子を使う。等価だとTrue、等価でないとFalseが返される。

print('abc' == 'abc')
# True

print('abc' == 'xyz')
# False

大文字と小文字は区別される。以降の演算子やメソッドでも同様。大文字と小文字を区別せずに比較する方法は後述。

print('abc' == 'ABC')
# False

!=は、等価でないとTrue、等価だとFalseを返す。

print('abc' != 'xyz')
# True

print('abc' != 'abc')
# False

部分一致: in, not in

部分一致、すなわち、ある文字列の中にもう一方の文字列が含まれているかどうかの判定にはin演算子を使う。

x in yとしたとき、xyに含まれている(xyの部分文字列である)とTrue、含まれていないとFalseが返される。xの各文字がバラバラにyに含まれている場合はFalse

print('bbb' in 'aaa-bbb-ccc')
# True

print('xxx' in 'aaa-bbb-ccc')
# False

print('abc' in 'aaa-bbb-ccc')
# False

not inは、含まれていないとTrue、含まれているとFalseを返す。

print('xxx' not in 'aaa-bbb-ccc')
# True

print('bbb' not in 'aaa-bbb-ccc')
# False

in, not inはリストなどの要素の判定にも使われる。if文の条件式として使う例などの詳細は以下の記事を参照。

前方一致・後方一致(先頭・末尾): startswith(), endswith()

前方一致、すなわち、ある文字列の先頭が指定した文字列から始まっているかを判定するには文字列のメソッドstartswith()を使う。

s = 'aaa-bbb-ccc'

print(s.startswith('aaa'))
# True

print(s.startswith('bbb'))
# False

引数には文字列のタプルを指定することも可能。先頭がタプルの要素のいずれかの文字列に一致するとTrue、どれにも一致しないとFalseが返される。タプルではなくリストだとエラーとなるので注意。

print(s.startswith(('aaa', 'bbb', 'ccc')))
# True

print(s.startswith(('xxx', 'yyy', 'zzz')))
# False

# print(s.startswith(['a', 'b', 'c']))
# TypeError: startswith first arg must be str or a tuple of str, not list

後方一致、すなわち、ある文字列の末尾が指定した文字列で終わっているかを判定するには文字列のメソッドendswith()を使う。使い方はstartswith()と同じ。

print(s.endswith('ccc'))
# True

print(s.endswith('bbb'))
# False

print(s.endswith(('aaa', 'bbb', 'ccc')))
# True

文字列の大小関係(順番)

文字列も数値などと同様に<, <=, >, >=演算子で比較できる。1文字目が同じなら2文字目、3文字目...と順番に比較される。

print('a' < 'b')
# True

print('aa' < 'ab')
# True

print('abc' < 'abcd')
# True

Python3では文字列strはUnicodeであり、文字列の大小関係(順番)は文字のUnicodeコードポイント(文字コード)で判定される。

文字のUnicodeコードポイントは組み込み関数ord()で取得可能。

print(ord('a'))
# 97

print(ord('b'))
# 98

UnicodeコードポイントはUnicodeコンソーシアムのサイトやWikipediaでも確認できる。

例えば最初の方のUnicode一覧は以下のWikipediaのページを参照。

アルファベットはまとまって配置されているが、大文字のほうが小文字よりもコードポイントが小さい(若い)。

print('Z' < 'a')
# True

print(ord('Z'))
# 90

ひらがな、カタカナもそれぞれまとまって配置されている。基本的には五十音順の通りで、ひらがなのほうがカタカナよりもコードポイントが小さい(若い)。

print('あ' < 'い')
# True

print(ord('あ'))
# 12354

print(ord('い'))
# 12356

print('ん' < 'ア')
# True

print(ord('ん'))
# 12435

print(ord('ア'))
# 12450

アルファベットやひらがな、カタカナは分かりやすい順番に(A→Z、あ→ん、ア→ン)に並んでいるが、漢字はバラバラ。読み仮名順などに並んでいるわけではない。

人間のイメージどおりの大小関係になるとは限らないので注意。

print('乙' < '亜')
# True

print(ord('乙'))
# 20057

print(ord('亜'))
# 20124

print('七' < '三')
# True

print(ord('七'))
# 19971

print(ord('三'))
# 19977

リストのメソッドsort()や組み込み関数sorted()で文字列のリストをソートする場合も、Unicodeコードポイントを元に順序が決まる。

print(sorted(['aaa', 'abc', 'Abc', 'ABC']))
# ['ABC', 'Abc', 'aaa', 'abc']

なお、組み込み関数sorted()に文字列を渡すと、ソートされた各文字が要素となるリストが返される。この場合も、Unicodeコードポイントを元に順序が決まる。以下の例のように、漢数字などはむしろバラバラになる。

print(sorted('一二三四五六七八九十'))
# ['一', '七', '三', '九', '二', '五', '八', '六', '十', '四']

任意の順番にソートしたい場合は以下の記事を参照。

大文字小文字を区別せずに比較

ここまで説明した演算子やメソッドはすべて大文字小文字を区別して処理する。

大文字小文字を区別せずに比較したい場合は、両方の文字列を大文字または小文字に変換して統一すればよい。文字列をすべて大文字に変換するにはupper()、小文字に変換するにはlower()を使う。

s1 = 'abc'
s2 = 'ABC'

print(s1 == s2)
# False

print(s1.lower() == s2.lower())
# True

なお、日本語のような大文字小文字の区別がない文字を含む文字列にupper()lower()メソッドを適用しても問題ない。大文字小文字の区別がない文字はそのまま変化しない(無視される)。

s = 'Abcあいうえお'

print(s.lower())
# abcあいうえお

print(s.upper())
# ABCあいうえお

正規表現パターンにマッチ: re.search(), re.fullmatch()

正規表現を利用するとより柔軟な処理が可能。

標準ライブラリのreモジュールを使う。詳細は以下の記事を参照。reモジュールの正規表現シンタックスの注意点などにも触れている。

re.search()

部分一致・前方一致・後方一致にはre.search()を使う。第一引数に正規表現パターンの文字列、第二引数に対象の文字列を指定する。

なお、前方一致にはre.match()も使えるがここでは触れない。詳細は前掲のreモジュールの記事を参照。

正規表現パターンには様々なメタ文字(特殊文字)が利用できるが、単に文字列をそのまま指定することも可能。その文字列が含まれているとマッチオブジェクト、含まれていないとNoneが返される。

import re

s = 'aaa-AAA-123'

print(re.search('aaa', s))
# <re.Match object; span=(0, 3), match='aaa'>

print(re.search('xxx', s))
# None

マッチオブジェクトは常にTrueと判定されるので、if文で条件式として使いたい場合はre.search()またはその返り値をそのまま使えばよい。詳細は以下の記事を参照。マッチオブジェクトのメソッドでマッチした部分の文字列や位置を取得することもできる。

メタ文字^は文字列の先頭、$は文字列の末尾にマッチする。これらを使うと前方一致・後方一致が実現できる。

print(re.search('^aaa', s))
# <re.Match object; span=(0, 3), match='aaa'>

print(re.search('^123', s))
# None

print(re.search('aaa$', s))
# None

print(re.search('123$', s))
# <re.Match object; span=(8, 11), match='123'>

そのほか様々なメタ文字や特殊シーケンスが利用可能。例えば、[A-Z]は大文字のアルファベットいずれか1文字を表し、+は直前のパターンが1回以上繰り返されることを表す。したがって、[A-Z]+はアルファベット大文字が1文字以上連続している部分文字列にマッチする。

print(re.search('[A-Z]+', s))
# <re.Match object; span=(4, 7), match='AAA'>

このように、正規表現パターンを活用すると特定の文字列ではなくある条件を満たす文字列との一致を判定できる。

ワイルドカード的な指定など、正規表現パターンを活用した基本的な例を以下の記事で紹介しているので参考にされたい。

re.fullmatch()

文字列全体が正規表現パターンにマッチしているかどうかを判定するにはre.fullmatch()を使う。一部がマッチしても、マッチしていない部分があるとNoneが返される。

s = '012-3456-7890'

print(re.fullmatch(r'\d{3}-\d{4}-\d{4}', s))
# <re.Match object; span=(0, 13), match='012-3456-7890'>

s = 'tel: 012-3456-7890'

print(re.fullmatch(r'\d{3}-\d{4}-\d{4}', s))
# None

ここで、\dは数字、{n}n回の繰り返しを表す。\dのように正規表現の特殊シーケンスではバックスラッシュ\が使われるので、''""の前にrを付けてエスケープシーケンスを無効化するraw文字列を使うと便利。

re.fullmatch()はPython3.4から追加された。それより前のバージョンでは上述の^$を使うとre.search()で同様の処理が可能。ここでは紹介していないが、re.match()$の組み合わせでもOK。

s = '012-3456-7890'

print(re.search(r'^\d{3}-\d{4}-\d{4}$', s))
# <re.Match object; span=(0, 13), match='012-3456-7890'>

s = 'tel: 012-3456-7890'

print(re.search('^\d{3}-\d{4}-\d{4}$', s))
# None

re.IGNORECASE

re.search()re.fullmatch()などの関数の引数flagsre.IGNORECASEを指定することで、大文字小文字を区別せずに比較できる。

s = 'ABC'

print(re.search('abc', s))
# None

print(re.search('abc', s, re.IGNORECASE))
# <re.Match object; span=(0, 3), match='ABC'>
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事