Pythonの文字列スライスは、短い記述で一部分を取り出したり、逆順にしたり、間引きしたりできる強力な機能です。
本記事では初学者にもわかる順序で、[start:stop:step]
の基本から負のインデックス、エラーになりやすいポイント、Unicodeの注意点までを丁寧に解説します。
実行結果つきのサンプルで挙動を確かめながら学べます。
Pythonのスライスの基本(文字列)
書式[start:stop:step]の意味
スライスの基本書式はs[start:stop:step]
です。
ここで、start
は開始位置、stop
は終端(ただし後述の通り終端は含まれません)、step
は何文字ごとに進むかを表します。
各引数は省略できます。
# 基本のサンプル: 英小文字で観察しやすく
s = "abcdefg"
print(s[1:5:2]) # 1から5未満まで2刻み → b, d
print(s[0:3]) # 0から3未満 → a, b, c
print(s[:4]) # start省略(先頭から)
print(s[3:]) # stop省略(末尾まで)
print(s[::]) # 全体をそのまま(コピー)
bd
abc
abcd
defg
abcdefg
終端は含まない(半開区間)
Pythonのスライスは半開区間([start, stop)
)です。
つまり、stop
の位置の文字は結果に含まれません。
この性質のおかげで、s[0:n]
とs[n:m]
を自然に連結できます。
s = "abcdefg"
print(s[1:4]) # 1,2,3の要素を取得
print(s[4]) # 4番目の要素(スライスの終端)は含まれていなかったことを確認
bcd
e
- 関連記事:リストの一部を取り出す – スライス
省略時のデフォルト値
省略した引数にはデフォルト値が入ります。
step
が正の場合と負の場合で、start
とstop
のデフォルトが異なる点に注意します。
以下にまとめます。
省略対象 | step > 0 のとき | step < 0 のとき |
---|---|---|
start | 0(先頭) | -1(末尾から) |
stop | len(s)(末尾の次) | -(len(s)+1)(先頭の前) |
step | 1 | -1 |
s = "abcdef"
print(s[:]) # start/stop/stepすべて省略 → 全体コピー
print(s[::-1]) # stepだけ-1 → 逆順
print(s[:3]) # start省略 → 先頭から3未満
print(s[3:]) # stop省略 → 3以降すべて
abcdef
fedcba
abc
def
インデックスは0始まり
Pythonのインデックスは0から始まります。
最初の文字はs[0]
です。
負のインデックスは末尾から数えますが、まずは0始まりになれると理解が安定します。
s = "Python"
print(s[0], s[1], s[2]) # 0始まりで順に取り出し
P y t
文字列スライスのよくある取得パターン
先頭n文字を取得する [:n]
先頭からn文字を取りたいときはs[:n]
が最も簡潔です。
範囲外のnを指定してもエラーにならず、存在する分だけ取得されます。
s = "こんにちはPython"
n = 5
print(s[:n]) # 先頭5文字
print(s[:100]) # 文字数を超えてもOK(自動クリップ)
こんにちは
こんにちはPython
- 関連記事:文字列の長さを調べる
末尾n文字を取得する [-n:]
末尾からn文字はs[-n:]
です。
ログの末尾やファイル拡張子などの抽出に役立ちます。
s = "report_2025_09.csv"
print(s[-4:]) # 拡張子 ".csv"
print(s[-7:]) # "9.csv"
.csv
9.csv
任意範囲の部分文字列 [i:j]
i以上j未満の範囲を抜き出します。
インデックスの境界が外れても安全に動作します。
s = "abcdefghij"
print(s[2:7]) # cdefg
print(s[-8:-3]) # d e f g h から -3(=h手前)まで → defg
cdefg
defg
逆順にする [::-1]
逆順はs[::-1]
が定番です。
回文判定のような用途でよく使います。
s = "racecar"
print(s[::-1]) # 逆順
print(s == s[::-1]) # 回文か判定
racecar
True
間引き抽出(step) [::2]
2文字おきに取りたい場合はs[::2]
のようにstep
を指定します。
奇数番目や偶数番目の抽出に便利です。
s = "0123456789"
print(s[::2]) # 偶数インデックス(0,2,4,6,8)
print(s[1::2]) # 奇数インデックス(1,3,5,7,9)
02468
13579
- 関連記事:リストを逆順ソート/反転する3つの方法
全体のコピー [:]
浅いコピーが欲しいときはs[:]
です。
文字列はイミュータブルなので、コピーの要件がある処理で意図を明示したいときに使います。
s = "immutable"
t = s[:] # 全体コピー
print(t, s, t is s) # 文字列は内部で同一化されることがある(実装依存)
immutable immutable True
負のインデックスとstepの使い方
末尾から数える負のインデックス
負のインデックスは末尾から数えます。
s[-1]
は最後の文字、s[-2]
は最後から2番目です。
s = "abcdef"
print(s[-1]) # f (最後)
print(s[-3:]) # def (最後から3文字)
print(s[:-3]) # abc (最後の3文字を除く)
f
def
abc
負のstepで逆方向に走査する
stepに負の値を指定すると、右から左に進みます。
start
は右側、stop
は左側を指すように設計するのがコツです。
s = "abcdefghij"
print(s[8:2:-1]) # i(8) → 3まで逆向きに(2は含まない) → ihgfed
print(s[::-2]) # 2刻みで逆方向 → jihfdb
ihgfed
jihfdb
範囲外でも空文字になる挙動
スライスは範囲外でも安全で、結果が存在しなければ""
(空文字)になります。
例外は発生しません。
s = "abc"
print(s[100:200]) # 範囲外 → 空文字
print(s[2:2]) # 開始と終了が同じ → 空文字
print(s[1:5:-1]) # 負のstepだが開始<終了 → 空文字
(出力はありません)
スライスの注意点とTips
文字列はイミュータブル(スライス代入不可)
文字列はイミュータブルです。
s[0] = "X"
のような代入はできません。
置換したいときはs = "X" + s[1:]
のように新しい文字列を作成します。
s = "hello"
try:
s[0] = "H" # 代入は不可
except TypeError as e:
print(type(e).__name__, str(e))
# 置換したい場合は新しい文字列を作る
t = "H" + s[1:]
print(t)
TypeError 'str' object does not support item assignment
Hello
- 関連記事:replaceで文字列を置換する
ステップ0はエラー(ValueError)
stepに0は指定できません。
必ず正または負の整数にします。
s = "abc"
try:
print(s[::0]) # 無効なstep
except ValueError as e:
print(type(e).__name__, str(e))
ValueError slice step cannot be zero
負のstep時の開始と終了の関係
負のstepではstart
はstop
より右側にある必要があります。
そうでないと空文字になります。
s = "abcdefgh"
print(s[5:1:-1]) # f e d c → fedc
print(s[1:5:-1]) # start(1)がstop(5)より左 → 空文字
fedc
境界は自動クリップされる
スライスは自動的に境界をクリップします。
大きすぎるインデックスや小さすぎるインデックスを指定してもエラーにならず、存在する範囲で切り出します。
s = "abcdef"
print(s[-100:1000]) # 全体
print(s[1000:]) # 空文字
print(s[:-1000]) # 空文字
abcdef
sliceオブジェクト(slice)で可読性向上
sliceオブジェクト(slice(start, stop, step)
)を使うと、スライス範囲に名前を付けたり再利用でき、可読性が高まります。
辞書や関数引数で渡したいときにも便利です。
# sliceを事前に定義して再利用
front3 = slice(0, 3) # 先頭3文字
even_idx = slice(0, None, 2)
names = ["Python", "Slicing", "Beginner"]
for w in names:
print(w[front3], w[even_idx])
# dictや関数に渡して使うケース
def take(s: str, sl: slice) -> str:
return s[sl]
mid = slice(2, 6)
print(take("ABCDEFGHIJ", mid))
Pto Pto
SliSlc
Beg Bgne
CDEF
Unicodeの結合文字に注意
スライスはコードポイント単位で行われるため、見た目の1文字(グラフェム)を途中で分断してしまうことがあります。
特に結合文字や国旗・家族絵文字に注意が必要です。
コード例(落とし穴のデモ)
import unicodedata
# "é" は単一コードポイントのNFCと、"e"+"◌́"のNFDで見た目が同じになる
nfc = "é" # U+00E9
nfd = "e\u0301" # U+0065 + U+0301
print(nfc, len(nfc)) # 見た目1文字、長さ1
print(nfd, len(nfd)) # 見た目1文字、長さ2
# NFDの先頭1文字だけを取ると、結合記号が切り落とされる
print(nfd[:1]) # "e" (アクセントが消える)
# 国旗絵文字は2コードポイントの組み合わせ
flag = "🇯🇵" # Regional Indicator Symbols 2つの連結
print(flag, len(flag))
print(flag[:1]) # 半分に割れてしまい不自然
é 1
é 2
e
🇯🇵 2
🇯
対処のヒント
- 正規化:
unicodedata.normalize("NFC", s)
で可能なら単一コードポイントへまとめ、分断リスクを減らします。 - グラフェム単位の分割: サードパーティの
regex
モジュール(標準のre
ではありません)の\X
で見た目の1文字単位に分割できます。環境が許せば次のように扱います。
# pip install regex が必要(環境に応じて導入)
import regex as re
s = "abcdef"
n = 3
graphemes = re.findall(r"\X", s)
print(graphemes[:n]) # 見た目の文字単位で安全にスライス
['a', 'b', 'c']
まとめ
本記事では、文字列スライス[start:stop:step]
の基本から、半開区間という重要概念、省略時のデフォルト、負のインデックスや負のstepの実践的な使い方までを段階的に解説しました。
文字列はイミュータブルであり、stepが0はエラー、境界は自動クリップされるなど、例外を避ける設計になっている一方、Unicodeの結合文字は思わぬ落とし穴になります。
必要に応じて正規化やグラフェム単位処理を使い分けると安心です。
基礎を押さえれば、部分取得、逆順、間引きなどの典型操作を短いコードで安全かつ明快に記述できます。