Pythonでもっとも頻繁に使うデータ型のひとつが文字列(str)である。今回は「入門Python3」第5章・第12章の内容をベースに、文字列の作り方から便利なメソッド、そして現代的なフォーマット方法であるf文字列まで、基礎からしっかり解説していく。
文字列とは何か
Pythonの文字列はUnicode文字を並べたもの、つまり、Unicode文字のシーケンスである。例えば次のようなものである。
"Hello!"
文字列はイミュータブルなので、一度作成した文字列を後から変更することはできない。
文字列の作り方
文字列の作り方には、次のようにいくつかの種類がある。
| 書き方 | 例 | 説明 |
|---|---|---|
| シングルクォート | 'Snap' |
基本の文字列リテラル |
| ダブルクォート | "Crackle" |
シングルクォートと同じ扱い。文字列内にシングルクォートを含めやすい |
| トリプルクォート | '''...''' / """...""" |
改行を含む複数行文字列を作れる |
| プレフィックスなし | 'text' |
\nなどのエスケープシーケンスが解釈される |
r / R(raw文字列) |
r'\d{4}-\d{2}-\d{2}' |
エスケープシーケンスを解釈せず、バックスラッシュをそのまま残す(正規表現などで使用) |
f / F(f-string) |
f'{name}さん' |
{}内の式を実行時に評価して埋め込む(Python 3.6〜) |
rf / fr(raw + f-string) |
rf'C:\Users\{name}' |
rawとf-stringの組み合わせ |
t / T(t-string) |
t'{name}さん' |
f-stringと同じ構文だが、結果はstrではなくstring.templatelib.Templateオブジェクト(Python 3.14で追加) |
rt / tr(raw + t-string) |
rt'...' |
rawとt-stringの組み合わせ |
クォートによる作成
Pythonの最も基本的な文字列リテラルは、文字列をシングルクォート(')かダブルクォート(")で囲む。どちらを使ってもPythonの扱いは同じである。
>>> 'Snap'
'Snap'
>>> "Crackle"
'Crackle'
>>> ""
''
最後の、1文字も含まないものも、完全に有効な文字列であり、空文字列と呼ばれる。
対話型インタプリタは、それらが文字列であるとわかるようにシングルクォートで囲って表示するが、クォートは文字列の一部ではない。文字列だけを表示したければprint()関数を使えば良い。print()は、値を標準出力(通常は画面)に表示するための組み込み関数である。
>>> x = "Hello!"
>>> x
'Hello!'
>>> print(x)
Hello!
2種類のクォートが用意されているのは、クォート文字そのものを文字列に含めやすくするためである。文字列をダブルクォートで囲めば、中にシングルクォートを入れられる(逆も同様)。
>>> "'Nay!' said the naysayer."
"'Nay!' said the naysayer."
>>> 'She said, "Hello!"'
'She said, "Hello!"'
エスケープシーケンス
キーから入力が難しい制御文字や特別な意味を持つ文字を表すエスケープシーケンスが用意されている。
| エスケープシーケンス | 意味 |
|---|---|
\\ |
バックスラッシュ(\) |
\' |
シングルクォート(') |
\" |
ダブルクォート(") |
\a |
ベル(BEL) |
\b |
バックスペース(BS) |
\f |
フォームフィード(FF) |
\n |
改行(LF) |
\r |
復帰(CR) |
\t |
水平タブ(TAB) |
\v |
垂直タブ(VT) |
\ooo |
8進数で指定した文字(例:\0はヌル文字) |
\xhh |
16進数で指定した文字 |
\N{name} |
指定した名前のUnicode文字 |
\uxxxx |
4桁16進数のUnicode文字 |
\Uxxxxxxxx |
8桁16進数のUnicode文字 |
エスケープシーケンスはバックスラッシュ(\)で始まる。バックスラッシュと後続の文字の組み合わせで1文字を表す。
シングルクォートやダブルクォートの文字列リテラルの中では改行することはできない。改行しようとするとSyntaxErrorが発生する。
>>> "one
File "<python-input-6>", line 1
"one
^
SyntaxError: unterminated string literal (detected at line 1)
文字列の中で改行したい場合には改行を表す\nを使う。
>>> message = "one\ntwo\nthree"
>>> print(message)
one
two
three
対話型インタプリタはエスケープシーケンスで表示する。
>>> message
'one\ntwo\nthree'
シングルクォートやダブルクォートで囲った文字列の中で、同じ文字を使いたい場合、\'や\"を使う。
>>> '\'Nay!\' said the naysayer.'
"'Nay!' said the naysayer."
>>> "She said, \"Hello! \""
'She said, "Hello! "'
タブ文字(\t)もよく使われる。
>>> print('a\tb\tc')
a b c
バックスラッシュは次の文字をエスケープするという特別な意味を持つ。そのため、バックスラッシュ自身を使いたい場合、その特別な意味をエスケープするために、バックスラッシュを2つ重ねる(\\)。
>>> path = 'C:\\Users\\taro'
>>> print(path)
C:\Users\taro
raw文字列
先頭にr(またはR)を付けた文字列はraw文字列と呼ばれ、エスケープシーケンスをエスケープとして解釈させたくないときに便利である(正規表現でよく使われる)。
>>> path = r'C:\Users\taro'
>>> print(path)
C:\Users\taro
raw文字列は正規表現でとても役に立つ。raw文字列を使わない場合は、ただでさえ読みにくい正規表現がさらに酷いものになる。raw文字列を使えばいくらかマシになる。
>>> import re
>>> re.search(r'\d+\.\d+\.\d+\.\d+', 'IP: 192.168.1.1')
<re.Match object; span=(4, 16), match='192.168.1.1'>
raw文字列は「エスケープシーケンスへの変換をしない」だけで、バックスラッシュが完全に無効化されるわけではない。実際には、バックスラッシュには「直後のクォート文字を文字列の終端と認識させない」という字句解析上の働きが残っている。
このため、raw文字列をバックスラッシュ1つだけで終わらせようとすると、SyntaxErrorになる。
>>> r'C:\Users\taro\'
File "<python-input-0>", line 1
r'C:\Users\taro\'
^
SyntaxError: unterminated string literal (detected at line 1)
末尾の\'がエスケープシーケンスとして解釈され、「文字列が閉じていない」と判断されてしまうのである。クォートの前にバックスラッシュを置いた場合、その2文字自体は文字列の内容としてそのまま残る。
>>> r'\''
"\\'"
>>> print(r'\'')
\'
Python公式ドキュメントにも、次のように説明されている。
raw リテラルでは、引き続く文字をバックスラッシュでエスケープすることはできますが、バックスラッシュ自身はリテラルの中に残ります。
実務上は、「パスの末尾がバックスラッシュで終わる」文字列をraw文字列で書けない点が地味にはまりやすい。その場合は、末尾だけ通常のエスケープで書くか、文字列連結で回避する必要がある。
>>> path = r'C:\Users\taro' + '\\'
>>> print(path)
C:\Users\taro\
文字列リテラルは並べて書くだけで連結できるので、次のように書くこともできる。
>>> path = r'C:\Users\taro' '\\'
>>> print(path)
C:\Users\taro\
複数行の文字列リテラル
複数行にまたがる文字列は、エスケープシーケンス(\n)を使っても書けるが、トリプルクォート('''または""")を使うともっと簡単に書ける。
>>> message = "Hello.\nMy name is Taro.\nNice to meet you."
>>> print(message)
Hello.
My name is Taro.
Nice to meet you.
これをトリプルクォートで書くと、改行をそのまま入力するだけでよく、\nを意識する必要がなくなる。
>>> message = """Hello.
... My name is Taro.
... Nice to meet you."""
>>> print(message)
Hello.
My name is Taro.
Nice to meet you.
出力結果は完全に同じだが、トリプルクォートのほうが見た目と実際の改行位置が一致するため、特に長い文章を書くときに直感的で読みやすい。
str()による文字列への変換
str()関数を使うと、他のデータ型を文字列に変換できる。
>>> str(98.6)
'98.6'
>>> str(True)
'True'
Pythonは、print()にオブジェクトを渡すときなど、内部的にこのstr()を自動的に呼び出している。
文字列の連結と繰り返し
文字列は+演算子で連結できる。
>>> a = 'Duck.'
>>> c = 'Grey Duck!'
>>> a + ' ' + c
'Duck. Grey Duck!'
*演算子を使うと、文字列を指定回数繰り返せる。
>>> 'Na ' * 4
'Na Na Na Na '
インデックス参照とスライス
[]による文字の抽出
文字列やリストなどのシーケンスは、並んでいる要素の一つをインデックスで参照できる。先頭の要素のインデックスは0で、1、2、…というように一つずつ増えていく。
文字列の後ろに角括弧[]でインデックスを指定すると、その位置の文字を取り出せる。末尾から数えるインデックスを指定することもできる。その場合、最も右側のインデックスは-1になる。
>>> numbers = "0123456789"
>>> numbers[0]
'0'
>>> numbers[1]
'1'
>>> numbers[-1]
'9'
>>> numbers[-2]
'8'
>>> numbers[5]
'5'
先ほど述べた通り文字列はイミュータブルなので、インデックスを指定して文字を直接書き換えることはできない。
>>> name = 'Henny'
>>> name[0] = 'P'
TypeError: 'str' object does not support item assignment
代わりにreplace()メソッドやスライスを組み合わせて、新しい文字列を作る必要がある。
>>> name = 'Henny'
>>> name.replace('H', 'P')
'Penny'
スライスによる部分文字列の取得
スライスを使うと、文字列から部分文字列を取り出せる。[start:end:step]という形式で、文字列に続けて角括弧内に指定する。startからendの1つ手前までの文字が対象になる点に注意が必要である。
[start:end:step]
startに開始位置のインデックス、endに終了位置のインデックス+1、stepには間隔を指定する。endの位置の文字は含まれないことに注意しよう。
start・end・stepはそれぞれ省略することができる。stepを省略するときは、その前のコロンも省略できる。
[start:end]はstartからend-1までの文字列を抽出[start:]はstartから末尾までの文字列を抽出[:end]は先頭からend-1までの文字列を抽出[:]は先頭から末尾までの文字列全体を抽出
>>> numbers[2:5]
'234'
>>> numbers[5:-1]
'5678'
stepは省略されることが多いが、他の要素も同様に省略できる。
>>> numbers[:5] # 先頭から5文字
'01234'
>>> numbers[5:] # 5番目から末尾まで
'56789'
>>> numbers[:]
'0123456789'
>>> numbers[::2] # 1つ飛ばしで抽出
'02468'
文字列のフォーマット
値を文字列に埋め込む方法は、Pythonの歴史とともにいくつか登場してきた。
古いスタイル(%演算子)
C言語譲りの書式指定である。
>>> '%s is %d years old' % ('Alice', 30)
'Alice is 30 years old'
新しいスタイル(format()メソッド)
波括弧{}をプレースホルダーとして使う。
>>> '{} is {} years old'.format('Alice', 30)
'Alice is 30 years old'
f文字列
Python 3.6で追加された、現在もっとも推奨される書式指定方法である。クォートの前にf(またはF)を付け、波括弧の中に変数名や式そのものを書ける。
波括弧{}はプレースホルダーで、Pythonに解釈されるときに変数や式の値に置き換えられる。
>>> name = 'taro'
>>> city = 'tokyo'
>>> f'My name is {name} and I live in {city}'
'My name is taro and I live in tokyo'
>>> f'My name is {name.capitalize()} and I live in {city.rjust(20)}'
'My name is Taro and I live in tokyo'
:のあとに次のような書式指定をすることもできる。
- パディング
- 揃え(align)
- 符号
- 幅
- 文字数/精度
書式指定は次の並び順で指定する。主要な書式指定についてはこの後に説明するが、完全な使用法はドキュメントを参照いただきたい。
[[パディング文字]揃え][符号][0][幅][,][.精度][型]
幅とパディング
幅を指定するには:の後に幅の文字数を指定する。表示する文字列より幅が大きければ、余った部分はパディング文字で埋められる。表示する文字列より短い幅を指定しても、それは無視される。パディング文字のデフォルトは半角スペースである。
# 幅の指定(:の後に数値)
>>> f'{"abc":6}'
'abc '
# 幅より文字列が長い場合は、幅の指定は無視される
>>> f'{"abcdef":3}'
'abcdef'
揃え(align)は型によって異なる。文字列はデフォルトで左揃え、数値は右揃えで表示される。
# 揃えは型によってデフォルトが異なる(文字列は左揃え、数値は右揃え)
>>> f'{"abc":6}'
'abc '
>>> f'{42:6}'
' 42'
揃え(align)を指定するには、幅の数値より前に指定する。<(左揃え)、>(右揃え)、^(中央揃え)のいずれかを指定する。
# 揃え記号は幅の数値より前に指定する
>>> f'{42:<6}'
'42 '
>>> f'{42:>6}'
' 42'
>>> f'{42:^6}'
' 42 '
パディング文字を指定する場合は、揃えの前に指定する。
# パディング文字を指定する場合は、揃え記号より前に指定する
>>> f'{42:*^6}'
'**42**'
符号
数値の表示を整える書式指定も見ていこう。符号を付けたり、精度を指定したり、3桁ごとにカンマを入れることもできる。
符号は、書式指定の中で揃え(align)やパディング文字より後、幅(width)より前に、+・-・半角スペースのいずれか1文字を置くことで指定する。
>>> number = 42
>>> pi = 3.14159
# +:正の数にも符号を付ける
>>> f'{number:+}'
'+42'
>>> f'{-number:+}'
'-42'
# -:デフォルトと同じ(負の数だけに符号を付ける)
>>> f'{number:-}'
'42'
>>> f'{-number:-}'
'-42'
# 半角スペース:正の数には符号の代わりに空白を入れる
>>> f'{number: }'
' 42'
>>> f'{-number: }'
'-42'
半角スペースの指定は、符号の有無に関わらず数字の開始位置を揃えるのに便利だ。
浮動小数点数の書式指定では、主に精度(.のあとの数値)と型(f・e・g・%など)を組み合わせて使う。
f:固定小数点表記
もっとも基本的な書式で、.のあとに小数点以下の精度(桁数)を指定する。精度を省略した場合のデフォルトは6桁である。
>>> pi = 3.14159
>>> f'{pi:.2f}'
'3.14'
>>> f'{pi:f}'
'3.141590'
精度より小数点以下の桁数が大きい場合、単純な四捨五入ではなく、最も近い値に丸められ、ちょうど中間の場合は偶数側に丸められる(バンカーズラウンディングとも呼ばれる)という方式で処理される。
>>> f'{0.125:.2f}' # 0.125はちょうど0.12と0.13の中間
'0.12'
>>> f'{0.375:.2f}' # 0.375はちょうど0.37と0.38の中間
'0.38'
さらに注意が必要なのは、浮動小数点数は10進数の小数を正確に表現できないため、見た目上は「中間値」のつもりで書いた数値が、実際には内部的にわずかにずれた値として保存されている場合があるということである。
>>> f'{2.675:.2f}'
'2.67'
2.675は四捨五入すれば2.68になりそうだが、実際には2.67になる。これは丸め方式の問題ではなく、2.675自体が内部的には2.67499999999999982...というわずかに小さい値として保存されているためである。丸め処理は、この内部的な実際の値に対して正確に行われている。
精度より小数点以下の桁数が小さい場合、足りない桁数分は自動的に0で埋められる。
>>> f'{3.1:.4f}'
'3.1000'
>>> f'{2:.2f}'
'2.00'
e/E:指数表記
指数表記にフォーマットしたい場合は、型にeまたはEを指定する。eを指定すると指数部分が小文字のeで、Eを指定すると大文字のEで表示される。それ以外の動作に違いはない。
>>> f'{12345.6789:.2e}'
'1.23e+04'
>>> f'{12345.6789:.2E}'
'1.23E+04'
精度(.のあとの数値)は、仮数部(eの前の部分)の小数点以下の桁数を指定するものであり、指数部の表示には影響しない。精度を省略した場合のデフォルトは、f型と同じく6桁である。丸めの方式もf型と同じである。
>>> f'{12345:e}'
'1.234500e+04'
%:パーセント表記
値を100倍し、%を付けて表示する。
>>> f'{0.2567:.1%}'
'25.7%'
g/G:自動選択
数値の大きさに応じて、固定小数点表記と指数表記を自動的に切り替える。精度は小数点以下ではなく、有効桁数全体を指す点がfと異なる。
>>> f'{0.0000001234:g}'
'1.234e-07'
>>> f'{1234.5678:g}'
'1234.57'
型を省略した場合
精度を指定しなければ、値をそのまま表示する必要最小限の桁数で表示される。精度だけ指定すると、gに近い動作(有効桁数指定)になる。
>>> f'{pi}'
'3.14159'
>>> f'{pi:.3}'
'3.14'
これらは幅・揃え・符号・カンマなどの指定と組み合わせて使える。
>>> f'{pi:>10.2f}'
' 3.14'
その他の指定
さらにPython 3.8以降では、変数の値だけではなく名前も表示したいときに役に立つ新しい指定方法が使える。f文字列の{}内の変数名の後ろに=を1つ入れればよく、これはデバッグで役立つ。
>>> x = 10
>>> f'{x=}'
'x=10'
Python 3.12ではf文字列内の式に関する制限がさらに緩和され、文字列のネストやコメントの記述なども可能になっている。
文字列メソッド
Python公式ドキュメントによると、str型には非常に多くのメソッドが用意されている。代表的なものをカテゴリ別に紹介する。
検索・置換系:find()/index()(部分文字列の位置を検索)、startswith()/endswith()(先頭・末尾の判定)、count()(出現回数)、replace()(置換)
分割・結合系:split()(区切り文字で分割)、join()(イテラブルの要素を結合)
>>> 'a,b,c'.split(',')
['a', 'b', 'c']
>>> ','.join(['a', 'b', 'c'])
'a,b,c'
大文字小文字変換系:upper()、lower()、capitalize()(先頭だけ大文字)、title()(各単語の先頭を大文字)
判定系:isalpha()、isdigit()、isspace()など、isで始まるメソッド群
空白除去系:strip()/lstrip()/rstrip()(両端・左端・右端の空白を削除)、removeprefix()/removesuffix()(接頭辞・接尾辞を削除)
これらはあくまで一部であり、詳しくはPython公式ドキュメントを参照するとよいだろう。
Unicodeについてひとこと
Python 3の文字列はUnicode文字のシーケンスなので、日本語や絵文字も特別な意識をせずに1つの文字列として扱える。より深いUnicodeの話(面・コードポイント・エンコーディングなど)や、バイト列を扱うbytes型・bytearray型については、また別記事で取り上げたい。
まとめ
- Python 3の文字列(str)はUnicode文字のシーケンスで、イミュータブル
- クォート(
',",''',""")で作成し、\でエスケープする +で連結、*で繰り返し、[]とスライスで部分文字列を取り出す- 検索・分割・大文字小文字変換など、豊富な文字列メソッドが用意されている
- フォーマットは
%演算子 →format()→ f文字列と進化してきており、今はf文字列が最有力
文字列はどんなPythonプログラムでも避けて通れない存在である。ぜひ実際に対話型インタプリタで試しながら、感覚をつかんでほしい。
参考資料
- 『入門Python3 第2版』Bill Lubanovic 著、鈴木駿 監訳、長尾高弘 訳(オライリー・ジャパン)第5章「文字列」・第12章「データの自在な操作」
- Python 公式ドキュメント:組み込み型 --- テキストシーケンス型 str
- Python 公式ドキュメント:字句解析 --- フォーマット済み文字列リテラル