Pythonにおける文字列操作について

この記事を書いた人
北爪 聖也

株式会社pipon代表取締役。 キャリアはADK(広告代理店)でテレビ広告運用をして残業120時間するが、ネット広告では自分の業務がAIで自動化されていることに驚愕する。そこで、機械学習受託会社に転職し、技術力を身につけた後、piponを創業。現在、製薬業界、大手監査法人、EC業界、様々な業界でAI受託開発事業を運営。

公開日:2019/9/23
更新日:2019/9/23
キーワード:Python 文字列操作
文字数:9200(読み終わるまでおよそ15分)

この記事でわかること

  • 文字列の特徴
  • Pythonによる文字列操作の方法

はじめに

今回は、Pythonによる文字列操作について見ていきます。

文字列とは、文字の集まりのことです。文字の列、そのままの意味です。

例えば、”tools”という単語は、 5 つの文字が連なっているので文字列です。連続しているものを「シーケンス」と言うので、文字列を文字シーケンスとも呼びます。

そして、文も文字列に相当します。例えば “Let’s go to the park.” のような文も、文字列(シーケンス)とみなします。

1. 文字列の特徴

studiographicさんによる写真ACからの写真

文字列の大きな特徴は、イミュータブル(immutable)と呼ばれる属性のオブジェクトであることです。

イミュータブルはプログラミングでよく出てくる言葉で、変更不可能を意味します。つまり、部分的に書き換えることができないということです。

なお、変更可能なオブジェクトをミュータブルと言います。

Pythonでは、文字列やタプルはイミュータブル、リストはミュータブルなオブジェクトに分類されます。

2. 文字列操作の具体的なやり方

acworksさんによる写真ACからの写真

それでは、文字列の様々な操作方法を見ていきます。

1) 文字列の連結

文字列の連結には、+演算子かjoinメソッドを使います。

+演算子を使うと、文字列を足し算するように、2つの文字列を連結することができます。例えば、”伊藤”と”先生”で”伊藤先生”の文字列を作ることができます(例文3-1)。

<例文1-1>

name = "伊藤" + "先生"
print(name)
伊藤先生

+演算子を用いた連結は、文字列を足し合わせるイメージが直感的に分かりやすいと思います。

余談ですが、数値と文字列を連結する場合、単純に+演算子を用いるとエラーになってしまうので、注意してください(例文1-2)。

<例文1-2>

hour = 4 + "時間"
TypeError                                 Traceback (most recent call last)
<ipython-input-3-f2005f047a44> in <module>()
----> 1 hour = 4 + "時間"

TypeError: unsupported operand type(s) for +: 'int' and 'str'

数値と文字列を連結したい場合は、数値を文字列に変換してから連結する必要があります(例文1-3)。

<例文1-3>

hour = str(4) + "時間"
print(hour)
4時間

数値と文字列を連結したいときは、注意してください。

さて、文字列の連結はjoinメソッドを使うこともできます。

joinメソッドは、イテラブルなオブジェクトに対して適用できます。イテラブルとは、その中のデータを順番に取り出すことができるオブジェクトのことで、リストやタプル、辞書、集合が該当します。

joinメソッドを使って、リストの要素をセパレータ(空白やカンマなど)で連結した文字列を作るには、次の書式のように書きます。

セパレータ.join(リスト)

実際の構文の例を、例文1-4に示します。

<例文1-4>

member = ["伊藤さん", "吉田さん", "佐藤さん"]
group = " と ".join(member)
print(group)
伊藤さん と 吉田さん と 佐藤さん

セパレータが不要であれば、セパレータを””とすれば、セパレータなしで連結することができます(例文1-5)。

<例文1-5>

member = ["伊藤さん", "吉田さん", "佐藤さん"]
group = "".join(member)
print(group)
伊藤さん吉田さん佐藤さん

2) 文字列の分割

splitメソッドを使えば、文字列の分割が可能です

セパレータで区切られている文字列を、splitメソッドでリストに分割するには、次の書式のようになります。

文字列.split(セパレータ)

