LINEのオープンチャットに投稿された質問に回答したので、ご紹介します。
今回は「Python初心者🔰」というオープンチャットに投稿されたものです。
この記事を書いている時点では218名の方が参加されています。
Python関連の中では3番目に参加人数の多いオープンチャットです。
質問
[‘タナカ[TANAKA]No1, ‘サトウ[SATO]NoA2’, ‘ナカムラ[NAKAMURA]No3’]
これを下記のようにカッコ内のみ抽出して、リスト型に整形したいのですがうまくできません💦
[‘TANAKA’, ‘SATO’, ‘NAKAMURA’]
回答
まずは回答したコードです。
import re
text_list = ['タナカ[TANAKA]No1', 'サトウ[SATO]NoA2', 'ナカムラ[NAKAMURA]No3']
pattern = '(?<=\[).*(?=\])'
text_list_re_search = [re.search(pattern, text).group() for text in text_list]
print(text_list_re_search)
上記の実行結果
['TANAKA', 'SATO', 'NAKAMURA']
re
モジュールは標準モジュールなので、pip等でインストールする必要はありません。
解説
今回の質問の要件はこんな感じ。
- 文字列から括弧の中身だけ抜き出す
- その文字列が複数あり、リスト形式になっている
文字列を特定の条件で抜き出したり、編集したい場合は正規表現を使うことが多いです。
今回も正規表現を使います。
これが1つ目のポイントです。
次に、その文字列が複数あり、リスト形式にまとめられているという点。
これはリスト内包表記を使うと簡単に書くことができます。
これが2つ目のポイントです。
では、順番に見ていきましょう。
正規表現
Python では正規表現のための機能が標準モジュールとして用意されています。
re
というモジュールです。
正規表現は英語で「Regular expressions」なので、re
という名前になっています。(たぶん)
前述しましたが、標準モジュールなので、インストールは不要です。
今回はre
モジュールのsearch()
メソッドを使用しました。
その名前の通り、条件に合う文字列を探すメソッドです。
search()
メソッドは引数に条件(パターン)と文字列を指定します。
条件(パターン)
今回使用した条件(パターン)は(?<=\[).*(?=\])
です。
何の情報もなく、これだけ見ると暗号ですね。。。
これを3つに分解します。(?<=\[)
と.*
と(?=\])
です。
まず最も簡単な.*
です。
Pythonの正規表現では.
は「任意の1文字」、*
は「0回以上の繰り返し」という意味です。
つまり、.*
は全ての文字列が該当しますし、空文字列も該当します。
次の1つ目の(?<=\[)
です。これは「後読み」とか「肯定の後読み」と言われるパターンで、この場合は「 [
の後ろの〜」という意味になります。
例えば、「『pre』の後ろ」と指定したい場合は、(?<=pre)
と書きます。
あれ?「 \ 」は?
って思いますよね。\
はエスケープシーケンスです。[
は正規表現の条件(パターン)の表記として意味を持っていきます。ただ、今回はただの文字列として [
を使いたいため、\[
と書く必要があります。
最後に(?=\])
です。これは「先読み」、「肯定の先読み」と言われるパターンで、この場合は「]
の前の〜」という意味になります。後読みの逆ですね。ここでもエスケープシーケンス\
を使っています。
ということで、3つ組み合わせると、「[
の後ろで、]
の前の、任意の文字列」という意味になります。
search()メソッド
search()
メソッドについては、返り値が特殊な型になっています。
次のコードで、search()
メソッドの返り値を確認してみます。
import re
sample_text = '1234abcde56789'
search_result = re.search('\D+', sample_text)
print(search_result)
print(type(search_result))
上記の実行結果
<re.Match object; span=(4, 9), match='abcde'>
<class 're.Match'>
search()
メソッドの返り値は「re.Match」型で、元も文字列のどの部分が条件(パターン)に一致したのか、一致した文字列、の情報が含まれています。
また、この「re.Match」型には一致した文字列を取得するgroup()
メソッドが用意されています。
import re
sample_text = '1234abcde56789'
search_result = re.search('\D+', sample_text)
print(search_result.group())
上記の実行結果
abcde
回答のコードではこれらをつなげてre.search(〜).group()
と書いています。
リスト内包表記
リスト内包表記の最もシンプルな書き方は、
[変数の処理 for 変数 in シーケンス]
という形式になります。
具体的な例をいくつか見てみます。
num_list = [i for i in range(1, 101)]
print(num_list)
上記の実行結果
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
num_list2 = [j**2 for j in range(1, 10)]
print(num_list2)
上記の実行結果
[1, 4, 9, 16, 25, 36, 49, 64, 81]
num_list = [i for i in range(1, 101)]
num_list3 = [k for k in num_list if k % 5 == 0]
print(num_list3)
上記の実行結果
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
どれも、リスト内包表記を使わなくても書くことはできますが、コードがシンプルになったり、内容によっては処理時間が短くなったりもします。
今回の回答も、リスト内包表記を使わないで書くと次のようになります。
import re
text_list = ['タナカ[TANAKA]No1', 'サトウ[SATO]NoA2', 'ナカムラ[NAKAMURA]No3']
pattern = '(?<=\[).*(?=\])'
text_list_re_search = []
for text in text_list:
re_search_result = re.search(pattern, text).group()
text_list_re_search.append(re_search_result)
print(text_list_re_search)
こっちのほうが見やすい、分かりやすい、という人もいるかもしれませんね。
Google Colaboratory
今回も、コードの動作は Google Colaboratory で確認しました。
掲載したコードは下記から確認できます。
環境構築の不要な Google が提供している Webサービスなので、Python を学習中の方にはオススメです。
おわりに
今回は、
文字列の括弧の中身だけ抜き出す方法
を紹介しました。
また、ポイントである、
- 正規表現(re.search)
- リスト内包表記
について簡単に解説をしました。
以上です。
コメント