閉じる

Pythonのスライスで文字列の一部を取得する方法まとめ

文字列の一部だけを取り出したいとき、Pythonではスライスを使うと簡潔かつ安全に実現できます。

本記事では、start:end:step という記法の読み方から、よく使うパターン、注意点、slice オブジェクトの再利用まで、初心者の方がつまずきやすいポイントを順序立てて解説します。

Pythonの文字列スライスの基本

文字列とインデックスの基礎

Pythonの文字列はシーケンス型で、左から0始まりのインデックスでアクセスできます。

右から数える負のインデックスも使えます。

以下の例では、文字列 abcdefg に対するインデックスを対応づけます。

文字とインデックスの対応を一覧にすると次のようになります。

文字正のインデックス負のインデックス
a0-7
b1-6
c2-5
d3-4
e4-3
f5-2
g6-1

インデックスで1文字を取り出す簡単な例です。

Python
# 基本のインデックス参照
s = "abcdefg"
print(s[0])   # 先頭の文字
print(s[1])   # 2番目の文字
print(s[-1])  # 末尾の文字
実行結果
a
b
g

slice記法 start:end:step の読み方

スライスは s[start:end:step] という形で書きます。

意味は次のとおりです。

  • start(開始位置): 取り出しを始めるインデックス
  • end(終了位置): 取り出しをやめる位置(この位置の直前まで)
  • step(ステップ): 何文字おきに取り出すか(デフォルトは1)
Python
# slice記法の基本
s = "abcdefg"
print(s[1:5:2])   # インデックス1から4までを2文字おきに
print(s[0:7:3])   # 0から6までを3文字おきに
実行結果
bd
adg

終了位置は含まない半開区間

end は含まれません。

これを半開区間と呼びます。

この性質により、隣接区間を連結すると元の文字列に重複や欠落が生じません。

Python
s = "abcdefg"
print(s[0:3])  # 0,1,2番目まで -> 'abc'
print(s[3:7])  # 3,4,5,6番目   -> 'defg'
print(s[0:3] + s[3:7] == s)  # 連結すると元に戻る
実行結果
abc
defg
True

省略できる開始位置・終了位置・ステップ

startend は省略すると先頭や末尾が既定され、step は省略すると1になります。

Python
s = "abcdefg"
print(s[:3])    # 先頭から3文字 -> 'abc'
print(s[3:])    # 3番目から最後まで -> 'defg'
print(s[:])     # 全体のコピー -> 'abcdefg'
print(s[::2])   # 1文字おき -> 'aceg'
実行結果
abc
defg
abcdefg
aceg

負のインデックスで末尾から切り出す

負のインデックスは末尾から数える指定に便利です。

末尾N文字の取得や末尾を落とす処理が簡単に書けます。

Python
s = "abcdefg"
print(s[-3:])   # 末尾3文字 -> 'efg'
print(s[:-1])   # 末尾を1文字落とす -> 'abcdef'
print(s[-5:-2]) # 末尾から5番目(=2)から末尾から2番目(=5)の直前まで -> 'cde'
実行結果
efg
abcdef
cde

逆順スライスで文字列を反転

step に負の値を指定すると左向きにたどるので、反転や逆方向の抽出ができます。

Python
s = "abcdefg"
print(s[::-1])   # 全体を反転 -> 'gfedcba'
print(s[5:1:-1]) # インデックス5から2まで逆順 -> 'fedc'
実行結果
gfedcba
fedc

スライスは新しい文字列を返す

スライスは元の文字列を変更せず、新しい文字列(同じ内容)を返します。

元の変数を再代入してもスライスで得た文字列には影響しません。

Python
s = "abcdefg"
t = s[:]         # 全体コピー
s = s.upper()    # sを別の文字列に差し替え(元の't'はそのまま)
print("t:", t)
print("s:", s)
実行結果
t: abcdefg
s: ABCDEFG

注: オブジェクトの同一性(is)は実装の最適化の影響を受ける場合があります。

スライスの「新しさ」は、元の文字列を変更しないという意味で理解すると安全です。

文字列スライスの実用パターン

先頭N文字を取得

先頭からN文字を取りたい場合は s[:N] と書きます。

Nが長さを超えていても安全に処理されます。

Python
text = "Hello, World!"
N = 5
head = text[:N]
print(head)
実行結果
Hello

末尾N文字を取得

末尾からN文字は s[-N:] です。

ログの拡張子や末尾の識別子の抽出に便利です。

Python
filename = "report_2025_08_31.csv"
ext = filename[-3:]   # 拡張子(単純な例)
print(ext)
実行結果
csv

任意位置から固定長を切り出す

