sec01 - 文字列のスライス

前回のレクチャーでは、文字列のインデックス(要素番号)について学習しました。今回のレクチャーでは、インデックスを使って、スライス(範囲指定)を行う方法を学習します。

文字列のスライス(部分文字列の取得)

前回のレクチャーでは、個々の文字の取得方法を学習しました。ここでは、部分文字列を取得する場合に用いる、スライスの方法を学習します。

文字列のスライスの基本

文字列の範囲指定を行う場合は、次の構文で指定します。

文字列のスライスの書式

s[n:m]: 要素番号"[n]"(含む)から要素番号"[m]"(※含めない)までの文字を取得する

文字列のスライスで気を付けなければならないのが、要素番号"[m]"の要素を含めないということです。(説明が難しいですが、『開始位置("[n]")から順に文字を抜き出していく。ただし、"[m]"の位置よりも左側の要素である限り』という言い回しの方が正確なのかもしれません。)

これは初学者の方はすんなりと受け入れることは難しいかもしれませんが、このようなルールになっています。

試しに、"Nico"の範囲をスライスする、次のコードを記述して実行してみましょう。

# "Nico"の範囲をスライスする
s = "I'm Nico, 10."
nico = s[4:8]
print(nico)

上記コードを実行すると、"Nico"の部分が出力されます。

スライスの挙動を、下の表を見ながら確認しましょう。まず、範囲指定の開始インデックスは[4]です。下記表の"[4]"に該当する文字を確認するとNになります。Pythonは、Nの文字から順に文字を抜き出していきます。(ただし、要素番号[8]の位置より左側の要素である限り。) 結果的に、[4][5][6][7]の"Nico"の文字が抜き出され、変数"nico"に代入されます。 (抜き出された文字はコピーされて、新しい変数に代入されるため、変数"s"の中身には変化がありません。)

[0][1][2][3][4][5][6][7][8][9][10][11][12]
I'm Nico, 10.
スライスを、文字列の最大の要素番号で指定した場合

"I'm Nico, 10."(最大の要素番号は[12])の文字列に対して、s[4:12]のように範囲指定すると、スライスは『[12]の要素を含まない』という挙動になるため、1番最後のピリオド(.)が抜き出されないという問題が起こります。つまり、これでは最後の要素を抽出できないということになります。

この問題への対処は、このレクチャーの後半の『インデックスを省略した書き方』の項目で解説します。

現段階では、『スライスはs[n:m][m]の要素は含めない』挙動になるというところを、しっかり押さえて頂ければと思います。

スライスの要素番号指定にマイナスの値を使用する

スライスの番号をマイナスの値を使って指定することもできます。先ほどの"Nico"の部分を抜き出すのに、次のような書き方もあります。

# "Nico"の範囲をスライスする
s = "I'm Nico, 10."
print(s[-9:8])
print(s[4:-5])
print(s[-9:-5])

上記コードのスライスの書き方は、いずれも有効です。s[n:m]nに文字列から抜き出す開始の要素番号が指定されており、文字列の中でnの開始位置より右側の位置を指すmの値が指定されていれば有効です。

[0][1][2][3][4][5][6][7][8][9][10][11][12]
[-13][-12][-11][-10][-9][-8][-7][-6][-5][-4][-3][-2][-1]
I'm Nico, 10.
要素番号指定の注意点

s[n:m]nmの指定方法の注意点ですが、文字列の中で、nで指定した要素の位置より、mで指定した位置の方が右側になるように指定します。

もし、nmが同じ位置を示したり、nの方がmより右側の位置の要素を指定した場合(位置の指定の順番が逆)は、『空文字 (カラ文字・0文字の文字列'')』が返されます。

# 空文字が返される例
s = "I'm Nico, 10."
print(s[4:4])  # 同じ位置を指定している
print(s[-9:4])  # 同じ位置を指定している
print(s[8:4])  # 位置の指定の順番が逆
print(s[-5:-9])  # 位置の指定の順番が逆(マイナスの値の場合)

スライスで指定できる要素番号の範囲

前回のレクチャーでは、インデックス指定で文字を抜き出す場合に(スライスでなくs[n]の書式)、文字列の範囲外の要素番号を指定するとエラーになるという解説をしましたが、スライスの場合は範囲外の値を指定してもエラーにならずに文字を抜き出してくれます。次のコードを実行し、確認してみましょう。

s = "I'm Nico, 10."
print(s[4:100])
print(s[-100:3])

上記コードの要素番号の指定方法のように、極端な数値を記述してもエラーになりません。

上記コードの2行目のように、s[n:m]mの値を文字列の最大の要素番号よりも大きな値で指定した場合は、『1番最後の要素まで文字を抜き出す』挙動になります。s[4:100]の場合は、[4]から最後の[12]を含む文字が抽出されます。これは、[100]までの要素が存在していると仮定した時に、[12][100]より左側に位置する要素であるからです。

上記コード3行目のs[-100:3]は、1番最初([0])から[2]までの文字を抽出します。(要素番号[3]は含まれません。) [-100]は文字列の先頭文字よりも左側の位置を指定していることになります。しかし、その部分には情報がないため、情報が存在する最初の文字([0])から文字列の抽出が行われます。