例文2-1では、空白で区切られた単語をリストにしています。空白がセパレータとなっているので、split()の引数は省略しています(デフォルトのセパレータは空白のため)。

<例文2-1>

memo = "I have a pen ."
word = memo.split()
print(word)
['I', 'have', 'a', 'pen', '.']

単語がカンマで区切られていれば、セパレータに”,”を指定します(例文2-2)。

<例文2-2>

bungu = "pen, notebook, eraser, stapler"
bungu_list = bungu.split(",")
print(bungu_list)
['pen', ' notebook', ' eraser', ' stapler']

文字列の分割はできましたが、単語の頭に空白が残っているのが気になります。空白が入らないようにするには、カンマの後に空白を入れて、”, “でセパレータを指定する必要があります(例文2-3)。

<例文2-3>

bungu = "pen, notebook, eraser, stapler"
bungu_list = bungu.split(", ")
print(bungu_list)
['pen', 'notebook', 'eraser', 'stapler']

3) 文字列の取り出し

文字列から文字を取り出すには、[ ]を使います。[ ]には、取り出す文字の位置を指定しますが、1文字目を0とするのに注意が必要です。位置にマイナスをつけると、後ろから数えることを示し、-1が最後の文字となります。

文字の位置を示す数字をインデックス番号と呼びます。

位置を0から数えるのが、プログラミングに馴染みがない方には間違えやすいところですが、色々な場面で出てきますので、必ず慣れておいてください。

文字列[文字位置]

例文3-1では、文字列の4文字目と後ろから2番目の文字を取り出しています。文字位置に3を指定すると先頭から4文字目、-2を指定すると最後から2文字目を取り出します。

<例文3-1>

moji = "QW123ERTY"

print(moji[3])

print(moji[-2])
2
T

ところで2項で、文字列はイミュータブルなオブジェクトであると述べました。

つまり、文字を取り出すことはできても、書き換えることはできないということです。試しに3文字目を”X”に書き換えようとすると、エラーになります(例文3-2)。

<例文3-2>

moji = "QW123ERTY"

moji[2] = "X"
TypeError                                 Traceback (most recent call last)
<ipython-input-9-a677cd14a489> in <module>()
      1 moji = "QW123ERTY"
      2 
----> 3 moji[2] = "X"

TypeError: 'str' object does not support item assignment

文字位置に範囲を指定することで、部分文字列(文字列の一部)を取り出すことができます。この操作を、スライスと呼びます。

取り出す範囲は、開始位置と終了位置で指定します。

文字列[開始位置 : 終了位置]

スライスは非常に重要な操作なので、しっかり覚えておいてください。

スライスの例をいくつか例文3-3に示します。

<例文3-3>

moji = "QW123ERTY"

#全部を取り出す
print(moji[:])

#4文字目から最後まで
print(moji[3:])

#3文字目から2文字
print(moji[2:2+2])

#先頭から、後ろから数えて2文字目の手前まで
print(moji[:-2])
QW123ERTY
23ERTY
12
QW123ER

文字位置に、ステップのオプションをつけることもできます。ステップとは1文字おき、2文字おきのように、飛び飛びで文字を取り出したいときに便利なオプションです。

文字列[開始位置 : 終了位置 : ステップ]

例文3-4ではステップを2にしているので、1文字目、3文字目、5文字目のように文字を取り出します。

<例文3-4>

moji = "QW123ERTY"

print(moji[::2])
Q13RY

ステップをマイナスの値で指定すると、文字を後ろから取り出すことができます(例文3-5)。

<例文3-5>

moji = "QW123ERTY"

print(moji[::-2])
YR31Q

開始位置と終了位置を省略してステップに-1を指定すると、逆順の文字列を作ることができます(例文3-6)。

<例文3-6>

moji = "QW123ERTY"

print(moji[::-1])
YTRE321WQ

4) sprintfスタイル(%演算子)による文字列の埋め込み

Pythonには、%演算子という演算子があります。%演算子は剰余演算子として用いられるのが一般的ですが、文字列に対して用いるフォーマット演算子として用いる方法もあります。