開始位置と長さが決まっているときは、s[pos:pos+length] と書くのが定番です。

Python
text = "ID:ABC12345;USER:alice"
pos = 3       # 'A' の位置
length = 4
part = text[pos:pos+length]  # 'ABC1'
print(part)
実行結果
ABC1

範囲外でも安全に切り出す

pos+length が長さを超えてもエラーにならず、可能な範囲で切り出されます。

Python
text = "short"
pos = 2
length = 10
print(text[pos:pos+length])  # 'ort'
実行結果
ort

先頭や末尾を削って中間を取得

前後の余白や共通プレフィックス/サフィックスを落として中間だけを取りたいときは、長さから差し引く形が分かりやすいです。

Python
s = "[payload:12345]"
prefix_len = 1  # '['
suffix_len = 1  # ']'
middle = s[prefix_len:len(s)-suffix_len]
print(middle)
実行結果
payload:12345

一文字おきに抽出するステップ指定

ステップを2にすると1文字おき、3にすると2文字飛ばしで取り出せます。

Python
s = "0123456789"
print(s[::2])  # 偶数インデックス -> '02468'
print(s[1::2]) # 奇数インデックス -> '13579'
実行結果
02468
13579

全体のコピーを作る s[:]

[:] は全体コピーです。

元の変数とは独立して保持しておきたいときに使えます。

Python
original = "immutable string"
copy = original[:]
print(copy == original)  # 内容は同じ
実行結果
True

逆順の一部を取り出す

末尾からN文字を逆順で取り出す場合は、負のステップを使うと1本で書けます。

Python
s = "ABCDEFGHIJ"
# 末尾5文字を逆順で -> 'JIHGF'
print(s[:-6:-1])

# インデックス8から3へ向けて2文字おきに逆順 -> 'IGE'
print(s[8:2:-2])
実行結果
JIHGF
IGE

スライスの注意点とベストプラクティス

範囲外でもエラーにならない挙動

開始や終了が範囲外でも例外は発生せず、可能な範囲だけが返ります。

意図せず空文字になるケースに気づきにくい点に注意します。

Python
s = "abc"
print(s[:100])   # 範囲外の終了 -> 'abc'
print(s[100:])   # 範囲外の開始 -> ''
実行結果
abc

開始が終了を超えると空文字になる

step が正のとき、start >= end だと空文字になります。

負のステップでは比較の向きが逆になります。

Python
s = "abcdef"
print(s[5:2])     # 正方向で5から2へは進めない -> ''
print(s[2:5:-1])  # 負方向で2から5へは進めない -> ''
print(s[5:2:-1])  # これはOK -> 'fed'
実行結果

fed

ステップ0はValueErrorになる

step に0は指定できません。

実行時に ValueError が発生します。

動的にステップを計算する場合は事前チェックが安全です。

Python
s = "abc"
step = 0
try:
    print(s[::step])  # ここで例外
except ValueError as e:
    print("エラー:", e)
実行結果
エラー: slice step cannot be zero

文字列はイミュータブルで代入不可

文字列はイミュータブル(不変)です。

インデックス代入やスライス代入はできません。

新しい文字列を作って置き換える発想が必要です。

Python
s = "abc"
try:
    s[0] = "X"          # 1文字の代入は不可
except TypeError as e:
    print("エラー1:", e)

try:
    s[1:3] = "YZ"       # スライス代入も不可
except TypeError as e:
    print("エラー2:", e)

# 置き換えたい場合は新しい文字列を作る
t = "X" + s[1:]
print("置換結果:", t)
実行結果
エラー1: 'str' object does not support item assignment
エラー2: 'str' object does not support item assignment
置換結果: Xbc

多バイト文字や絵文字の分割に注意

Pythonのインデックスは「コードポイント」単位であり、人が1文字と認識する「書記素(グラフェム)」を分断することがあります。

特に結合文字(濁点やスキントーン修飾、国旗の合成など)を含む場合に注意が必要です。

Python
text = "A👍🏼B🇯🇵C"  # 見た目は5文字に近いが、コードポイントはもっと多い
for i, ch in enumerate(text):
    # 各要素はコードポイント単位で1文字扱い
    print(i, ch, hex(ord(ch)))

# コードポイント単位のスライスは「書記素」を分断し得る
print("分断の例1:", repr(text[1]))    # '👍'
print("分断の例2:", repr(text[2]))    # '🏼' (スキントーン修飾のみ)
print("分断の例3:", repr(text[4:6]))  # 国旗の一部だけが取れる可能性
実行結果
0 A 0x41
1 👍 0x1f44d
2 🏼 0x1f3fc
3 B 0x42
4 🇯 0x1f1ef
5 🇵 0x1f1f5
6 C 0x43
分断の例1: '👍'
分断の例2: '🏼'
分断の例3: '🇯🇵'

