文字列とコンテナの総合演習

これまでのレッスンでは、リスト、タプル、辞書、セット、文字列メソッドを学びました。今回はそれらを組み合わせて、最も一般的な「コンボ」を使って実際の問題を解決します。これらのテクニックはプロフェッショナルなプログラミングで日常的に使われています。


1. split() とリストの連携

split() は文字列をリストに分解します——データ処理の第一歩です:

PYTHON
# CSV データの解析
line = "Alice,25,Beijing,Engineer"
fields = line.split(",")
print(fields)               # ['Alice', '25', 'Beijing', 'Engineer']

# 空白による分割(デフォルト)
text = "hello   world    python"
words = text.split()
print(words)                # ['hello', 'world', 'python']

# 分割数の制限
data = "2026-06-23-15-30-00"
parts = data.split("-", 3)
print(parts)                # ['2026', '06', '23', '15-30-00']

例:設定ファイルの解析(難易度 ⭐⭐)

PYTHON
# 設定ファイルの解析をシミュレート
config_text = """
host=localhost
port=8080
debug=true
theme=dark
"""

# 行で分割 → 等号で分割 → 辞書に保存
config = {}
for line in config_text.strip().split("\n"):
    if "=" in line:
        key, value = line.split("=", 1)
        config[key.strip()] = value.strip()

print(config)
# {'host': 'localhost', 'port': '8080', 'debug': 'true', 'theme': 'dark'}

# 設定を読み取り
print(f"Current host: {config.get('host', 'localhost')}")
print(f"Current port: {config.get('port', '80')}")
▶ 試してみよう

出力:

TEXT
{'host': 'localhost', 'port': '8080', 'debug': 'true', 'theme': 'dark'}
Current host: localhost
Current port: 8080

2. join() の威力

join()split() の逆で——リストを文字列に結合します:

PYTHON
# 基本的な使い方
words = ["Hello", "World", "Python"]
sentence = " ".join(words)
print(sentence)             # Hello World Python

# カンマで結合
csv = ",".join(["Alice", "25", "Beijing"])
print(csv)                  # Alice,25,Beijing

# 改行で結合
lines = "\n".join(["First line", "Second line", "Third line"])
print(lines)

join() のパフォーマンス上の利点

PYTHON
# ❌ 推奨しない — ループ内での文字列連結
result = ""
for i in range(1000):
    result += str(i) + ","
# これにより 1000 個の一時的な文字列オブジェクトが作成される

# ✅ 推奨 — リストに集めて join()
parts = [str(i) for i in range(1000)]
result = ",".join(parts)
# 1 回のマージで、はるかに効率的

例:表の書式設定(難易度 ⭐⭐)

PYTHON
# join + リスト内包表記でフォーマット済み表出力
headers = ["Name", "Age", "City"]
rows = [
    ["Alice", "25", "Beijing"],
    ["Bob", "30", "Shanghai"],
    ["Charlie", "22", "Guangzhou"],
]

# ヘッダー
print(" | ".join(headers))
print("-" * 25)

# データ行
for row in rows:
    print(" | ".join(row))
▶ 試してみよう

出力:

TEXT
Name | Age | City
─────────────────────────
Alice | 25 | Beijing
Bob | 30 | Shanghai
Charlie | 22 | Guangzhou

3. sorted() と文字列・辞書

sorted() はリストだけでなく、任意の反復可能オブジェクトに対して機能します:

PYTHON
# 文字列のソート — アルファベット順
text = "python"
sorted_chars = sorted(text)
print(sorted_chars)                 # ['h', 'n', 'o', 'p', 't', 'y']
print("".join(sorted_chars))        # hnopty — 再結合

# 辞書のソート — デフォルトでキーをソート
d = {"banana": 3, "apple": 5, "cherry": 2}
sorted_keys = sorted(d)
print(sorted_keys)                  # ['apple', 'banana', 'cherry']

# 値でソート
sorted_by_value = sorted(d.items(), key=lambda x: x[1])
print(sorted_by_value)              # [('cherry', 2), ('banana', 3), ('apple', 5)]

# セットのソート
s = {3, 1, 4, 1, 5, 9}
sorted_set = sorted(s)
print(sorted_set)                   # [1, 3, 4, 5, 9]

例:カスタムソートルール(難易度 ⭐⭐)

PYTHON
# ファイル名を数値部分でソート
files = ["file_10.txt", "file_2.txt", "file_1.txt", "file_20.txt"]

# 通常のソート — 文字列ソート、「10」は「2」より前に来る('1' < '2' のため)
print(sorted(files))
# ['file_1.txt', 'file_10.txt', 'file_2.txt', 'file_20.txt']

# 数値部分でソート — 数値を抽出し int に変換してからソート
def extract_number(filename):
    num_str = filename.split("_")[1].split(".")[0]
    return int(num_str)

sorted_files = sorted(files, key=extract_number)
print(sorted_files)
# ['file_1.txt', 'file_2.txt', 'file_10.txt', 'file_20.txt']
▶ 試してみよう

4. enumerate() と zip()

この 2 つの組み込み関数は日常業務で非常に一般的です。

enumerate() — 反復中にインデックスを取得

PYTHON
fruits = ["apple", "banana", "orange"]

# enumerate なし — 不格好
for i in range(len(fruits)):
    print(f"{i + 1}. {fruits[i]}")