Python3には、文字列操作用に5)で述べるformatメソッドが準備されており、%演算子を用いた文字列の埋め込みは推奨されていません。

しかし、以前は%演算子を用いていたので、古いコードには%演算子が用いられていることがあるので、ここで簡単に使い方を紹介します(例文4-1)。なお、%sは文字列を意味します。

<例文4-1>

#文字列を挿入したい箇所に%sを入れ、後ろで入れたい文字列を指定します。
print("これは、私の%sです。" % "犬と猫")

#文字列を複数挿入したいときは、挿入したい箇所に%sを入れ、後ろで入れたい文字列を順番に指定します。
print("私は、%s %sを一生懸命に勉強しています。" % ('Python', '3'))

#%sに引数を指定することで、記述の順番を気にする必要がなくなります。
print("私は、%(lang)s %(ver)sが大好きです。" % {'lang': 'Python', 'ver': '3'})
これは、私の犬と猫です。
私は、Python 3を一生懸命に勉強しています。
私は、Python 3が大好きです。

5) formatメソッドを使った文字列の埋め込み

Pythonでは、formatメソッドを使うことで、文字列の中に手軽に数値や文字列を埋め込むことができます。

そして、Python3.6からは、format()相当のf-strings(fプリフィックス)を使うことで、かなりシンプルに文字列への埋め込みができるようになりました。

formatメソッドを使った例を見てみましょう。

文字列内に埋め込みたい箇所を{ }で指定し、埋め込みたい値をformat()の引数にすると、その値が順番に{ }の位置に挿入されます(例文5-1)。

<例文5-1>

hobby = "私の趣味は、{}と{}と{}です。"
hobby.format("登山", "ドライブ", "お酒")
'私の趣味は、登山とドライブとお酒です。'

数値を埋め込みたい場合、わざわざ数値を文字列に変換する必要はありません(例文5-2)。

<例文5-2>

daten = 59
name = "平田"
hr = 9
daritu = .311

k = "{}選手の打率は{}、打点は{}、ホームランは{}本です。"
kekka = k.format(name, daritu, daten, hr)
print(kekka)
平田選手の打率は0.311、打点は59、ホームランは9本です。

キーワード引数を使って指定する方法もあります(例文5-3)。

<例文5-3>

k = "{name}選手の打率は{daritu}、打点は{daten}、ホームランは{hr}本です。"
kekka = k.format(daten = 59, name = "平田", hr = 9, daritu = .311)
print(kekka)
平田選手の打率は0.311、打点は59、ホームランは9本です。

例文5-2に比べると、シンプルに書けていることが分かります。

formatメソッドの代わりに、f-stringsを使った埋め込み方も見ておきましょう。f-stringsを使うと、例文5-2を例文5-3のように書き換えることができます。

<例文5-3>

daten = 59
name = "平田"
hr = 9
daritu = .311

kekka = f"{name}選手の打率は{daritu}、打点は{daten}、ホームランは{hr}本です。"
print(kekka)
平田選手の打率は0.311、打点は59、ホームランは9本です。

fを付けた文字列に、変数を埋め込むことができました。

f”{値}”を使った表現も目にすることが多いと思うので、どちらの書き方も知っておくとよいです。

6) 文字列の置換

文字列を置換するには、replaceメソッドを使います。オプションに置換したい個数(回数)を指定することができます。

個数を指定しない場合、検索文字列をすべて置換します。

replace(検索文字列, 置換文字列, 個数)

例文6-1では、”a”を”x”に置換しています。3個すべての”a”を”x”に置換した、新しい文字列を作ります。

<例文6-1>

moji = "abcabcabc"
moji.replace("a", "x")
'xbcxbcxbc'

個数を指定すると先頭から順番に置換を行い、個数以上の置換は行いません(例文6-2)。

<例文6-2>

moji = "abcabcabc"
moji.replace("a", "x", 2)
'xbcxbcabc'

初めの2個の”a”のみが”x”に置換され、3個目の”a”は置換されていないことが分かります。

7) 正規表現を用いた文字列の置換

