Python初心者が理解しやすい、基本のキから解説するシリーズ。
今回はPythonにおける「文字列の切り取り」についてです。
位置の指定や、ある条件にマッチした文字列を切り取り(抽出して取得)すること。他の言語だとsubstring関数などが使われるテクニックです。
- 文字列の指定した開始位置~終了位置の部分を切り取り(取得し)たい
- 文字列の指定した開始位置~終了位置の部分を削除したい
- 文字列のある特徴にマッチした部分だけを抽出したい
- 文字列のある特徴にマッチした部分だけを削除したい
このような文字列の切り取りの方法について、現役のエンジニアが解説しています。
結論
文字列の開始位置、終了位置を指定して部分文字列を取得するには、スライスを使用します。
文字列[開始位置:終了位置+1]
text = "ABCDEF"
s = text[2:5]
print(s)
# CDE
指定範囲(文字列の中間)を削除する場合もスライスを取得したあとに、文字列の結合(+)をします。
text = "hoge fuga"
s = text[:2] + text[7:]
print(s)
# hoga
文字列のうち特徴にマッチした部分だけを抽出するには、正規表現 re モジュールで検索します。
import re
text = "[aaa]123-bbb"
m = re.search(r'\[(.+)\]', text)
if m is not None:
print(m.group(1))
# aaa
あらかじめ決められた文字列を削除する(取り除く)には、replace()で十分です。
text = "The quick brown fox jumps over the lazy dog"
s = text.replace('quick ', '')
print(s)
# The brown fox jumps over the lazy dog
より複雑なパターンにマッチした部分だけを削除する(取り除く)には、reモジュールのsub()を用います。
text = "[aaa]123-bbb,ccc"
s = re.sub(r'\[.+\]', '', text)
print(s)
# 123-bbb,ccc
実行環境
Python 3.9.2
文字列の指定位置・範囲を切り取り(取得)
ある文字列の特定の位置または範囲の部分文字列を切り取る(取得する)には、Pythonのスライス機能を使用します。
スライスは文字列やリストなどのオブジェクトの一部分を抜き出せる機能です。
スライスの使い方は開始位置start
と終了位置end
を指定するだけです。コロン(:)の後ろには、終了位置となる要素の1つ後の要素を指定することに注意しましょう。
部分文字列 = 文字列変数[start:end+1]
例えば、次のようなtext文字列で、冒頭の6文字を取り出すには次のようにします。
text = "Ctrl01 hogehoge"
s = text[0:6]
print(s)
# Ctrl01
1文字の場合は [x:x]
とするか、単に [x]
とします。
s = text[0]
print(s)
# C
位置にマイナス(-x)を指定すると、末尾(文字列の最後)から数えた位置を表せます。
例えば、最後の文字を取得するには -1
を指定します。
s = text[-1]
print(s)
# e
スライスでコロン(:)の前後を指定しない場合は、それぞれ先頭、末尾を表します。
例えば次のようなコードでは2文字目から末尾(最後)までとなります。
s = text[1:]
print(s)
# trl01 hogehoge
同じように、先頭を始点に、最後から3文字目を終点とする場合は次のようにします。
s = text[:-2]
print(s)
# Ctrl01 hogeho
なお、他の言語でいう配列とは異なり、範囲外の位置を指定してもエラーとなりません。
範囲外は単純に無視されます。
s = text[0:1000]
print(s)
# Ctrl01 hogehoge
文字列の指定位置・範囲の削除
では、指定した範囲の文字列を削除するにはどうしたらいいでしょうか?
これにもスライスを応用できます。
先頭(末尾)の指定範囲を削除することは、末尾(先頭)の指定範囲を切り抜く(取得する)ことと同じです。
text = "Ctrl01 hogehoge"
s = text[7:]
print(s)
# hogehoge
また、文字列の中間の指定範囲を削除するには、スライスを2回行って文字列の結合をすることで対応します。
text = "Ctrl01 hogehoge"
s = text[:4] + text[6:]
print(s)
# Ctrl hogehoge
文字列の特徴にマッチした部分だけを抽出
文字列の特定位置・範囲ではなく、ある特徴をもった部分(部分文字列)を抽出するには、正規表現を使用します。
Python では標準モジュールである re モジュールを使用します。
正規表現は最初はとっつきにくく、難しく感じられるかもしれません。ですが、一度身につけておくと、文字列操作の幅が格段に広がります。また、仕事でプログラミングする方にとっては必須ですので、避けて通れません。ここは一踏ん張りです!
どのように抽出するか(例)
正規表現で文字列の操作をするには re モジュールを使用します。
reモジュールは標準ライブラリですが import は必要です。
以下は [] 内の文字列を抽出するコードの例。
import re
text = "[aaa]123-bbb"
m = re.search(r'\[(.+)\]', text)
if m is not None:
print(m.group(1))
# aaa
re.search() は文字列stringの中から正規表現パターンpatternにマッチする部分を検索します。ただし、マッチする部分が複数あっても、最初にマッチした部分だけを取得します。
match = re.search(pattern, string)
これ以外のコード部分の意味について、少しずつ確認していきましょう。
複数の文字にマッチする ワイルドカード
re.findall() は正規表現のパターンpatternにマッチした全ての部分文字列をリストで返します。簡略化のため以下のサンプルではre.findall()を使います。
list_match = re.findall(pattern, string)
次のような文字列textがあるとき、ハイフン"-" に続く文字列を取得するコード。
text = "[aaa]123-bbb,ccc"
matched = re.findall(r'-.+', text)
print(matched)
# ['-bbb,ccc']
print(matched[0])
# -bbb,ccc
"-" に続く文字列を取得するには正規表現では"-.+"というパターンになります。
.
は任意の一文字、+
は直前の文字の1文字以上の繰り返しを表します。この組み合わせで「任意の1文字以上の文字列」という意味です。
1文字以上ではなく、0文字以上の繰り返しと指定するには *
を用います。
Pythonの正規表現では、マッチする最も長い文字列を優先して返します。そのため、*
でも同じ結果となるのが確認できます。(結果が'-'ではなく、'-bbb,ccc'となる)
matched = re.findall(r'-.*', text)
print(matched)
# ['-bbb,ccc']
また、0文字または1文字を指定するには ?
を用います。
matched = re.findall(r'-.?', text)
print(matched)
# ['-b']
特定の文字種にマッチする
正規表現で特定の文字種を指定するには []
で囲みます。
例えば、以下のように表現できます。
任意のアルファベット | [a-zA-Z] |
任意のひらがな | [あ-を] |
任意の数字 | [0-9] または \d |
これらは組み合わせて表現もできます(例. [a-zA-Z0-9])
matched = re.findall(r'-[a-zA-Z]+', text)
print(matched)
# ['-bbb']
matched = re.findall(r'\d+', text)
print(matched)
# ['123']
matched = re.findall(r'[0-9]+', text)
print(matched)
# ['123']
指定範囲をグループ化
上のサンプルコードだと "-" が残ってしまっているので、これを含めないで抽出してみましょう。
ある特徴の一部を選択的に抽出するには、グループ化(グルーピング)というテクニックを使います。
指定する正規表現patternの一部を ()
で囲むとその部分を選択して抽出できます。
例えば、同じtextを例にすると
matched = re.findall(r'-([a-zA-Z]+)', text)
print(matched)
# ['bbb']
これで本当に切り取りたい部分文字列を取得できました。
正規表現をチェックする
以下のようなツールを使うと、正規表現が正しいかどうかをかんたんに確認できます。
正規表現チェッカー PHP: preg_match() / JavaScript: match()
re.match(), re.search(), re.findall()
re.findall() はマッチしたすべての部分文字列をリスト(list)にして返しますが、最初のサンプルコードで使用した re.search() は文字列のうちマッチした部分(1回のみ)を返すという違いがあります。
reモジュールの関数の違いは次のとおりです。
関数 | 機能 | 戻り値 |
---|---|---|
re.match() | 文字列の先頭でマッチするか検索(1回のみ) | Match オブジェクト |
re.search() | 文字列のどこかでマッチするか検索(1回のみ) | Match オブジェクト |
re.findall() | 文字列でマッチするすべての部分を返す | list オブジェクト |
Matchオブジェクトから、抽出された文字列を取り出すにはMatch.group()やMatch.groups()を使います。
pattern = r'\[(.+)\]'
text = "xxx[aaa]123-bbb,ccc"
m = re.match(pattern, text)
print(m)
# None
m = re.search(pattern, text)
if m is not None:
print(m.group(1))
# aaa
m = re.findall(pattern, text)
print(m)
# ['aaa']
文字列の特徴にマッチした部分を削除
ある特徴にマッチする部分文字列を削除するにはどうしたらいいでしょうか?
あらかじめ決められた特定の文字列(不要テキスト)を削除するには組み込み関数の replace() を使えば十分です。
text = "The quick brown fox jumps over the lazy dog"
s = text.replace('quick ', '')
print(s)
# The brown fox jumps over the lazy dog
replace() はこちらでも詳しく解説しています。
特定の文字列ではなく、ある特徴(パターン)にマッチする部分だけを削除するには re.sub() を利用します。
s = re.sub(pattern, repl, string)
pattern 正規表現のパターン
repl 置換文字列(削除なら空文字'')
string 対象文字列
text = "[aaa]123-bbb,ccc"
s = re.sub(r'\[.+\]', '', text)
print(s)
# 123-bbb,ccc
Pythonの学習法について
Python の勉強が辛くなっていませんか?
Pythonは比較的取り組みやすい言語と言われていますが、プログラミング初心者にとっては分からないことだらけ。
ゼロから独学で勉強するのは厳しい道のりです。
今回、様々な現場、システム、言語を経験してきた現役エンジニアの立場から、初心者でも挫折しない学習方法を解説する記事を書きました。もちろん、お金をかけずに習得できる方法も解説しています。
できるだけストレスがかからない勉強法を解説しているので、ぜひ参考にしてみてくださいね。