上の例ではたまたま text[3:5] が完全な国旗になっていますが、常に安全という保証はありません。

表現形式(NFC/NFD)や合成の仕方によっては分断されます。

見た目の「1文字」単位で切り出したい場合は、サードパーティの regex モジュールの書記素単位パターン \X を使うのが現実的です。

regexのインストール

regexはインストールが必要なライブラリです。未インストールの場合はインストールしておきましょう。

Shell
pip install regex
Python
# pip install regex が必要です
import regex as re

text = "A👍🏼B🇯🇵C"
# 書記素(ユーザーが知覚する1文字)単位に分割
graphemes = re.findall(r"\X", text)
print(graphemes)          # ['A', '👍🏼', 'B', '🇯🇵', 'C']
print("書記素スライス:", "".join(graphemes[1:4]))  # '👍🏼B🇯🇵'
実行結果
['A', '👍🏼', 'B', '🇯🇵', 'C']
書記素スライス: 👍🏼B🇯🇵

境界値は変数で管理して意図を明確に

マジックナンバーを避け、len(s) や意味のある変数名を使って境界を表現すると読みやすくなります。

範囲をクリップする場合は minmax を併用するのが簡潔です。

Python
def safe_slice(s: str, start: int, length: int) -> str:
    n = len(s)
    a = max(0, start)
    b = min(n, a + max(0, length))
    return s[a:b]

s = "abcdef"
print(safe_slice(s, 2, 3))   # 'cde'
print(safe_slice(s, -10, 4)) # 'abcd' (負の開始は0にクリップ)
print(safe_slice(s, 3, 100)) # 'def'  (終了は長さにクリップ)
実行結果
cde
abcd
def

sliceオブジェクトの活用

sliceオブジェクトで範囲を再利用

slice(start, end, step) はスライス範囲をオブジェクトとして表現します。

複数の文字列に同じスライスを適用する場合に便利です。

Python
sl = slice(2, 5)  # [2:5)
a = "ABCDEFGHIJ"
b = "0123456789"
print(a[sl])  # 'CDE'
print(b[sl])  # '234'
実行結果
CDE
234

関数にスライスを渡して使い回す

関数の引数として slice を渡すと、どのようなシーケンスでも同じ「切り出しポリシー」を共有できます。

Python
from typing import Sequence, TypeVar

T = TypeVar("T")

def take_slice(seq: Sequence[T], sl: slice) -> Sequence[T]:
    # 文字列、リスト、タプルなどに対して動作
    return seq[sl]

sl = slice(1, None, 2)  # 1から末尾まで2おき
print(take_slice("abcdefgh", sl))  # 'bdfh'
print(take_slice([0,1,2,3,4,5,6], sl))  # [1, 3, 5]
実行結果
bdfh
[1, 3, 5]

スライスとインデックス計算の組み合わせ

slice.indices(length) は、与えた長さに対して start, stop, step を正規化(負の値や範囲外を補正)したタプルを返します。

ログ出力やデバッグで「実際にどの範囲が切られるのか」を確認するのに役立ちます。

Python
s = "abcdefg"
sl = slice(-5, 100, 2)   # 負の開始や範囲外の終了を含む
start, stop, step = sl.indices(len(s))
print("正規化:", start, stop, step)  # 実際には [2:7:2] と等価
print(s[sl], "==", s[start:stop:step])
実行結果
正規化: 2 7 2
ace == ace

また、負方向のスライスでも同様に正規化を確認できます。

Python
s = "abcdefg"
sl = slice(5, -10, -2)
print("正規化:", sl.indices(len(s)))  # (5, 0, -2)
print(s[sl])                          # 'fedb'
実行結果
正規化: (5, 0, -2)
fedb

まとめ

Pythonのスライスは、短く読みやすいコードで文字列の一部を安全に取り出せる強力な機能です。

start:end:step の基本と、終了位置が含まれない半開区間であること、開始・終了・ステップの省略規則、負のインデックスや負のステップの扱いを押さえると、実用的な抽出パターンをほぼ網羅できます。

一方で、範囲外がエラーにならないため空文字が紛れ込む点、step=0 はエラーになる点、文字列のイミュータビリティ、そして多バイト文字や絵文字の分断には注意が必要です。

再利用性を高めたい場合は slice オブジェクトや indices を活用して、境界の正規化やポリシーの共有を行うとよいでしょう。

最小限の記述で最大限の可読性を目指し、スライスを日常の文字列処理に活かしていきます。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!