5. 文字列を使ったテキスト処理
プログラミングではテキストがあらゆる場所に登場します。ユーザーへのメッセージ表示、データファイルの処理、Web アプリケーションの構築など、テキストを扱うことはプログラマーとして身につける最も基本的なスキルのひとつです。Python では、テキストを扱うために 文字列 (string) を使います。文字列は、単語や文、その他あらゆる種類のテキストデータを表現できる文字の並びです。
これまでの章でも、print() や input() を使うときに文字列を少しだけ扱ってきました。ここからは文字列をより深く掘り下げて、どのように作成し、操作し、Python の強力な組み込み文字列機能を使って実際のテキスト処理の問題を解決するかを学びます。
この章では、特殊文字を含む文字列の作成方法、文字列の結合、文字列の一部の取り出し、大小文字や書式の変更、文字列内のテキスト検索、そして変更という観点でなぜ文字列が数値と異なる振る舞いをするのかについて学びます。章の終わりには、Python でテキストを扱うためのしっかりとした基礎が身についているはずです。
5.1) 文字列リテラルとエスケープシーケンス
5.1.1) 文字列リテラルの作成
文字列リテラル (string literal) とは、コードの中に直接書かれた文字列の値のことです。これまでにも、シングルクォート (') やダブルクォート (") を使って文字列を作成してきました。
# string_basics.py
greeting = 'Hello, World!'
message = "Python is great!"
print(greeting) # Output: Hello, World!
print(message) # Output: Python is great!Python ではシングルクォートとダブルクォートはまったく同じように機能します — どちらを使うかはあなたの好みです。ただし、文字列の中にクォート文字が含まれているときには、この 2 種類を使い分けられると便利です。
# quotes_in_strings.py
# 文字列の中にシングルクォートが含まれているときにダブルクォートを使う
sentence = "It's a beautiful day!"
print(sentence) # Output: It's a beautiful day!
# 文字列の中にダブルクォートが含まれているときにシングルクォートを使う
quote = 'She said, "Hello!"'
print(quote) # Output: She said, "Hello!"文字列を囲んでいるのと同じ種類のクォート文字を文字列の中に含める必要がある場合には、バックスラッシュ (\) を使ってエスケープ (escape) できます。
# escaping_quotes.py
# シングルクォートで囲んだ文字列の中でシングルクォートをエスケープする
sentence = 'It\'s a beautiful day!'
print(sentence) # Output: It's a beautiful day!
# ダブルクォートで囲んだ文字列の中でダブルクォートをエスケープする
quote = "She said, \"Hello!\""
print(quote) # Output: She said, "Hello!"バックスラッシュは、後に続くクォートが文字列の終わりではなく、文字列の内容の一部であることを Python に伝えます。
5.1.2) 三重クォートによる複数行文字列
複数行にまたがる文字列のために、Python には 三重クォート (triple quotes) が用意されています。シングルクォート 3 つ (''') でもダブルクォート 3 つ (""") でも構いません。
# multiline_strings.py
poem = """Roses are red,
Violets are blue,
Python is awesome,
And so are you!"""
print(poem)
# Output:
# Roses are red,
# Violets are blue,
# Python is awesome,
# And so are you!三重クォートで囲んだ文字列は、入力したとおりに改行やスペースがすべて保持されます。長いテキストブロックやドキュメンテーション文字列 (docstring、19 章で扱います) に特に便利ですし、シングルクォートとダブルクォートの両方をエスケープなしで含めたいときにも役立ちます。
# triple_quotes_convenience.py
dialogue = '''The teacher said, "Don't forget: it's important to practice!"'''
print(dialogue) # Output: The teacher said, "Don't forget: it's important to practice!"5.1.3) よく使うエスケープシーケンス
クォートのエスケープ以外にも、バックスラッシュは エスケープシーケンス (escape sequence) を導入します。これは、直接入力するのが難しい、あるいは不可能な文字を表す特殊な 2 文字の組み合わせです。
# escape_sequences.py
# 改行: 次の行に移動する
print("First line\nSecond line")
# Output:
# First line
# Second line
# タブ: 水平のスペースを挿入する
print("Name:\tJohn\nAge:\t25")
# Output:
# Name: John
# Age: 25
# バックスラッシュ: 実際のバックスラッシュを含める
path = "C:\\Users\\Documents"
print(path) # Output: C:\Users\Documentsよく使われるエスケープシーケンスをまとめると次のようになります。
| Escape Sequence | 意味 | 出力例 |
|---|---|---|
\n | 改行 (行の区切り) | 2 行のテキスト |
\t | タブ (水平スペース) | インデントされたテキスト |
\\ | バックスラッシュ | \ 文字 |
\' | シングルクォート | ' 文字 |
\" | ダブルクォート | " 文字 |
エスケープシーケンスの理解は、ファイルパス (特にバックスラッシュを使う Windows では重要です)、整形された出力、特殊な書式を必要とするテキストの扱いなどに不可欠です。
5.1.4) バックスラッシュをそのまま扱う raw 文字列
場合によっては、バックスラッシュをエスケープシーケンスとしてではなく、そのままの文字として扱いたいことがあります。これはファイルパスや正規表現 (39 章で簡単に触れます) を扱うときによくあります。Python では、文字列の前に r を付けることで raw 文字列 (raw string) を作成できます。
# raw_strings.py
# 通常の文字列: バックスラッシュはエスケープシーケンスとして解釈される
regular = "C:\new\test"
print(regular) # Output: C:
# ew est
# (\n は改行に、\t はタブになる)
# raw 文字列: バックスラッシュがそのまま扱われる
raw = r"C:\new\test"
print(raw) # Output: C:\new\testraw 文字列では、\n は改行ではなく、バックスラッシュと n という 2 文字そのものになります。raw 文字列は特に Windows のファイルパスで便利です。
# windows_paths.py
# raw 文字列を使わない場合、バックスラッシュをすべてエスケープする必要がある
path1 = "C:\\Users\\John\\Documents\\file.txt"
# raw 文字列を使うと、バックスラッシュを自然に書ける
path2 = r"C:\Users\John\Documents\file.txt"
print(path1) # Output: C:\Users\John\Documents\file.txt
print(path2) # Output: C:\Users\John\Documents\file.txtどちらの方法でも結果は同じですが、バックスラッシュが多い場合には raw 文字列の方が読みやすくなります。
5.2) 連結と文字列の繰り返し
5.2.1) + による文字列の連結
+ 演算子を使って文字列同士を結合できます。これを 連結 (concatenation) と呼びます。
# string_concatenation.py
first_name = "John"
last_name = "Smith"
# + で文字列を結合する
full_name = first_name + " " + last_name
print(full_name) # Output: John Smith
# より長い文字列の構築
greeting = "Hello, " + full_name + "!"
print(greeting) # Output: Hello, John Smith!連結は、文字列を末尾同士でつなげて新しい文字列を作ります。Python は自動的にスペースを挿入してはくれないので、自分で明示的にスペースを入れる必要があります。
# concatenation_spacing.py
word1 = "Hello"
word2 = "World"
# スペースなし
no_space = word1 + word2
print(no_space) # Output: HelloWorld
# スペースあり
with_space = word1 + " " + word2
print(with_space) # Output: Hello World1 つの式の中で、好きなだけ多くの文字列を連結できます。
# multiple_concatenation.py
address = "123" + " " + "Main" + " " + "Street"
print(address) # Output: 123 Main Street重要な制限: 連結できるのは文字列同士だけです。文字列と数値を連結しようとするとエラーになります。
# concatenation_error.py
age = 25
# これはエラーになります:
# message = "I am " + age + " years old" # TypeError!
# 数値を先に文字列に変換する必要があります
message = "I am " + str(age) + " years old"
print(message) # Output: I am 25 years old文字列と数値の変換については 5.6 節で詳しく見ていきます。
5.2.2) * による文字列の繰り返し
Python では、* 演算子を使って文字列を複数回繰り返す便利な方法が用意されています。
# string_repetition.py
separator = "-" * 20
print(separator) # Output: --------------------
# パターンの作成
pattern = "abc" * 3
print(pattern) # Output: abcabcabc
# 出力の整形に便利
print("=" * 30)
print("Important Message")
print("=" * 30)
# Output:
# ==============================
# Important Message
# ==============================繰り返し演算子は、任意の正の整数と組み合わせて使えます。
# repetition_examples.py
# 0 回の繰り返しは空文字列になる
nothing = "Hello" * 0
print(nothing) # Output: (empty string)
print(len(nothing)) # Output: 0
# 1 回の繰り返しは元の文字列と同じ
once = "Hello" * 1
print(once) # Output: Hello
# より大きな回数の繰り返し
many = "Go! " * 5
print(many) # Output: Go! Go! Go! Go! Go!文字列の繰り返しは、視覚的な区切り線の作成、パディング (埋め草) の追加、テスト用データの生成などに特に便利です。
# practical_repetition.py
# 簡単なテキストボックスの作成
width = 40
border = "=" * width
title = "Welcome"
padding = " " * ((width - len(title)) // 2)
print(border)
print(padding + title)
print(border)
# Output:
# ========================================
# Welcome
# ========================================5.2.3) 連結と繰り返しの組み合わせ
これら 2 つの演算子は、同じ式の中で組み合わせて使えます。Python の演算子の優先順位 (数値と同様に、掛け算が足し算より先) が適用されます。
# combined_operations.py
# 先に繰り返しが行われ、その後に連結される
result = "=" * 10 + " Title " + "=" * 10
print(result) # Output: ========== Title ==========
# 括弧を使って順序を制御する
repeated_phrase = ("Hello " + "World ") * 3
print(repeated_phrase) # Output: Hello World Hello World Hello Worldこれらの操作が文字列操作の基礎となり、シンプルな部品から複雑な文字列を組み立てられるようになります。
5.3) 文字列のインデックスとスライス
Python の文字列は文字の シーケンス (sequence) であり、各文字には特定の位置が割り当てられています。インデックスとスライスを使うことで、個々の文字にアクセスしたり、文字列の一部を取り出したりできます。
5.3.1) 文字列インデックスの理解
文字列の各文字には インデックス (index) と呼ばれる数値の位置が割り当てられています。Python は 0 から始まるインデックス (zero-based indexing) を採用しており、最初の文字はインデックス 0、2 文字目は 1、というように続きます。
# string_indexing.py
text = "Python"
# インデックスによる個々の文字へのアクセス
print(text[0]) # Output: P (最初の文字)
print(text[1]) # Output: y (2 文字目)
print(text[5]) # Output: n (6 文字目)インデックスと文字の対応を図で表すと次のようになります。
String: P y t h o n
Index: 0 1 2 3 4 5Python には 負のインデックス (negative index) もあり、文字列の末尾から数えることができます。インデックス -1 は最後の文字、-2 は最後から 2 番目、という具合です。
# negative_indexing.py
text = "Python"
print(text[-1]) # Output: n (最後の文字)
print(text[-2]) # Output: o (最後から 2 番目)
print(text[-6]) # Output: P (最初の文字)負のインデックスは、文字列の長さを正確に知らなくても末尾付近の文字にアクセスしたいときに特に便利です。
String: P y t h o n
Positive: 0 1 2 3 4 5
Negative: -6 -5 -4 -3 -2 -1重要: 存在しないインデックスにアクセスしようとすると IndexError が発生します。
# index_error.py
text = "Python"
# これは問題なく動作する
print(text[5]) # Output: n
# インデックス 6 は存在しないためエラーになる
# print(text[6]) # IndexError: string index out of range5.3.2) スライスで部分文字列を取り出す
インデックスが 1 文字だけを返すのに対して、スライス (slice) は文字列の一部 (これを 部分文字列 (substring) と呼びます) を取り出せます。基本的な構文は次のとおりです。
string[start:stop]これは、インデックス start から stop の 手前 までの文字を取り出します。
# basic_slicing.py
text = "Python Programming"
# インデックス 0 から 6 の手前までの文字を取り出す
print(text[0:6]) # Output: Python
# インデックス 7 から 18 の手前までの文字を取り出す
print(text[7:18]) # Output: Programming
# 中ほどの一部を取り出す
print(text[7:11]) # Output: Progここで大事なのは、stop インデックスは結果に 含まれない という点です。インデックスは文字と文字の「間」を指していると考えると分かりやすくなります。
P y t h o n
0 1 2 3 4 5 6したがって text[0:6] は「位置 0 から始めて、位置 6 の手前まで」という意味になり、位置 0, 1, 2, 3, 4, 5 の文字が得られます。
5.3.3) start や stop の省略
start を省略すると先頭から、stop を省略すると末尾までをスライスします。
# omitting_indices.py
text = "Python Programming"
# 先頭からインデックス 6 の手前まで
print(text[:6]) # Output: Python
# インデックス 7 から末尾まで
print(text[7:]) # Output: Programming
# 先頭から末尾まで (文字列全体)
print(text[:]) # Output: Python Programmingこれらの省略形は Python のコードの中で非常によく使われ、意図が分かりやすくなり、長さの値をハードコードする必要もなくなります。
5.3.4) スライスでの負のインデックスの利用
スライスでも負のインデックスが使えます。末尾から数える場合に便利です。
# negative_slice_indices.py
text = "Python Programming"
# 最後の 11 文字
print(text[-11:]) # Output: Programming
# 最後の 11 文字以外すべて
print(text[:-11]) # Output: Python
# 最後の 7 文字
print(text[-7:]) # Output: ramming
# インデックス 7 から、末尾から 3 文字目の手前まで
print(text[7:-3]) # Output: Programm ('ing' の手前で止まる)負のインデックスは、末尾から何文字かを除外したいときにも特に役立ちます。
# removing_suffix.py
filename = "document.txt"
# 最後の 4 文字 (.txt) 以外をすべて取り出す
name_without_extension = filename[:-4]
print(name_without_extension) # Output: document5.3.5) ステップ値付きのスライス
スライスは 3 番目の値である ステップ (step) を含めることもでき、いくつの文字ごとに飛ばすかを指定できます。
string[start:stop:step]# slicing_with_step.py
text = "Python Programming"
# 文字列全体から 1 文字おきに取り出す
print(text[::2]) # Output: Pto rgamn
# インデックス 0 から 6 まで、1 文字おき
print(text[0:6:2]) # Output: Pto
# 3 文字おき
print(text[::3]) # Output: Ph oai特に便利なテクニックとして、ステップに -1 を指定すると文字列を逆順にできます。
# reversing_strings.py
text = "Python"
# 文字列全体を逆順にする
reversed_text = text[::-1]
print(reversed_text) # Output: nohtyP
# 実用例: 回文かどうかの判定
word = "radar"
if word == word[::-1]:
print(f"{word} is a palindrome!") # Output: radar is a palindrome!5.3.6) スライスはエラーにならない
インデックスと違い、スライスはとても寛容です。範囲外のインデックスを指定しても、Python が自動的に調整してくれます。
# safe_slicing.py
text = "Python"
# どれもエラーにならない
print(text[0:100]) # Output: Python (末尾で止まる)
print(text[10:20]) # Output: (empty string - start が末尾より後ろ)
print(text[-100:3]) # Output: Pyt (start が 0 に調整される)この挙動のおかげで、文字列の長さを正確に知らない場合でも、安心してスライスを使えます。
5.3.7) スライスの実用例
よく使うパターンをいくつか見てみましょう。
# practical_slicing.py
text = "Hello, World!"
# 最初の 5 文字
print(text[:5]) # Output: Hello
# 最後の 6 文字
print(text[-6:]) # Output: World!
# 最初と最後の文字を除いたすべて
print(text[1:-1]) # Output: ello, World
# 1 文字おき
print(text[::2]) # Output: Hlo ol!
# 文字列を逆順に
print(text[::-1]) # Output: !dlroW ,olleHインデックスとスライスの理解は、Python におけるテキスト処理の基本です。これらのテクニックは、プログラミングの道のりの中で何度も登場します。
5.4) 大文字・小文字と空白のための基本的な文字列メソッド
Python の文字列には多くの組み込み メソッド (method) — 文字列オブジェクトに紐づいた関数 — が用意されています。この節では、大文字・小文字の変換と空白の扱いのためのメソッドを見ていきます。これはテキストのクレンジングや整形に必須です。
5.4.1) 文字列メソッドの基本
メソッドはドット記法 (string.method_name()) を使って呼び出します。メソッドは特定の型のオブジェクトに属する関数です。文字列に対して、Python は便利なメソッドを数多く提供しています。
# method_basics.py
text = "hello"
# 文字列に対してメソッドを呼び出す
result = text.upper()
print(result) # Output: HELLO
# 元の文字列は変化しない (理由は 5.8 節で説明します)
print(text) # Output: helloメソッドは新しい文字列を返すので、メソッド同士を連結 (メソッドチェーン) することもできます。
# method_chaining.py
text = " hello world "
# 複数のメソッドを連続して呼び出す
result = text.strip().upper().replace("WORLD", "PYTHON")
print(result) # Output: HELLO PYTHON5.4.2) 大文字・小文字変換メソッド
Python には大文字・小文字を変換するいくつかのメソッドがあります。
# case_methods.py
text = "Python Programming"
# すべて大文字にする
print(text.upper()) # Output: PYTHON PROGRAMMING
# すべて小文字にする
print(text.lower()) # Output: python programming
# 最初の 1 文字を大文字にし、残りを小文字にする
print(text.capitalize()) # Output: Python programming
# 単語ごとに先頭を大文字にする (タイトルケース)
print(text.title()) # Output: Python Programmingこれらのメソッドは、ユーザー入力を標準化するときに特に便利です。
# case_normalization.py
# ユーザー入力をシミュレートする
user_input = "YES"
# 大文字・小文字を無視した比較
if user_input.lower() == "yes":
print("User confirmed!") # Output: User confirmed!
# 別の方法: upper() を使う
command = "start"
if command.upper() == "START":
print("Starting process...") # Output: Starting process...title() メソッドは、各単語の最初の文字を大文字にし、残りを小文字にします。名前やタイトルの整形に便利です。
# title_case.py
name = "john smith"
print(name.title()) # Output: John Smith
book = "the great gatsby"
print(book.title()) # Output: The Great Gatsbyただし、title() はアポストロフィや特殊なケースでは限界があることに注意してください。
# title_limitations.py
text = "it's a beautiful day"
print(text.title()) # Output: It'S A Beautiful Day (S が大文字になっていることに注意)
# より高度なタイトルケース処理が必要な場合は、独自のロジックが必要になることもありますcapitalize() メソッドは文字列全体の最初の 1 文字だけを大文字にします。
# capitalize_examples.py
sentence = "python is great"
print(sentence.capitalize()) # Output: Python is great
# 最初の 1 文字だけが大文字になる点に注意
multi_word = "hello world"
print(multi_word.capitalize()) # Output: Hello world (Hello World ではない)5.4.3) 大文字・小文字判定メソッド
Python には、大文字・小文字を判定するメソッドも用意されています。
# case_checking.py
text1 = "HELLO"
text2 = "hello"
text3 = "Hello World"
# すべての文字が大文字かどうか
print(text1.isupper()) # Output: True
print(text2.isupper()) # Output: False
# すべての文字が小文字かどうか
print(text1.islower()) # Output: False
print(text2.islower()) # Output: True
# 文字列がタイトルケースかどうか
print(text3.istitle()) # Output: True
print(text2.istitle()) # Output: Falseこれらのメソッドは True か False を返します (3 章で学んだブール値)。そのため条件分岐に最適です。
# case_checking_conditions.py
password = "SECRET123"
if password.isupper():
print("Password is all uppercase") # Output: Password is all uppercase5.4.4) 空白の除去メソッド
空白 (whitespace) とは、スペース、タブ (\t)、改行 (\n) などを含みます。Python には、文字列の端から空白を取り除くメソッドが用意されています。
# whitespace_removal.py
text = " Hello, World! "
# 両端の空白を取り除く
print(text.strip()) # Output: Hello, World!
# 左側 (先頭側) の空白だけ取り除く
print(text.lstrip()) # Output: Hello, World!
# 右側 (末尾側) の空白だけ取り除く
print(text.rstrip()) # Output: Hello, World!strip() メソッドは、ユーザー入力をクレンジングするときに非常に役立ちます。
# cleaning_input.py
# 余分なスペースを含むユーザー入力をシミュレートする
user_name = " John Smith "
# 入力をきれいにする
clean_name = user_name.strip()
print(f"Welcome, {clean_name}!") # Output: Welcome, John Smith!これらのメソッドはタブや改行も取り除きます。
# strip_all_whitespace.py
text = "\n\t Hello \t\n"
print(repr(text)) # Output: '\n\t Hello \t\n'
cleaned = text.strip()
print(repr(cleaned)) # Output: 'Hello'strip(), lstrip(), rstrip() は文字列の両端のみから空白を取り除き、中央の空白はそのまま残ることに注意してください。
# strip_edges_only.py
text = " Hello World "
print(text.strip()) # Output: Hello World (中央のスペースは残る)5.4.5) 特定の文字の除去
strip 系メソッドには、空白以外の特定の文字を指定して、端から取り除く機能もあります。
# 複数の種類の文字を取り除く
text = "...Hello!!!"
cleaned = text.strip(".!")
print(cleaned) # Output: Hellostrip() に文字列を渡すと、その文字列に含まれる任意の文字の組み合わせを端から取り除きます。
# strip_character_set.py
text = "xxxyyyHelloyyyxxx"
# 端の x と y をどちらも取り除く
result = text.strip("xy")
print(result) # Output: Hello5.4.6) 大文字・小文字メソッドと空白メソッドの実用例
これらのメソッドが役立つ実際のシナリオを見てみましょう。
# practical_text_cleaning.py
# ユーザー入力のクレンジングと標準化
user_email = " JohnSmith@EXAMPLE.com "
clean_email = user_email.strip().lower()
print(clean_email) # Output: johnsmith@example.com
# 名前を適切な形に整形する
raw_name = " john smith "
formatted_name = raw_name.strip().title()
print(formatted_name) # Output: John Smith
# コマンドの処理 (大文字・小文字を区別しない)
command = " START "
if command.strip().upper() == "START":
print("Command recognized!") # Output: Command recognized!これらのメソッドはテキストのクレンジングと正規化の基礎であり、ユーザー入力の処理、ファイルの読み込み、データ分析前の前処理などで頻繁に使うことになります。
5.5) 文字列の検索と置換
文字列の中でテキストを見つけたり、書き換えたりすることは、プログラミングでよく行う作業です。Python には、部分文字列の検索やテキストの置換のための強力なメソッドが用意されています。
5.5.1) find() と index() による部分文字列の検索
find() メソッドは、部分文字列を検索し、最初に見つかった位置のインデックスを返します。
# find_method.py
text = "Python is great. Python is powerful."
# 最初の "Python" の出現位置を探す
position = text.find("Python")
print(position) # Output: 0 (先頭で見つかる)
# "great" を探す
position = text.find("great")
print(position) # Output: 10
# 存在しないものを探す
position = text.find("Java")
print(position) # Output: -1 (見つからない)find() メソッドは、見つからなかった場合には -1 を返すため、エラーを起こさずに安全に使えます。
# safe_searching.py
text = "Hello, World!"
# 部分文字列が存在するかどうかを確認する
if text.find("World") != -1:
print("Found 'World'!") # Output: Found 'World'!
if text.find("Python") == -1:
print("'Python' not found") # Output: 'Python' not found特定の位置から検索を始めることもできます。
# find_with_start.py
text = "Python is great. Python is powerful."
# 最初の出現位置
first = text.find("Python")
print(first) # Output: 0
# 最初の出現位置の次から検索して、2 回目の出現位置を探す
second = text.find("Python", first + 1)
print(second) # Output: 17index() メソッドは find() とほぼ同じ動きをしますが、部分文字列が見つからないときにエラーを発生させます。
# index_method.py
text = "Hello, World!"
# これは問題なく動作する
position = text.index("World")
print(position) # Output: 7
# これは ValueError になる:
# position = text.index("Python") # ValueError: substring not found使い分けの目安:
find()は、存在するかどうかを確認したいときに使う (見つからないと-1を返す)index()は、部分文字列が必ず存在すると分かっているときに使う (見つからないとエラー)
# choosing_find_vs_index.py
text = "Python Programming"
# find() を使って安全に確認する
if text.find("Java") != -1:
print("Found Java")
else:
print("Java not found") # Output: Java not found
# 部分文字列があることが分かっているときに index() を使う
position = text.index("Python") # Python が必ず含まれていると分かっている
print(f"Found at position {position}") # Output: Found at position 05.5.2) rfind() と rindex() による末尾からの検索
rfind() と rindex() メソッドは、文字列の右端 (末尾) から検索を行います。
# rfind_method.py
text = "Python is great. Python is powerful."
# 最後の "Python" の出現位置を探す
last_position = text.rfind("Python")
print(last_position) # Output: 17
# find() との比較 (find() は最初の出現位置を返す)
first_position = text.find("Python")
print(first_position) # Output: 0これは、最後に現れた位置が欲しいときに便利です。
# last_occurrence.py
filename = "document.backup.txt"
# 拡張子を取得するために最後のピリオドを探す
last_dot = filename.rfind(".")
if last_dot != -1:
extension = filename[last_dot:]
print(extension) # Output: .txt5.5.3) count() による出現回数のカウント
count() メソッドは、部分文字列が何回現れるかを教えてくれます。
# count_method.py
text = "Python is great. Python is powerful. Python is fun."
# "Python" が何回現れるか数える
count = text.count("Python")
print(count) # Output: 3
# 1 文字のカウント
letter_count = text.count("o")
print(f"Letter 'o' appears {letter_count} times") # Output: Letter 'o' appears 4 times特定の範囲内だけで数えることもできます。
# count_in_range.py
text = "abcabcabc"
# 文字列全体で "abc" を数える
total = text.count("abc")
print(total) # Output: 3
# 最初の 6 文字だけで "abc" を数える
partial = text.count("abc", 0, 6)
print(partial) # Output: 25.5.4) replace() によるテキストの置換
replace() メソッドは、指定した部分文字列を別の文字列に置き換えた新しい文字列を作成します。
# replace_method.py
text = "I love Java. Java is great."
# すべての "Java" を "Python" に置き換える
new_text = text.replace("Java", "Python")
print(new_text) # Output: I love Python. Python is great.
# 元の文字列は変わらない
print(text) # Output: I love Java. Java is great.第 3 引数に数値を渡すことで、置換回数を制限できます。
# limited_replace.py
text = "one one one one"
# 最初の 2 回だけ置換する
result = text.replace("one", "two", 2)
print(result) # Output: two two one onereplace() メソッドは大文字・小文字を区別します。
# case_sensitive_replace.py
text = "Python is great. python is powerful."
# これは "Python" (先頭が大文字) だけを置換する
result = text.replace("Python", "Java")
print(result) # Output: Java is great. python is powerful.大文字・小文字を区別しない置換を行いたい場合は、自分で工夫する必要があります。
# case_insensitive_approach.py
text = "Python is great. python is powerful."
# すべて小文字にしてから置換するが、大文字・小文字の情報は失われる
result = text.lower().replace("python", "java")
print(result) # Output: java is great. java is powerful.5.5.5) 検索・置換の実用例
これらのメソッドが活躍する実用的なシナリオを見てみましょう。
# practical_search_replace.py
# データのクレンジング: 不要な文字の削除
phone = "123-456-7890"
clean_phone = phone.replace("-", "")
print(clean_phone) # Output: 1234567890
# 単語のマスク (検閲)
message = "This is a bad word and another bad word."
censored = message.replace("bad", "***")
print(censored) # Output: This is a *** word and another *** word.
# ファイル拡張子の取り出し
filename = "document.txt"
dot_position = filename.rfind(".")
if dot_position != -1:
extension = filename[dot_position + 1:]
print(f"File type: {extension}") # Output: File type: txt
# 単語の出現回数を数える (簡易版)
text = "Python is fun. I love Python. Python rocks!"
word = "Python"
occurrences = text.count(word)
print(f"'{word}' appears {occurrences} times") # Output: 'Python' appears 3 timesこれらの検索・置換メソッドは、テキスト処理、データクレンジング、文字列操作を行う Python プログラムにおいて基本的なツールとなります。
5.6) 文字列と数値の相互変換
プログラミングで最もよく行う作業のひとつが、テキスト表現と数値表現の相互変換です。input() でユーザーから入力を読み取ると、たとえユーザーが数値を入力していても、結果は文字列になります。同様に、数値をテキストの中に表示したいときには、数値を文字列に変換する必要があります。
5.6.1) 文字列を数値に変換する
3 章ですでに int() と float() 関数を見ましたが、ここではもう少し掘り下げてみましょう。
# string_to_number.py
# 文字列を整数に変換する
age_text = "25"
age = int(age_text)
print(age) # Output: 25
print(type(age)) # Output: <class 'int'>
# 文字列を浮動小数点数に変換する
price_text = "19.99"
price = float(price_text)
print(price) # Output: 19.99
print(type(price)) # Output: <class 'float'>これらの変換は、ユーザー入力を処理するときに不可欠です。
# user_input_conversion.py
# ユーザー入力のシミュレーション (実際のコードでは input() を使う)
user_age = "30"
user_height = "5.9"
# 数値に変換して計算できるようにする
age = int(user_age)
height = float(user_height)
# こうして計算が行えるようになる
print(f"In 10 years, you'll be {age + 10}") # Output: In 10 years, you'll be 40
print(f"Your height in meters: {height * 0.3048:.2f}") # Output: Your height in meters: 1.80重要: 変換対象の文字列は、有効な数値を表していなければなりません。そうでない場合はエラーになります。
# conversion_errors.py
# これらは問題なく動作する
print(int("123")) # Output: 123
print(float("3.14")) # Output: 3.14
# これらは ValueError になる:
# print(int("hello")) # ValueError: invalid literal for int()
# print(int("12.5")) # ValueError: invalid literal for int() with base 10
# print(float("12.5.3")) # ValueError: could not convert string to floatこれらのエラーを安全に扱う方法は 28 章で学びます。今のところは、「文字列が有効な数値を表していないと変換が失敗する」という点を覚えておきましょう。
5.6.2) 数値文字列の空白の扱い
Python の変換関数は、先頭や末尾にある空白を自動的に処理してくれます。
# whitespace_handling.py
# スペースが含まれていても問題なく動作する
print(int(" 42 ")) # Output: 42
print(float(" 3.14 ")) # Output: 3.14
# 安全のために strip() と組み合わせる
user_input = " 100 "
number = int(user_input.strip())
print(number) # Output: 100これは、余分なスペースを含みがちなユーザー入力を処理するときに役立ちます。
5.6.3) 数値を文字列に変換する
str() 関数は、あらゆる値をその文字列表現に変換します。
# number_to_string.py
age = 25
height = 5.9
# 数値を文字列に変換する
age_text = str(age)
height_text = str(height)
print(type(age_text)) # Output: <class 'str'>
print(type(height_text)) # Output: <class 'str'>
# これで他の文字列と連結できる
message = "I am " + str(age) + " years old"
print(message) # Output: I am 25 years old数値を文字列と連結したいときには、必ずこの変換が必要になります。
# concatenation_with_numbers.py
score = 95
total = 100
# 連結するために数値を文字列に変換する必要がある
result = "Score: " + str(score) + "/" + str(total)
print(result) # Output: Score: 95/100
# 別の方法: f-string を使う (6 章で詳しく扱います)
result = f"Score: {score}/{total}"
print(result) # Output: Score: 95/1005.6.4) 整数と浮動小数点数の相互変換
整数型と浮動小数点型の間でも変換ができます。
# int_float_conversion.py
# float から int への変換 (小数部分を切り捨てる)
price = 19.99
price_int = int(price)
print(price_int) # Output: 19 (小数部分は削除され、丸められない)
# int から float への変換
age = 25
age_float = float(age)
print(age_float) # Output: 25.0重要: float から int への変換は小数部分を切り捨てるだけで、四捨五入はしません。
# truncation_not_rounding.py
print(int(3.9)) # Output: 3 (4 ではない!)
print(int(3.1)) # Output: 3
print(int(-3.9)) # Output: -3 (0 に向かって切り捨て)
# 四捨五入したい場合は、先に round() 関数を使う (4 章で扱います)
print(int(round(3.9))) # Output: 45.6.5) 変換の実用例
型変換が重要になる実用的なシナリオをいくつか見てみましょう。
# practical_conversions.py
# ユーザー入力の読み取りと処理
# (input() のシミュレーション - 実際のコードでは input() を使う)
user_input = "42"
# 計算のために数値に変換する
number = int(user_input)
doubled = number * 2
print(f"Double of {number} is {doubled}") # Output: Double of 42 is 84
# 整形された出力の作成
name = "John"
age = 30
height = 5.9
# 方法 1: 数値を文字列に変換する
info = name + " is " + str(age) + " years old and " + str(height) + " feet tall"
print(info) # Output: John is 30 years old and 5.9 feet tall
# 方法 2: f-string を使う (より読みやすい - 6 章で扱います)
info = f"{name} is {age} years old and {height} feet tall"
print(info) # Output: John is 30 years old and 5.9 feet tall
# ファイルからのデータ処理 (予告)
data_line = "100,200,300" # CSV ファイルの 1 行をシミュレート
numbers = data_line.split(",") # 文字列のリストに分割する
total = int(numbers[0]) + int(numbers[1]) + int(numbers[2])
print(f"Total: {total}") # Output: Total: 6005.6.6) 変換でよくある落とし穴
次のようなよくあるミスに注意しましょう。
# conversion_pitfalls.py
# 落とし穴 1: 数値でない文字列を変換しようとする
# text = "hello"
# number = int(text) # ValueError!
# 落とし穴 2: 変換せずに算術演算を行おうとする
age_text = "25"
# next_year = age_text + 1 # TypeError: can only concatenate str to str
# 正しい方法:
age = int(age_text)
next_year = age + 1
print(next_year) # Output: 26
# 落とし穴 3: int() で精度を失う
price = 19.99
price_int = int(price) # 20 ではなく 19 になる!
print(price_int) # Output: 19
# 落とし穴 4: カンマや通貨記号を含む文字列をそのまま変換しようとする
# price_text = "$1,234.56"
# price = float(price_text) # ValueError!
# まず文字列をクレンジングする必要がある:
price_text = "$1,234.56"
clean_price = price_text.replace("$", "").replace(",", "")
price = float(clean_price)
print(price) # Output: 1234.56型変換を理解することは、ユーザーとやり取りするプログラムや現実世界のデータを処理するプログラムを作るうえで非常に重要です。Python プログラミングの中で、これらの変換は何度も使うことになります。
5.7) in と not in による部分文字列の確認
Python には、ある文字列が別の文字列を含んでいるかどうかを調べるための、読みやすくシンプルな in / not in 演算子があります。これは検証、フィルタリング、条件分岐などで非常に役立ちます。
5.7.1) in を使った部分文字列の確認
in 演算子は、一方の文字列がもう一方の中に見つかれば True を、そうでなければ False を返します。
# in_operator.py
text = "Python is a powerful programming language"
# 部分文字列が存在するかどうか
print("Python" in text) # Output: True
print("powerful" in text) # Output: True
print("Java" in text) # Output: Falseこれは、find() や index() を使うよりもはるかに読みやすくなります。
# in_vs_find.py
text = "Hello, World!"
# in を使う (分かりやすい)
if "World" in text:
print("Found World!") # Output: Found World!
# find を使う (やや分かりにくい)
if text.find("World") != -1:
print("Found World!") # Output: Found World!in 演算子は大文字・小文字を区別します。
# case_sensitivity.py
text = "Python Programming"
print("python" in text) # Output: False (小文字の 'p')
print("Python" in text) # Output: True (大文字の 'P')
# 大文字・小文字を区別しない確認をしたい場合は、先に小文字に変換する
print("python" in text.lower()) # Output: True5.7.2) not in による不存在の確認
not in 演算子は、部分文字列が含まれて「いない」ことを確認します。
# not_in_operator.py
text = "Python is great"
print("Java" not in text) # Output: True (Java は含まれていない)
print("Python" not in text) # Output: False (Python は含まれている)これは、入力値の検証などに特に便利です。
# validation_examples.py
# ユーザー名に不正な文字 (スペース) が含まれていないかチェックする
username = "john_smith"
if " " not in username:
print("Username is valid (no spaces)") # Output: Username is valid (no spaces)5.7.3) その他の文字列チェックメソッド
Python には、文字列の性質を調べるための便利なメソッドが他にもあります。
# string_checking_methods.py
text = "Python"
# 文字列が特定の部分文字列で始まっているかどうか
print(text.startswith("Py")) # Output: True
print(text.startswith("Ja")) # Output: False
# 文字列が特定の部分文字列で終わっているかどうか
print(text.endswith("on")) # Output: True
print(text.endswith("ing")) # Output: False
# in を使うよりも、こちらの方が意図が明確な場合が多い
filename = "report.txt"
print(filename.endswith(".txt")) # Output: True
print(".txt" in filename) # Output: True (だが、やや曖昧)
# startswith/endswith は複数の候補をまとめてチェックできる
filename = "document.pdf"
print(filename.endswith((".pdf", ".doc", ".txt"))) # Output: Trueこれらのチェックメソッドは、入力値の検証、データのフィルタリング、条件分岐などで重要な道具となります。手動で文字列を検索するよりも、コードを読みやすく、保守しやすくしてくれます。
5.8) 文字列はイミュータブル: その実際的な意味
Python の文字列の最も重要な特徴のひとつは、イミュータブル (immutable) であるという点です。つまり、一度作成されたら変更できません。最初は制約のように感じるかもしれませんが、イミュータビリティを理解することは、正しい Python コードを書き、微妙なバグを避けるうえで非常に重要です。
5.8.1) イミュータビリティとは何か
文字列がイミュータブルである、というのは「既存の文字列の中身を変更できない」という意味です。一見「文字列を変更している」ように見える操作も、実際には新しい文字列を作成しています。
# immutability_basics.py
text = "Hello"
# 見た目は文字列を変更しているようですが、実際はそうではない
text = text + " World"
print(text) # Output: Hello World
# 実際に起きていること:
# 1. Python は新しい文字列 "Hello World" を作成する
# 2. 変数 'text' はこの新しい文字列を参照するようになる
# 3. 元の "Hello" という文字列は (ガベージコレクションされるまで) そのまま残る文字列の個々の文字を変更することはできません。
# cannot_modify_characters.py
text = "Hello"
# これはエラーになる:
# text[0] = "J" # TypeError: 'str' object does not support item assignment
# 代わりに新しい文字列を作る必要がある
text = "J" + text[1:]
print(text) # Output: Jelloこれは、(13 章で学ぶ) リストの振る舞いとは根本的に異なります。リストは ミュータブル (mutable) であり、要素を変更できます。
# lists_are_mutable.py
# リストのプレビュー (13 章で詳しく扱います)
numbers = [1, 2, 3]
numbers[0] = 10 # リストでは問題なく要素を変更できる
print(numbers) # Output: [10, 2, 3]
# しかし文字列ではこうはいきません:
text = "Hello"
# text[0] = "J" # これは文字列では TypeError!5.8.2) 文字列メソッドが新しい文字列を返す理由
文字列を「変更する」ように見える文字列メソッドはすべて、新しい文字列を返し、元の文字列はそのまま残します。
# methods_return_new_strings.py
original = "hello world"
# これらのメソッドはすべて新しい文字列を返す
uppercase = original.upper()
capitalized = original.capitalize()
replaced = original.replace("world", "Python")
# 元の文字列は変わらない
print(original) # Output: hello world
print(uppercase) # Output: HELLO WORLD
print(capitalized) # Output: Hello world
print(replaced) # Output: hello Pythonそのため、変更結果を保持したい場合は、必ず変数に代入する必要があります (同じ変数でも新しい変数でもかまいません)。
# keeping_changes.py
text = " hello "
# 間違い: 戻り値をどこにも代入していない
text.strip()
print(text) # Output: hello (スペースが残ったまま!)
# 正しい方法: 戻り値を代入する
text = text.strip()
print(text) # Output: hello (スペースが取り除かれている)これは初心者がよくやってしまうミスです。
# common_mistake.py
message = "python programming"
# ミス: メソッドを呼び出しているが、結果を使っていない
message.upper()
message.replace("python", "Python")
print(message) # Output: python programming (変わっていない!)
# 正しい方法: 戻り値を代入する
message = message.upper()
message = message.replace("PYTHON", "Python")
print(message) # Output: Python PROGRAMMING5.8.3) イミュータビリティの影響
イミュータビリティを理解すると、より良いコードを書けます。
1. 文字列は共有しても安全
# safe_sharing.py
original = "Hello"
copy = original # 両方の変数が同じ文字列を指す
# 文字列はイミュータブルなので、これは安全
copy = copy + " World"
print(original) # Output: Hello (変わらない)
print(copy) # Output: Hello World (新しい文字列)2. 文字列操作は新しいオブジェクトを作る
# new_objects.py
text = "Python"
# 各操作は新しい文字列オブジェクトを作成する
result1 = text.upper()
result2 = text.lower()
result3 = text.replace("P", "J")
# すべて別々のオブジェクト
print(id(text)) # あるメモリアドレス
print(id(result1)) # 別のメモリアドレス
print(id(result2)) # 別のメモリアドレス
print(id(result3)) # 別のメモリアドレス3. ループの中で文字列を組み立てると非効率になりうる
# inefficient_string_building.py
# これは多くの一時的な文字列オブジェクトを作成する
result = ""
for i in range(5):
result = result + str(i) # 毎回新しい文字列を作っている
print(result) # Output: 01234
# より効率的な方法 (多数の連結を行う場合):
# リストと join を使う (6 章で学びます)
parts = []
for i in range(5):
parts.append(str(i))
result = "".join(parts)
print(result) # Output: 012345.8.4) イミュータビリティと関数の引数
文字列を関数に渡すとき、その文字列が誤って変更されてしまう心配はありません。
# safe_function_arguments.py
def process_text(text):
# ここで行う操作はすべて新しい文字列を作成する
text = text.upper()
text = text.replace("A", "X")
return text
original = "banana"
result = process_text(original)
print(original) # Output: banana (変わらない)
print(result) # Output: BXNXNX (加工されたバージョン)これは、ミュータブルな型 (13 章で学ぶリストなど) と異なります。ミュータブルな型は、関数の中で変更すると元のオブジェクトにも影響します。
5.8.5) イミュータビリティを可視化する
文字列を「変更」したときに実際に何が起きているのかを、図で表してみましょう。
文字列がイミュータブルであることを理解すると、次のような点で役立ちます。
- メソッドの戻り値を変数に代入し忘れるミスを避けられる
- 文字列操作が新しいオブジェクトを作成する理由を理解できる
- 大量の文字列を組み立てるときに、より効率的な方法を選べる
- プログラムのさまざまな部分で文字列を安全に共有できる
このイミュータビリティは、リストのようなミュータブルな型と文字列を区別する根本的な特徴のひとつです。リストについては、この本の第 IV 部で詳しく見ていきます。
章のまとめ:
この章では、Python で文字列を使ってテキストを扱うための基本を学びました。ここまでで、次のことが理解できているはずです。
- クォートとエスケープシーケンスを使って文字列リテラルを作成する方法
- 連結と繰り返しで文字列を組み合わせる方法
- インデックスとスライスで個々の文字にアクセスしたり部分文字列を取り出したりする方法
- 大文字・小文字変換や空白除去のメソッドで文字列を変換する方法
- 文字列内のテキストを検索し、置換する方法
- 入力処理や出力整形のために、文字列と数値を相互に変換する方法
inとnot in演算子で部分文字列を確認する方法- 文字列がイミュータブルであること、そしてそれがコードにどのような意味を持つか
これらの文字列操作のスキルは、Python におけるテキスト処理の基礎を成します。ユーザーインターフェースの構築、データファイルの処理、入力値の検証、出力の整形などを行う際に、これらのテクニックを何度も使うことになるでしょう。
次の章では、ここで学んだ基礎の上に、さらに高度な文字列処理テクニックを重ねていきます。具体的には、文字列の分割と結合、f-string や format() メソッドを使った強力なフォーマット機能、そして国際化対応のためのテキストエンコーディングについて学びます。