文章の中からあるパターンに従って特定の文字列を置換したい場合、正規表現を使うと簡単に置換できます。

正規表現とは一言で言うと、「様々な文字列を一つの文字列で表現する表記法」です。

ここでは、reモジュールのsub関数を使った文字列の置換方法を見ていきます。

re.sub(正規表現, “置換する文字列”, 置換対象の文字列

以下のような郵便番号のデータを、アスタリスク(*)に置換する例を見てみましょう。

それぞれのデータは、郵便番号の後に地名が載っているとします。

140-0014 東京都品川区大井
150-0013 東京都渋谷区恵比寿
157-0066 東京都世田谷区成城

このデータに対してsub関数を使い、郵便番号の部分をアスタリスクに置換してみましょう(例文7-1)。

<例文7-1>

import re

# """は複数行の文字列を表します

yubin = """\
140-0014 東京都品川区大井
150-0013 東京都渋谷区恵比寿
157-0066 東京都世田谷区成城
"""
yubin2 = re.sub('^[0-9]{3}-[0-9]{4}',"***-****",yubin) # 正規表現を使った置換
print (yubin2)
***-**** 東京都品川区大井
150-0013 東京都渋谷区恵比寿
157-0066 東京都世田谷区成城

1行目の郵便番号だけが置換されて、2行目以降は変わっていません。

これは正規表現で「^」(文字列の先頭)を指定しているためです。

改行された2行目以降は、文字列の先頭とは見なされないため、置換されることはありません。

正規表現を用いることで、パターンさえ合っていればどのような郵便番号が入っていても、置換することができるため、一般性の高いコードを書くことができます。

8) 文字列の検索

文字列を検索して、指定した文字が見つかった最初のインデックス番号を返してくれるのが、findメソッドです。スペースも1文字としてカウントします。

find(文字列, 開始位置, 終了位置)

指定した文字列が見つからなかったときは、-1を返します(例文8-1)。

<例文8-1>

word = "downtown people"

a = word.find("o")
b = word.find("a")

print(a)
print(b)
1
-1

“o”は3個ありますが、最初に見つけた位置である”1″を返します(0から数えはじめるのに注意)。

“a”は文字列に含まれないため、”-1″を返します。

なお、rfindメソッドを使うと、最後に見つけた位置を返します(例文8-2)。

<例文8-2>

word = "downtown people"

word.rfind("o")
11

countメソッドを使うと、文字列を検索して、引数で指定した文字列が何個含まれているかを返します(例文8-3)。

count(文字列)

<例文8-3>

moji = "apple, lemon, melon"

moji.count("l")
3

また、次のように検索範囲を指定して数えることもできます(例文8-4)。検索範囲は、開始位置から終了位置の手前までで、終了位置は含まれないことに注意が必要です。

終了位置を省略すると、文字列の最後までが検索範囲となります。

count(文字列, 開始位置, 終了位置)

<例文8-4>

moji = "apple, lemon, melon"

moji.count("l", 0, 7)
1

9) 1文字ずつの取り出し

文字列は要素を1個ずつ取り出せるイテレータなので、for文を使った1文字ずつの処理が可能です(例文9-1)。

<例文9-1>

for moji in 'abcdef':
    
    print(moji)
a
b
c
d
e
f

文字列はイミュータブルなオブジェクトなので、書き換えはできませんが、このように1文字ずつ取り出すことはできます。

10) 文字列の削除

文字列の先頭と末尾にある余分な文字を除きたいとき、stripメソッドが役に立ちます(例文10-1)。

<例文10-1>

a = "  おはよう  \n"

a. strip()
'おはよう'

stripメソッドの引数に何も指定しないと、文字列の前後の空白と改行コードを除去しますが、引数に文字を指定すると、その文字を削除することができます(例文10-2)。

<例文10-2>

a = "おはようございます"

a. strip("ございます")
'おはよう'

stripメソッドは、文字列の先頭と末尾の余分な文字を削除するメソッドですが、先頭のみの余分な文字を除きたいときはlstripメソッド、末尾の余分な文字を除きたいときはrstripメソッドが使えます。

