Python / len() / 文字列の長さを返す関数

Pythonのlen(text)は、空白や改行を含めて文字列のユニコードコードポイント数を整数で返します。バイト数や画面上で人が1文字と感じる数とは必ずしも一致しません。リスト・タプル・辞書など、長さを持つコレクションでは項目数を返します。

基本的な使い方と空白・改行

len()は長さに対応するオブジェクトを1つ受け取ります。空文字列は0で、文字列を囲む引用符自体は数えません。コードポイントは文字要素を識別する値で、Pythonのstr型が扱う単位です。

Python 3を使います。各例のコード全体をUTF-8のlength_example.pyに保存し、python length_example.py、環境によってはpython3で実行します。ほかの例は内容を置き換えて独立に実行できます。

for text in ("abc", "", "a b", "a\nb"):
    print(repr(text), len(text))

期待される出力:

'abc' 3
'' 0
'a b' 3
'a\nb' 3

"a b"には空白、"a\nb"には改行が1つあり、どちらも長さ3です。ソース上の\nは2文字の表記ですが、文字列値では1つの改行です。repr()で表示すると区別しやすくなります。

文字列の長さとUTF-8のバイト数

UTF-8でエンコードすると保存・送信用のbytesになります。len(text)はコードポイント数、len(text.encode("utf-8"))は符号化後のバイト数です。例の\u00e9はアクセント付きのéを表します。

text = "caf\u00e9"
data = text.encode("utf-8")
print(len(text))
print(len(data))
4
5

文字列は4コードポイントですが、最後の文字がUTF-8で2バイトになるため合計5バイトです。ファイルや通信規格がバイトで制限する場合は、指定のエンコーディングで計算します。文字列の長さをそのままメモリ使用量と考えることもできません。

結合文字と絵文字は見た目の文字数と異なる場合がある

éは1コードポイント、またはeと結合アクセントの計2コードポイントで表現できます。見た目が似ていても値や長さは同じとは限りません。複数のコードポイントが1文字のように見える単位はユニコードの書記素クラスタで扱います。

import unicodedata

composed = "\u00e9"
decomposed = "e\u0301"
emoji = "\U0001f469\u200d\U0001f4bb"
print(len(composed), len(decomposed))
print(composed == decomposed)
print(len(unicodedata.normalize("NFC", decomposed)))
print(len(emoji))
1 2
False
1
3

unicodedata.normalize("NFC", text)によるユニコード正規化は、正準的に結合可能な表現を合成します。この例では長さ2の表現を1にしますが、すべての絵文字や組み合わせを1コードポイントにする関数ではありません。例の女性技術者の絵文字シーケンスは3コードポイントです。

画面の「文字数制限」は、コードポイント・書記素クラスタ・バイトのどれを使うか先に決めます。NFCだけで書記素の計算を代用せず、該当するユニコード分割規則に対応する実装と代表的な入力で確認してください。

コレクションの項目数と非対応の値

リスト・タプル・辞書・集合にも使用できます。辞書はキー数、集合は重複のない要素数を数えます。入れ子のリストでは外側の項目数であり、内部の値を自動的に合計しません。

print(len([10, 20, 30]))
print(len((10, 20)))
print(len({"a": 10, "b": 20}))
print(len({1, 1, 2}))
rows = [[1, 2], [3, 4]]
print(len(rows))
print(sum(len(row) for row in rows))
3
2
2
2
2
4

数値やNoneには長さが定義されず、TypeErrorになります。表示の長さが必要なら文字列に変換できますが、負号や小数点も数えます。反復可能でも必ずlen()に対応するわけではなく、通常のジェネレーターにも長さはありません。

for value in (1234, None):
    try:
        print(len(value))
    except TypeError:
        print("TypeError")
print(len(str(1234)))

items = (n * 2 for n in range(3))
try:
    print(len(items))
except TypeError:
    print("TypeError")
values = list(items)
print(len(values))
print(list(items))
TypeError
TypeError
4
TypeError
3
[]

ジェネレーターをリストにすれば項目数を調べられますが、要素を消費してメモリへ格納します。2回目のlist(items)は空です。大量データや無限イテレーターを、長さの確認だけのためにリスト化しないでください。

入力の長さを検証する完全な例

次の関数は文字列だけを受け取り、前後の空白を除いた値のコードポイント数が1~12か確認します。空文字列や空白だけの入力は拒否し、元の文字列自体は変更しません。

def valid_name(text):
    if not isinstance(text, str):
        raise TypeError("name must be a string")
    name = text.strip()
    return 1 <= len(name) <= 12

for text in ("Ada", "   ", "a" * 13, "  Ada  "):
    print(repr(text), valid_name(text))
'Ada' True
'   ' False
'aaaaaaaaaaaaa' False
'  Ada  ' True

内容や文字種の検証、内部のすべての空白の削除、見た目の文字数制限は行いません。サービスの長さの単位、空白の扱い、許可文字、サーバー側の検証を合わせ、画面と保存段階で同じ入力を異なる基準で判定しないようにします。