1番最後の要素までスライスしたい場合の書き方

文字列の1番最後の要素までスライスしたい場合、多くの場合は、次の項目(『インデックスを省略した書き方』)で解説する書式を利用します。(s[4:]のような記述になります。)

先の例のように、極端に大きな要素番号をs[n:m]mに指定したり、s[4:len(s)]のように文字列の長さをmに指定することで、最後の要素まで文字列を抽出できますが、Pythonの書き方としては一般的ではありません。

コードを書く時点で、文字列をスライスで最後の要素まで抽出することが決まっている場合は、シンプルにs[4:]のように記述しましょう。

インデックスを省略した書き方

スライスの書式(s[n:m])の、nmの値を省略して記述することができます。その場合、次のように文字が抜き出されます。

  • nを省略した時: 要素番号[0]の要素から抜き出す
  • mを省略した時: 1番最後の要素を含めて抜き出す

ここでの注意点は、mを省略した時は『1番最後の要素を含めて』抜き出すということです。『mを指定するとm含まない』、『を指定しなければ最後の要素も含めて抜き出す』という挙動の違いに注意してください。

それでは、次のコードを実行し、スライスの結果を確認してみましょう。

s = "I'm Nico, 10."
print(s[:5])  # "I'm N" (要素番号[0]から、要素番号[5]の1つ手前まで)
print(s[5:])  # "ico, 10." (要素番号[5]から、最後の要素まで)
print(s[:])  # "I'm Nico, 10." (要素番号[0]から、最後の要素まで)
print(s[-3:])  # "10." (後ろから3番目の要素([-3])から、最後の要素まで)

上記コードの出力結果は、各行のコメントに記載しています。文字列が抜き出される範囲を、下記表と照らし合わせて確認してみてください。

[0][1][2][3][4][5][6][7][8][9][10][11][12]
[-13][-12][-11][-10][-9][-8][-7][-6][-5][-4][-3][-2][-1]
I'm Nico, 10.

上記コードの補足としては、s[:]のように記述すると、先頭の文字から最後まで、元の文字列と同じものが抽出されます。print出力するだけなら、"print(s)"と記述するのと結果は同じになります。また、文字列の場合は[:]を使うメリットは一切ないため、これは単にそのような表記が可能であることを示す例になります。

(厳密には、s[:]の場合は抽出した文字列の情報がコピーされて新しいデータが生成されるという挙動になるため、"print(s)"と挙動が違います。残念ながら、文字列の場合はメリットが一切ないのですが、今後学習するlist(リスト)などの"データ構造"に関するレクチャーの中で、[:]は重要な役割があるという解説を行います。[:]を使用する場面や役割については、『Pythonのデータと変数の管理の仕方』という専用のレクチャーの中で詳しく解説します。)

文字列スライスのステップ

文字列のスライスは、"ステップ"を指定することができます。ステップを使うことで、文字列の"偶数番目の文字を取得"や"文字列を逆順にする"といった処理を行うことができます。

ステップの書式

s[n:m:step]: 要素番号"[n]"(含む)から要素番号"[m]"(※含めない)までの範囲で、step数ごとに文字を抽出する

  • step使用時、n, mの値は省略可能

【※ stepの値がマイナスの時の注意点 ※】

stepにはマイナスの値を指定することもでき、その場合は逆順に文字が抽出されます。この時、n, mの値は、[n]の要素の位置より[m]の要素の位置の方が左側を指定していないと空文字が返されるので注意が必要です。例: s[5:0:-1]のように指定します。

(ステップの値が正の整数の、n, mの値の指定方法はこれまで通りです。)

それでは、次のコードを実行して、結果を確認してみましょう。

s = '0123456789'
print(s[::2])  # 偶数番目の文字を取得
print(s[1::2])  # 奇数番目の文字を取得
print(s[::-1])  # 逆順に取得
print(s[6:0:-1])  # 逆順に取得([m]は含まないので、6から1までの逆順になる)

今回は、分かりやすく0から9までの数字の文字列を使用します。

s[::2]は、偶数番目の文字を取得します。nmの値が指定されていないので、先頭の文字から最後の文字の範囲で、2文字ごとに文字を抽出します。出力結果は"02468"になります。

s[1::2]は、奇数番目の文字を取得します。[1]の要素から2文字ごとに文字を抽出します。mの値が指定されていないので最後の文字を含めます。出力結果は"13579"になります。

s[::-1]は、nmの値が指定されていないので、先頭の文字から最後の文字までの範囲の中で-1文字毎に文字を抽出します。つまり、逆順に1文字ずつ抽出されます。出力結果は"9876543210"になります。

s[6:0:-1]は、nmの値を指定し、その範囲の中で逆順に文字を抽出するケースです。stepをマイナスの値にしたときは、nよりmの方が左側の位置を指定する必要があります。nは要素番号[6]なので、それよりも左側の位置の[0]~[5]の範囲でmを指定する必要があります。尚、この場合のスライスの範囲ですが、『要素番号"[n]"(含む)から"[m]"(※含めない)までの文字を取得する』というルールは有効ですので、『[6]を含んで[0]を含まない範囲で、-1文字ずつ文字を抽出する』という挙動になり、出力結果は"654321"になります。