「文字列の一部だけ取り出したい」「先頭3文字だけ欲しい」「後ろから2文字切り出したい」
Pythonでこうした処理を行うとき、最も便利なのがスライス(slice)機能です。
よくある処理例:
- ファイル名から拡張子を取得
- URLからドメイン名を抽出
- ユーザーIDの先頭文字でカテゴリ分け
- パスワードの一部をマスク表示
- 文字列の逆順表示
従来の方法との比較:
# 従来の方法(面倒)
text = "Python"
result = ""
for i in range(3):
result += text[i]
print(result) # "Pyt"
# スライスなら1行
print(text[:3]) # "Pyt"
スライスを使えば、Pythonの文字列をまるで配列のように自在に操作できます。
この記事では、文字列スライスの基本構文から、よく使うパターン、注意点、逆順処理まで分かりやすく解説します。
第1章:文字列スライスとは?基本構文を覚えよう
スライスの基本構文
文字列[開始:終了]
重要なポイント:
- 「開始」インデックスから「終了インデックスの1つ手前」まで取り出す
- 文字列のインデックスは0から始まる
- 終了インデックスは含まれない(left-inclusive, right-exclusive)
基本的な使用例
text = "Python"
print(text[0:3]) # "Pyt" (0, 1, 2番目の文字)
print(text[1:4]) # "yth" (1, 2, 3番目の文字)
print(text[2:6]) # "thon" (2, 3, 4, 5番目の文字)
インデックスのイメージ
文字列: P y t h o n
番号 : 0 1 2 3 4 5
逆番号: -6 -5 -4 -3 -2 -1
負のインデックスも使える
text = "Python"
print(text[-3:-1]) # "ho" (後ろから3番目〜後ろから2番目)
print(text[-2:]) # "on" (後ろから2番目〜最後まで)
print(text[:-2]) # "Pyth" (最初〜後ろから3番目まで)
ステップ(間隔)も指定できる
文字列[開始:終了:ステップ]
text = "Python"
print(text[0:6:2]) # "Pto" (0, 2, 4番目の文字)
print(text[1:5:2]) # "yh" (1, 3番目の文字)
完全な構文の理解
# [開始:終了:ステップ] の完全形
text = "abcdefghij"
print(text[1:8:2]) # "bdfh" (1から7まで2つ飛び)
print(text[0:10:3]) # "adgj" (0から9まで3つ飛び)
print(text[2:7:1]) # "cdefg" (2から6まで1つずつ)
第2章:スライスの応用テクニック
1. 開始・終了の省略
最初から指定位置まで:[:n]
text = "Python Programming"
print(text[:6]) # "Python" (先頭から6文字)
print(text[:3]) # "Pyt" (先頭から3文字)
print(text[:1]) # "P" (先頭1文字)
指定位置から最後まで:[n:]
text = "Python Programming"
print(text[7:]) # "Programming" (7文字目以降)
print(text[3:]) # "hon Programming" (3文字目以降)
print(text[-4:]) # "ming" (後ろから4文字)
全体をコピー:[:]
text = "Python"
copy_text = text[:]
print(copy_text) # "Python" (完全なコピー)
2. 負のインデックスの活用
最後のn文字を取得:[-n:]
filename = "document.pdf"
print(filename[-3:]) # "pdf" (拡張子取得)
print(filename[-4:]) # ".pdf" (ドット付き拡張子)
url = "https://www.example.com"
print(url[-3:]) # "com" (トップレベルドメイン)
最後のn文字を除去:[:-n]
text = "Hello World!!!"
print(text[:-3]) # "Hello World" (最後の3文字を除去)
print(text[:-1]) # "Hello World!!" (最後の1文字を除去)
3. 逆順・並び替えのテクニック
完全な逆順:[::-1]
text = "Python"
print(text[::-1]) # "nohtyP" (完全な逆順)
# 回文(palindrome)の判定に便利
word = "radar"
if word == word[::-1]:
print(f"{word} は回文です")
間隔を指定した抽出:[::n]
text = "abcdefghijklmnop"
print(text[::2]) # "acegikmo" (1文字おき)
print(text[::3]) # "adgjmp" (2文字おき)
print(text[1::2]) # "bdfhjlnp" (2番目から1文字おき)
逆順で間隔指定:[::-n]
text = "abcdefgh"
print(text[::-1]) # "hgfedcba" (完全逆順)
print(text[::-2]) # "hfdb" (逆順で1文字おき)
print(text[-1::-3]) # "hea" (最後から3文字おき)
4. 範囲を指定した逆順
text = "abcdefghij"
print(text[7:2:-1]) # "hgfed" (7番目から2番目まで逆順)
print(text[5:1:-2]) # "fdb" (5番目から1番目まで逆順で1文字おき)
第3章:実用的なスライスの使用例
ファイル操作での活用
# ファイル名と拡張子の分離
def get_file_info(filename):
"""ファイル名から名前と拡張子を取得"""
if "." in filename:
dot_index = filename.rfind(".") # 最後の'.'の位置
name = filename[:dot_index]
extension = filename[dot_index+1:]
return name, extension
return filename, ""
# 使用例
files = ["report.pdf", "image.jpg", "script.py", "readme"]
for file in files:
name, ext = get_file_info(file)
print(f"ファイル名: {name}, 拡張子: {ext}")
# スライスを使った簡単な方法
def simple_extension_check(filename):
"""拡張子チェック"""
if filename[-3:] == "pdf":
return "PDFファイル"
elif filename[-3:] == "jpg" or filename[-4:] == "jpeg":
return "画像ファイル"
elif filename[-2:] == "py":
return "Pythonファイル"
else:
return "その他"
for file in files:
print(f"{file}: {simple_extension_check(file)}")
データのマスキング・プライバシー保護
def mask_personal_info(data_type, value):
"""個人情報のマスキング"""
if data_type == "phone":
# 電話番号の下4桁以外をマスク
return "*" * (len(value) - 4) + value[-4:]
elif data_type == "email":
# メールアドレスの@より前を一部マスク
if "@" in value:
at_index = value.find("@")
username = value[:at_index]
domain = value[at_index:]
if len(username) > 3:
masked_username = username[:2] + "*" * (len(username) - 2)
else:
masked_username = "*" * len(username)
return masked_username + domain
return value
elif data_type == "credit_card":
# クレジットカード番号の最初と最後の4桁以外をマスク
if len(value) >= 8:
return value[:4] + "*" * (len(value) - 8) + value[-4:]
return "*" * len(value)
# 使用例
print(mask_personal_info("phone", "09012345678")) # ****5678
print(mask_personal_info("email", "john.doe@example.com")) # jo*****@example.com
print(mask_personal_info("credit_card", "1234567890123456")) # 1234****3456
URL・パス操作
def parse_url(url):
"""URLの各部分を解析"""
# プロトコルの取得
if "://" in url:
protocol_end = url.find("://")
protocol = url[:protocol_end]
rest = url[protocol_end + 3:]
else:
protocol = ""
rest = url
# ドメインとパスの分離
if "/" in rest:
domain_end = rest.find("/")
domain = rest[:domain_end]
path = rest[domain_end:]
else:
domain = rest
path = "/"
return {
"protocol": protocol,
"domain": domain,
"path": path
}
# 使用例
urls = [
"https://www.example.com/path/to/page",
"http://api.service.com/v1/users",
"www.simple.com",
"localhost:8000/admin"
]
for url in urls:
parsed = parse_url(url)
print(f"URL: {url}")
print(f" プロトコル: {parsed['protocol']}")
print(f" ドメイン: {parsed['domain']}")
print(f" パス: {parsed['path']}\n")
テキスト処理・バリデーション
def validate_format(data_type, value):
"""フォーマットバリデーション"""
if data_type == "japanese_postal":
# 日本の郵便番号 (XXX-XXXX)
if len(value) == 8 and value[3] == "-":
return value[:3].isdigit() and value[4:].isdigit()
elif data_type == "phone_jp":
# 日本の電話番号 (XXX-XXXX-XXXX)
if len(value) == 13 and value[3] == "-" and value[8] == "-":
return (value[:3].isdigit() and
value[4:8].isdigit() and
value[9:].isdigit())
elif data_type == "time_24h":
# 24時間形式の時刻 (HH:MM)
if len(value) == 5 and value[2] == ":":
try:
hour = int(value[:2])
minute = int(value[3:])
return 0 <= hour <= 23 and 0 <= minute <= 59
except ValueError:
return False
return False
# テスト
test_data = [
("japanese_postal", "123-4567"),
("japanese_postal", "1234567"),
("phone_jp", "090-1234-5678"),
("phone_jp", "090-12345678"),
("time_24h", "23:59"),
("time_24h", "25:61")
]
for data_type, value in test_data:
result = validate_format(data_type, value)
print(f"{data_type} '{value}': {'有効' if result else '無効'}")
文字列の加工・変換
def text_transformations(text):
"""様々な文字列変換の例"""
results = {}
# 先頭・末尾の取得
results["first_char"] = text[:1] if text else ""
results["last_char"] = text[-1:] if text else ""
results["first_three"] = text[:3]
results["last_three"] = text[-3:]
# 中間部分の取得
if len(text) > 6:
results["middle"] = text[3:-3]
else:
results["middle"] = ""
# 逆順
results["reversed"] = text[::-1]
# 特殊パターン
results["every_second"] = text[::2] # 1文字おき
results["every_third"] = text[::3] # 2文字おき
results["reverse_every_second"] = text[::-2] # 逆順で1文字おき
return results
# 使用例
sample_text = "Programming"
transformations = text_transformations(sample_text)
print(f"元の文字列: {sample_text}")
for key, value in transformations.items():
print(f"{key}: '{value}'")
第4章:スライスでよくあるミスと注意点
1. 範囲外アクセスでもエラーにならない
text = "abc"
# リストなら IndexError になるが、スライスはエラーにならない
print(text[0:10]) # "abc" (正常に動作)
print(text[5:8]) # "" (空文字列)
print(text[-10:2]) # "ab" (負の値が大きくても大丈夫)
# ただし、単一インデックスアクセスはエラーになる
try:
print(text[10]) # IndexError
except IndexError as e:
print(f"エラー: {e}")
2. 開始が終了より大きい場合
text = "abcdefg"
print(text[5:2]) # "" (空文字列)
print(text[3:1]) # "" (空文字列)
print(text[10:5]) # "" (空文字列)
# 逆順の場合は正常に動作
print(text[5:2:-1]) # "fed" (逆順で5番目から2番目まで)
3. スライスは新しい文字列を返す(イミュータブル)
original = "Python"
sliced = original[1:3]
print(f"元の文字列: {original}") # "Python" (変わらない)
print(f"スライス結果: {sliced}") # "yt"
# 文字列は変更不可能(immutable)
# original[0] = "J" # TypeError: 'str' object does not support item assignment
4. メモリ効率に関する注意
# 大きな文字列の一部を取得する場合
large_text = "x" * 1000000 # 100万文字
# スライスは新しい文字列オブジェクトを作成
small_part = large_text[:10]
# 元の文字列が不要になったら明示的にdel
del large_text
print(f"小さな部分: {small_part}")
5. ステップが0の場合のエラー
text = "abcdef"
try:
print(text[::0]) # ValueError
except ValueError as e:
print(f"エラー: {e}") # slice step cannot be zero
6. スライスオブジェクトの使用
# slice()関数を使った明示的なスライス
text = "abcdefghij"
# slice(start, stop, step)
s1 = slice(1, 5) # text[1:5] と同じ
s2 = slice(None, None, 2) # text[::2] と同じ
s3 = slice(2, 8, 3) # text[2:8:3] と同じ
print(text[s1]) # "bcde"
print(text[s2]) # "acegi"
print(text[s3]) # "cfh"
# 動的にスライスを構築する場合に便利
def get_slice(start=None, stop=None, step=None):
return slice(start, stop, step)
dynamic_slice = get_slice(1, -1, 2)
print(text[dynamic_slice]) # "bdfh"
第5章:応用テクニックとパターン
文字列の分割・結合での活用
def smart_split_join(text, delimiter=","):
"""スライスを使った文字列操作"""
# CSV風データの処理
if delimiter in text:
# 最初の区切り文字より前
first_part = text[:text.find(delimiter)]
# 最後の区切り文字より後
last_part = text[text.rfind(delimiter)+1:]
# 中間部分
first_delimiter = text.find(delimiter)
last_delimiter = text.rfind(delimiter)
if first_delimiter != last_delimiter:
middle_part = text[first_delimiter+1:last_delimiter]
else:
middle_part = ""
return {
"first": first_part,
"middle": middle_part,
"last": last_part,
"all_parts": text.split(delimiter)
}
return {"original": text}
# 使用例
test_strings = [
"apple,banana,cherry",
"first,second",
"single",
"a,b,c,d,e"
]
for s in test_strings:
result = smart_split_join(s)
print(f"'{s}' -> {result}")
パターンマッチングでの活用
def detect_pattern(text):
"""文字列パターンの検出"""
patterns = []
# 長さベースの判定
if len(text) >= 10:
patterns.append("長い文字列")
# 先頭・末尾パターン
if text[:4].lower() == "http":
patterns.append("URL")
if text[-4:].lower() in [".com", ".org", ".net"]:
patterns.append("ドメイン")
if text[:1].isdigit() and text[-1:].isdigit():
patterns.append("数字で囲まれている")
# 対称性チェック
if text == text[::-1]:
patterns.append("回文")
# 繰り返しパターン
half_length = len(text) // 2
if half_length > 0 and text[:half_length] == text[half_length:half_length*2]:
patterns.append("前半後半が同じ")
return patterns if patterns else ["特別なパターンなし"]
# テスト
test_cases = [
"https://www.example.com",
"radar",
"abcabc",
"12345test54321",
"hello world",
"a"
]
for case in test_cases:
patterns = detect_pattern(case)
print(f"'{case}': {', '.join(patterns)}")
まとめ:スライスを使いこなせば、文字列操作は自由自在!
Pythonのスライスをマスターすれば、文字列を切り出す、逆順にする、部分的にチェックするといった日常的な処理がグッと簡単になります。
本記事の重要ポイント
基本構文:
[開始:終了:ステップ]
の3つのパラメータ- 開始・終了・ステップはすべて省略可能
- 負のインデックスで後ろから指定
よく使うパターン:
[:n]
: 先頭からn文字[n:]
: n文字目以降[-n:]
: 最後のn文字[::-1]
: 完全逆順[::2]
: 1文字おき
注意すべきポイント:
- 範囲外でもエラーにならない:安全だが思わぬ結果に注意
- 新しい文字列を作成:元の文字列は変更されない
- ステップに0は指定不可:ValueErrorが発生
コメント