コース進捗 コース目次 24レッスン中 24件を公開中
Pythonの基礎
データとコレクション
信頼できるプログラムを作る
オブジェクトでモデル化する
プロフェッショナルなPython
上級Python
文字列は小さなタイルの列
Pythonでは、"Python"、"こんにちは"、"👍🏽"のように引用符で囲んだテキストを文字列と呼びます。小さなタイルが一列に並んでいると考えてみましょう。Pythonは、その部品をUnicodeのコードポイントとして扱います。
- 受け取る 名前、メモ、タグ
- 整える 空白や表記をそろえる
- 探す 必要な部分を見つける
- 作り直す 元の文字列はそのまま
位置は0から数えます。len()が数えるのはコードポイントです。バイト数でも、人が見て感じる文字数でもない場合があります。
word = "Python"
print(len(word))
print(word[0])
print(word[-1])
print(word[1:4])
6
P
n
yth
word[0]は最初、word[-1]は最後のタイルです。スライスは開始位置を含み、終了位置の直前で止まります。したがって[1:4]が取るのは位置1、2、3です。
指す、切る、分ける、つなぐ
1つのインデックスは、実在する位置を指す必要があります。word[99]ならIndexErrorです。一方、スライスはおおらかで、word[:99]はword全体を返します。空文字列には先頭も末尾もないため、インデックスを使う前に確認します。
name = "".strip()
if name:
print(name[0])
else:
print("先頭の文字はありません")
split()は文字列を分け、リストにします。join()は、選んだ区切りを使って文字列の部品をつなぎます。
messy = " 読む 書く\t遊ぶ "
pieces = messy.split()
print(pieces)
print(" ".join(pieces))
['読む', '書く', '遊ぶ']
読む 書く 遊ぶ
- 分ける テキストが部品になる
- 並べる リストで順番に待つ
- つなぐ 再びテキストになる
" ".join(text.split())で、余分な空白を1つにまとめられます。split(",")では、カンマだけが区切りです。カンマが2つ続けば、間に空の部品ができます。また、join()へ渡す要素はすべて文字列でなければなりません。
クリーナーは新しい文字列を返す
文字列は**変更不可能(immutable)**です。すでにある文字列のタイル1枚だけを入れ替えることはできません。次は、わざと失敗させる例です。
word = "python"
word[0] = "P"
これはTypeErrorになります。代わりに新しい文字列を作ります。
word = "python"
title = "P" + word[1:]
print(word)
print(title)
python
Python
文字列メソッドも同じです。strip()は前後の空白を取り、replace()は指定した並びを置き換え、casefold()はUnicodeを意識した強い検索用表記を作ります。どれも元を変更せず、新しい文字列を返します。
raw = " Python makes TEXT "
clean = raw.strip().replace("makes", "cleans")
searchable = clean.casefold()
print(clean)
print("python" in searchable)
print(searchable.count("text"))
print(searchable.find("cleans"))
Python cleans TEXT
True
1
7
inは「ある?」、count()は「重ならない一致が何個?」、find()は「最初の位置は?」に答えます。見つからないときのfind()は-1です。if text.find(term):は使いません。位置0は偽、-1は真として扱われ、意味が逆転するからです。
1つに見えても部品は複数かもしれない
見た目が同じ2つの文字列でも、コードポイントの並びが異なることがあります。標準ライブラリのunicodedataを使うと、正準等価な文字列を同じUnicode形式へそろえられます。
- タイル1枚 合成済みの文字
- タイル2枚 文字とアクセント
- NFC 等価な並びをそろえる
import unicodedata
one = "café"
two = "cafe\u0301"
print(one == two)
print(len(one), len(two))
print(one == unicodedata.normalize("NFC", two))
print(len("👍🏽"))
False
4 5
True
2
絵文字は画面上では1つに見えますが、ここでは2コードポイントです。基本のインデックスは途中で分割できます。文字列の位置は、バイト位置でも、必ず安全な「見た目の1文字」の境界でもありません。
ミニ制作:学習メモクリーナー
note_cleaner.pyを作ります。標準ライブラリだけで動く完成プログラムです。Unicodeを正規化し、空白をまとめ、空でないキーワードを数えます。元の文字列は変更しません。
import unicodedata
def search_key(text):
return unicodedata.normalize("NFC", text).strip().casefold()
def clean_note(text):
normalized = unicodedata.normalize("NFC", text)
return " ".join(normalized.split())
raw_note = " CAFÉ を練習し、cafe\u0301を復習する。 "
raw_keyword = " Café "
note = clean_note(raw_note)
keyword = search_key(raw_keyword)
matches = search_key(note).count(keyword)
print(f"元のメモ:<{raw_note}>")
print(f"整形後:<{note}>")
print(f"一致数:{matches}")
print(f"元の空白が残る:{raw_note.startswith(' ')}")
python3 note_cleaner.pyで実行し、次を確認します。
元のメモ:< CAFÉ を練習し、caféを復習する。 >
整形後:<CAFÉ を練習し、caféを復習する。>
一致数:2
元の空白が残る:True
境界の約束:キーワードは空にしません。text.count("")は文字ではなく隙間を数え、len(text) + 1になるため、学習語の集計には使えません。
3つの小さなミッション
- 安全な頭文字。 入力へ
strip()を使い、空でない場合だけ最初と最後のコードポイントを表示します。 - 短いプレビュー。
text[:limit]を返すpreview(text, limit)を書き、0、3、文字列より大きな値で試します。 - 検索探偵。 位置
0で一致する語と、存在しない語を試し、inまたはfind() != -1で正しく判定します。
第6章へ進む準備
len()、正負のインデックス、スライスを使える- 文字列が変更不可能だと説明できる
strip()、split()、join()、casefold()で文字列を整えられる- 問いに合わせて
in、count()、find()を選べる unicodedata.normalize()で等価なUnicode表現をそろえられる- コードポイント、見た目の記号、バイトが別物だと覚えている
- メモクリーナーを実行し、4つの確認結果を再現できる
次章では、split()が作った部品をリストへ入れ、変更できるリストと形を固定するタプルを比べます。