Pythonで文字列のリスト(配列)の条件を満たす要素を抽出、置換

Modified: | Tags: Python, 文字列, リスト

文字列を要素とするリスト(配列)から、特定の条件を満たす文字列の要素のみを抽出したり、置換や変換などの処理をしたりして新たなリストを生成するには、リスト内包表記を使う。

文字列の抽出や置換についての詳細は以下の記事を参照。

なお、リストは異なる型のデータを格納可能で、厳密には配列とは異なる。配列を扱いたい場合はarray(標準ライブラリ)やNumPyを使う。

リスト内包表記

リストから新たなリストを生成する場合はリスト内包表記を使うとforループよりもシンプルに書ける。

[ for 任意の変数名 in イテラブルオブジェクト if 条件式]

要素を条件式で選択するだけであればで処理することはないので、

[変数名 for 変数名 in 元のリスト if 条件式]

という形になる。

if 条件式if not 条件式にすると否定になり、条件式を満たさない要素を抽出できる。

リスト内包表記を使ったリストの要素の抽出や置換・変換などについての詳細は以下の記事を参照。

特定の文字列を含む(部分一致) / 含まない: in

特定の文字列 in 元の文字列で、元の文字列特定の文字列が含まれるとTrueを返す。これを条件式とする。

inの否定はnot inを使う。

l = ['oneXXXaaa', 'twoXXXbbb', 'three999aaa', '000111222']

l_in = [s for s in l if 'XXX' in s]
print(l_in)
# ['oneXXXaaa', 'twoXXXbbb']

l_in_not = [s for s in l if 'XXX' not in s]
print(l_in_not)
# ['three999aaa', '000111222']

特定の文字列を置換

リストの要素の文字列を置換したい場合、リスト内包表記で各要素に文字列メソッドreplace()を使う。

置換対象の文字列がない場合はreplace()を適用しても変更しないので、if 条件式で要素を選択する必要はない。

l = ['oneXXXaaa', 'twoXXXbbb', 'three999aaa', '000111222']

l_replace = [s.replace('XXX', 'ZZZ') for s in l]
print(l_replace)
# ['oneZZZaaa', 'twoZZZbbb', 'three999aaa', '000111222']

特定の文字列を含む要素をまるごと置き換えたい場合はinで抽出して三項演算子で処理する。三項演算子は真の値 if 条件式 else 偽の値という形で書く。

リスト内包表記のの部分を三項演算子とすればOK。

l_replace_all = ['ZZZ' if 'XXX' in s else s for s in l]
print(l_replace_all)
# ['ZZZ', 'ZZZ', 'three999aaa', '000111222']

まとまりごとに括弧で囲むと以下のようになる。慣れないうちは括弧を使ったほうが理解しやすく間違いがないかもしれない。文法上は括弧を書いても問題はない。

[('ZZZ' if ('XXX' in s) else s) for s in l]

条件としてinを使うとリスト内包表記のinと紛らわしいが、リスト内包表記および三項演算子の構文の形を意識すれば難しくない。

特定の文字列で始まる / 始まらない: startswith()

文字列メソッドstartswith()は、文字列が引数に指定した文字列で始まるとTrueを返す。

l = ['oneXXXaaa', 'twoXXXbbb', 'three999aaa', '000111222']

l_start = [s for s in l if s.startswith('t')]
print(l_start)
# ['twoXXXbbb', 'three999aaa']

l_start_not = [s for s in l if not s.startswith('t')]
print(l_start_not)
# ['oneXXXaaa', '000111222']

特定の文字列で終わる / 終わらない: endswith()

文字列メソッドendswith()は、文字列が引数に指定した文字列で終わるとTrueを返す。

l = ['oneXXXaaa', 'twoXXXbbb', 'three999aaa', '000111222']

l_end = [s for s in l if s.endswith('aaa')]
print(l_end)
# ['oneXXXaaa', 'three999aaa']

l_end_not = [s for s in l if not s.endswith('aaa')]
print(l_end_not)
# ['twoXXXbbb', '000111222']

大文字・小文字で判定し抽出

文字列メソッドisupper(), islower()で文字列がすべて大文字か小文字かを判定できる。

メソッドの詳細は以下の記事を参照。

l = ['oneXXXaaa', 'twoXXXbbb', 'three999aaa', '000111222']

l_lower = [s for s in l if s.islower()]
print(l_lower)
# ['three999aaa']

大文字・小文字を変換

すべての文字を大文字や小文字に変換したい場合は、文字列メソッドupper()lower()を使う。そのほか、最初の一文字だけ大文字にするcapitalize()や、大文字と小文字を入れ替えるswapcase()などもある。

メソッドの詳細は以下の記事を参照。

上述の置換の例と同様に、条件を満たす要素のみ処理したい場合は三項演算子を使う。

l = ['oneXXXaaa', 'twoXXXbbb', 'three999aaa', '000111222']

l_upper_all = [s.upper() for s in l]
print(l_upper_all)
# ['ONEXXXAAA', 'TWOXXXBBB', 'THREE999AAA', '000111222']

l_lower_to_upper = [s.upper() if s.islower() else s for s in l]
print(l_lower_to_upper)
# ['oneXXXaaa', 'twoXXXbbb', 'THREE999AAA', '000111222']

英字か数字か判定し抽出

文字列メソッドisalpha()isnumeric()で、文字列がすべて英字か数字かなどを判定できる。詳細は以下の記事を参照。

l = ['oneXXXaaa', 'twoXXXbbb', 'three999aaa', '000111222']

l_isalpha = [s for s in l if s.isalpha()]
print(l_isalpha)
# ['oneXXXaaa', 'twoXXXbbb']

l_isnumeric = [s for s in l if s.isnumeric()]
print(l_isnumeric)
# ['000111222']

複数条件

リスト内包表記の条件式の部分は複数条件にすることもできる。複数の条件式をandorでつなげればOK。否定notも使える。

三つ以上の条件式を使う場合は順番によって結果が異なるので、まとまりごとに()で囲んでおいたほうが無難。

l = ['oneXXXaaa', 'twoXXXbbb', 'three999aaa', '000111222']

l_multi = [s for s in l if s.isalpha() and not s.startswith('t')]
print(l_multi)
# ['oneXXXaaa']

l_multi_or = [s for s in l if (s.isalpha() and not s.startswith('t')) or ('bbb' in s)]
print(l_multi_or)
# ['oneXXXaaa', 'twoXXXbbb']

正規表現

正規表現を使うと自由度の高い処理が可能。

re.match()がマッチしたときに返すmatchオブジェクトを条件式で評価すると常にTrueと判定される。マッチしない場合は条件式でFalseとなるNoneを返すので、正規表現にマッチする要素のみを抽出する場合は、これまでのようにリスト内包表記の条件式の部分にre.match()を適用すればOK。

import re

l = ['oneXXXaaa', 'twoXXXbbb', 'three999aaa', '000111222']

l_re_match = [s for s in l if re.match('.*XXX.*', s)]
print(l_re_match)
# ['oneXXXaaa', 'twoXXXbbb']

正規表現にマッチした部分を置換するre.sub()も便利。マッチした要素のみを抽出して置換する場合はif 条件式を追加すればOK。

l_re_sub_all = [re.sub('(.*)XXX(.*)', r'\2---\1', s) for s in l]
print(l_re_sub_all)
# ['aaa---one', 'bbb---two', 'three999aaa', '000111222']

l_re_sub = [re.sub('(.*)XXX(.*)', r'\2---\1', s) for s in l if re.match('.*XXX.*', s)]
print(l_re_sub)
# ['aaa---one', 'bbb---two']

関連カテゴリー

関連記事