# enumerate あり — エレガント
for i, fruit in enumerate(fruits, 1):    # start=1 で 1 からカウント
    print(f"{i}. {fruit}")

zip() — 複数リストの並列反復

PYTHON
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
cities = ["Beijing", "Shanghai", "Guangzhou"]

# 3 つのリストを同時に反復
for name, score, city in zip(names, scores, cities):
    print(f"{name}: {score} points, from {city}")

出力:

TEXT
Alice: 85 points, from Beijing
Bob: 92 points, from Shanghai
Charlie: 78 points, from Guangzhou

例:学生スコアレポート(難易度 ⭐⭐⭐)

PYTHON
# enumerate + zip を使って完全なレポートカードを生成
students = ["Alice", "Bob", "Charlie", "Diana"]
chinese = [85, 92, 78, 88]
math = [90, 85, 95, 80]
english = [78, 95, 82, 90]

print("=== Student Score Report ===")
print(f"{'Rank':<4}{'Name':<8}{'Chinese':<8}{'Math':<8}{'English':<8}{'Average':<8}")

# 平均を計算
averages = [(c + m + e) / 3 for c, m, e in zip(chinese, math, english)]

# 平均の降順でソート
data = list(zip(students, chinese, math, english, averages))
data.sort(key=lambda x: x[4], reverse=True)

# 出力
for rank, (name, c, m, e, avg) in enumerate(data, 1):
    print(f"{rank:<4}{name:<8}{c:<8}{m:<8}{e:<8}{avg:<8.1f}")
▶ 試してみよう

5. 総合データ処理ケース

学んだすべてを実際のシナリオに適用しましょう:

ケース:ログ分析(難易度 ⭐⭐⭐)

PYTHON
# シミュレートされたサーバーログ
log_data = """
2026-06-20 10:23:45 INFO  User 1001 logged in
2026-06-20 10:25:12 ERROR Database connection timeout
2026-06-20 10:26:30 INFO  User 1002 logged in
2026-06-20 10:27:45 WARN  Disk usage 85%
2026-06-20 10:28:10 ERROR User 1001 request timeout
2026-06-20 10:29:00 INFO  User 1001 logged out
"""

# 1. 行で分割
lines = log_data.strip().split("\n")

# 2. 各ログエントリを解析
parsed = []
for line in lines:
    parts = line.split()
    timestamp = f"{parts[0]} {parts[1]}"
    level = parts[2]
    message = " ".join(parts[3:])
    parsed.append({"time": timestamp, "level": level, "msg": message})

# 3. レベル別にカウント
level_count = {}
for entry in parsed:
    level = entry["level"]
    level_count[level] = level_count.get(level, 0) + 1

# 4. レベル順にソートして出力
print("=== Log Statistics ===")
for level, count in sorted(level_count.items()):
    print(f"{level}: {count} entries")

# 5. すべての ERROR を抽出
print("\n=== Error Details ===")
errors = [e for e in parsed if e["level"] == "ERROR"]
for e in errors:
    print(f"[{e['time']}] {e['msg']}")

出力:

TEXT
=== Log Statistics ===
ERROR: 2 entries
INFO: 3 entries
WARN: 1 entry

=== Error Details ===
[2026-06-20 10:25:12] Database connection timeout
[2026-06-20 10:28:10] User 1001 request timeout

よくあるユースケース


❓ よくある質問

Q split()split(" ") の違いは何ですか?
A 引数なしの split() は任意の空白(スペース、改行、タブ)で分割し、空の文字列を自動的に削除します。split(" ") は単一スペースでのみ分割し、連続するスペースは空の文字列を生成します。ほとんどの場合、引数なしの split() が良い選択です。
⚠️ Q:join() はリスト内のすべての要素が文字列である必要がありますか? A:はい。",".join([1, 2, 3]) はエラーになります。数値を先に文字列に変換してください:",".join(str(x) for x in [1, 2, 3])、またはリスト内包表記で str() を使います。

Q zip() が異なる長さのリストを処理するときはどうなりますか?
A zip() は最も短いリストで停止します。3 つのリストのうち 1 つが 2 要素しかない場合、zip() は 2 組しか生成しません。最も長いリストに合わせて None で埋めるには、itertools.zip_longest() を使用します。

📖 まとめ


📝 練習問題

  1. 基本(難易度 ⭐)s = "apple,banana,orange,grape" として、split() でリストに分解し、join()"apple | banana | orange | grape" を出力してください。

  2. 中級(難易度 ⭐⭐):2 つのリスト students = ["Alice", "Bob", "Charlie"]scores = [85, 92, 78] が与えられたとき、zip()enumerate() を使ってランキングレポートを出力してください:

    TEXT
    #1: Bob — 92 points
    #2: Alice — 85 points
    #3: Charlie — 78 points
    
  3. 挑戦(難易度 ⭐⭐⭐):「簡易 CSV ジェネレーター」を書いてください。リストのリスト data = [["Name", "Age", "City"], ["Alice", 25, "Beijing"], ["Bob", 30, "Shanghai"]] が与えられたとき、CSV 文字列(カンマ区切りの行)として出力します。ヒント:数値にはリスト内包表記で str() を使い、各行に ",".join()、全行に "\n".join() を使用。

100%