例文10-3では、“おはよう”の前後に2文字ずつ空白がありますが、rstripを使うと末尾、lstripを使うと先頭の空白のみを削除します。

<例文10-3>

a = "  おはよう  "

right = a. rstrip()
left = a. lstrip()

print(right)
print(left)
  おはよう
おはよう 

11) 比較演算子による文字列の比較

aとbの文字列が同じかどうかを調べるときは、比較演算子を使うと間違いがありません。

文字列aと文字列bが同じかどうかを比較したいときは、==演算子を使います。比較結果は理論値で示され、比較結果が同じであればTrue、違っていればFalseを返します(例文11-1)。

<例文11-1>

a = "私は、山登りと水泳とドライブが好きです。"
b = "私は、山登りと水泳とドライブが好きです。"
c = "私は、山登りと氷泳とドライブが好きです。"

print(a==b)
print(a==c)
True
False

同じかどうかの比較にはis演算子を使うこともあります。しかし、is演算子は値が同じかどうかを比較するのではなく、オブジェクトが同じかどうかを比較する演算子なので、注意が必要です。

例文11-1のaとbをis演算子で比較すると、どうなるか見てみましょう(例文11-2)。

<例文11-2>

a = "私は、山登りと水泳とドライブが好きです。"
b = "私は、山登りと水泳とドライブが好きです。"

a is b
False

aとbは全く同じ文字列で、==演算子で比較するとTrueが返ってきましたが、is演算子で比較するとFalseが返ってきます。

aの文字列の一部を変えても、bの文字列が変わることはないので、aとbは異なるオブジェクトということになります。従って、Falseが返されたのです。

例文11-3のように書くと、Trueが返ってきます。

<例文11-3>

a = "私は、山登りと水泳とドライブが好きです。"
b = a

a is b
True

aの文字列の一部を変えると、bも同じ文字列となります。よって、aとbは同じオブジェクトなので、is演算子で比較するとTrueが返ってきたのです。

==演算子は値が同じかどうか、is演算子はオブジェクトが同じかどうか、を比較する演算子であることを、覚えておいてください。

12) 大文字小文字の変換

半角の英字は、大文字と小文字の相互変換が可能です。upperメソッドはすべてを大文字にし、lowerメソッドはすべてを小文字に変換します(例文12-1)。

<例文12-1>

a = "I went to Tokyo station."

a.upper()
a.lower()
'I WENT TO TOKYO STATION.'
'i went to tokyo station.'

その他に、大文字と小文字を入れ替えるswapcaseメソッド、文字列の1文字目だけを大文字にし、以降をすべて小文字にするcapitalizeメソッド、各単語の1文字目を大文字にするtitleメソッドなどがあります(例文12-2)。

<例文12-2>

a = "I went to Tokyo station."

irekae = a. swapcase()
omoji = a. capitalize()
title = a. title()

print(irekae)
print(omoji)
print(title)
i WENT TO tOKYO STATION.
I went to tokyo station.
I Went To Tokyo Station.

3. おわりに

今回は、Pythonによる文字列の操作方法の代表的なものを紹介しました。

機械学習を行う際はデータの前処理が必須なので、文字列の操作が必要になることがよくあります。

機械学習の世界に踏み込もうとしている方は、機械学習のアルゴリズムだけでなく、文字列の操作方法もマスターすることをおすすめします。

4. 参考サイト

Python文字列操作マスター
https://qiita.com/tomotaka_ito/items/594ee1396cf982ba9887

Pythonの%演算子による文字列フォーマット
https://qiita.com/takahiro_itazuri/items/e585b46d096036bc837f

Python公式ドキュメント re — 正規表現操作
https://docs.python.org/ja/3/library/re.html

正規表現による文字列置換をマスターしよう
https://www.sejuku.net/blog/23421

piponではエンジニアの皆様に業務委託や副業でAI・データサイエンスの案件をご紹介しています!

piponの案件にご興味がある方は以下のフォームにご登録ください。案件をご案内します。

https://share.hsforms.com/1qk0uPA_lSu-nUFIvih16CQegfgt