#14. 文字列、バイト、Unicode

テキスト データとバイナリ データは、Python では別個のオブジェクト ファミリです。strUnicode テキストを表します。bytes不変のバイナリデータを表します。bytearray可変バイナリデータを表します。

This separation is one of Python 3’s most important runtime design choices.テキストには文字とエンコーディングがあります。 Binary data has bytes. CPython implements these concepts with different object layouts, APIs, and invariants.

14.1 テキスト データとバイナリ データ

A string is text:python id="ly45du" s = "hello" バイト オブジェクトはバイナリ データです。python id="1wh8hi" b = b"hello" ASCII コンテンツでは似ているように見えますが、タイプは異なります。python id="iqtxra" print(type("hello")) # <class 'str'> print(type(b"hello")) # <class 'bytes'> Python はそれらを暗黙的に混合しません。python id="8bzmko" "hello" + b"world" # TypeError これは意図的なものです。テキストとバイトを組み合わせるには、エンコードの決定が必要です。```python id="8w5t7o" text = "hello" data = text.encode("utf-8")

again = data.decode("utf-8")


##14.2`str`Unicode テキストを表します

ニシキヘビ`str`Unicode コードポイントのシーケンスです。```python id="tq2xfz"
s = "Python 🐍"
print(len(s))

len(s)エンコードされたバイト数ではなく、コードポイントをカウントします。```python id="lxgm2u" s = "é"

print(len(s)) # 1 print(len(s.encode("utf-8"))) # 2


この違いは、CPython の内部全体にわたって現れます。```text id="c3cd4w"
str
    logical Unicode text

bytes
    encoded or arbitrary binary data
```## 14.3 Unicode コードポイント

Unicode コード ポイントは、Unicode 標準によって割り当てられた整数値です。

:

|キャラクター |コードポイント |
| --------- | ---------- |
|`A`       | `U+0041`   |
| `é`       | `U+00E9`   |
| ``       | `U+4E2D`   |
| `🐍`      | `U+1F40D`|

Python はこれを次の方法で公開します。`ord`そして`chr````python id="cvt38d"
print(ord("A"))        # 65
print(hex(ord("🐍")))  # 0x1f40d

print(chr(0x1f40d))    # 🐍
```単純な普遍的な意味では、文字列は内部的に UTF-8 バイトとして保存されません。 CPython は、文字列の内容に最適化された内部レイアウトを選択します。

## 14.4 柔軟な文字列表現

CPython は、Unicode 文字列の柔軟な内部表現を使用します。

重要なアイデアはシンプルです。```text id="hj5qoo"
Use the smallest fixed-width storage that can represent every code point in the string.
```概念的には:

|文字列内の最大コードポイント |         収納幅 |
| ---------------------------- | --------------------: |
| ASCII のみ |  1 文字あたり 1 バイト |
|まで`U+00FF`|  1 文字あたり 1 バイト |
|まで`U+FFFF`| 1 文字あたり 2 バイト |
|その上`U+FFFF`| 1 文字あたり 4 バイト |

例:```python id="k8wqod"
"abc"       # compact ASCII path
"café"      # may fit in 1-byte storage
"中"        # needs 2-byte storage
"🐍"        # needs 4-byte storage
```この設計により、すべての Unicode コード ポイントをサポートしながら、一般的な ASCII の多いテキストで 1 文字あたり 4 バイトの無駄が回避されます。

## 14.5 文字列オブジェクトのメタデータ

CPython 文字列には文字データ以上のものが格納されます。

概念的なフィールドには次のものが含まれます。```text id="xr6949"
object header
length
hash cache
state flags
kind
compact flag
ASCII flag
ready flag
character data
optional UTF-8 cache
```正確な C レイアウトはバージョンによって異なりますが、フィールド名よりも不変条件の方が重要です。

重要なメタデータ:

|メタデータ |目的 |
| ------------ | ----------------------------------------------- |
|長さ | Unicode コードポイントの数 |
|ハッシュキャッシュ |最初の計算後にハッシュを保存します。
|種類 |収納幅 |
| ASCII フラグ | ASCII 文字列の高速パス |
|コンパクトフラグ |データがオブジェクト ヘッダーの近くに保存されるかどうか |
| UTF-8 キャッシュ | C API 用にキャッシュされたエンコードされたフォーム |

文字列は不変であるため、キャッシュされたメタデータは安全です。ハッシュは一度計算されると有効なままになります。

## 14.6 文字列の不変性

Python 文字列は不変です。```python id="8lnw2h"
s = "hello"
s[0] = "H"       # TypeError
```操作により新しい文字列が作成されます。```python id="q4ux6e"
s = "hello"
t = "H" + s[1:]

print(s)    # hello
print(t)    # Hello
```不変性は CPython にいくつかの利点をもたらします。```text id="ylorrg"
hash values can be cached
strings can be safely interned
strings can be shared across dictionaries
strings can be used as dict keys
substring operations cannot mutate originals
```その代償として、文字列の連結を繰り返すと、記述が適切でないと多くの中間文字列が割り当てられる可能性があります。

## 14.7 文字列のハッシュ化

文字列はハッシュ可能です。```python id="7pmw4s"
hash("name")
```文字列のハッシュはその内容から計算されます。文字列は不変であるため、CPython は結果を文字列オブジェクト内にキャッシュできます。

文字列は辞書のキーとして頻繁に使用されるため、これは重要です。```text id="n69066"
module globals
object attribute names
class dictionaries
keyword argument names
JSON-like data
configuration maps
protocol field names
```キャッシュされた文字列ハッシュがないと、属性検索と辞書検索のコストが高くなります。

## 14.8 ストリングインターン

インターンとは、選択された場合に、等しい文字列値に対して 1 つの文字列オブジェクトを再利用することを意味します。```python id="alsrhm"
a = "identifier"
b = "identifier"

print(a is b)   # may be True
```CPython は、多くの識別子のような文字列をインターンします。これは、これらの文字列が属性検索や名前空間で一般的であるためです。

インターンは次のような場合に役立ちます。```text id="te68vu"
attribute names
variable names
keyword names
module names
common internal strings
```インターンされた文字列では、内部パスのハッシュ チェックの後に等価性チェックがポインタ チェックになることがよくあります。

ただし、ユーザー コードは任意の文字列 ID に依存すべきではありません。

正しい:```python id="k4ah3b"
if a == b:
    ...
```正しくない:```python id="tgxz95"
if a is b:
    ...
```使用`is`アイデンティティ セマンティクス、特に文書化されたシングルトンの場合のみ`None`

## 14.9 エンコーディング

エンコーディングはテキストをバイトに変換します。```python id="n587av"
s = "café"
b = s.encode("utf-8")

print(b)        # b'caf\xc3\xa9'
```文字列は Unicode テキストです。 UTF-8  1 つの外部バイト表現です。

一般的なエンコーディング:

|エンコーディング |使用 |
| -------- | ----------------------------------- |
| UTF-8 | Web、ファイル、APIUnix システム |
| UTF-16 |一部のプラットフォーム API とファイル形式 |
| UTF-32 |固定幅 Unicode ストレージ |
|ラテン語-1 |レガシー Western バイト マッピング |
|アスキー | 7 ビット英語サブセット |

CPython はエンコーディングをプロパティとして扱いません。`str`。文字列はデコードされたテキストです。エンコーディングはバイト境界を越えるときに使用されます。

## 14.10 デコード

デコードするとバイトがテキストに変換されます。```python id="n1sd4m"
b = b"caf\xc3\xa9"
s = b.decode("utf-8")

print(s)        # café
```選択したエンコーディングに対してバイトが無効な場合、エラー ハンドラーを使用しない限りデコードは失敗します。```python id="83jkr9"
b = b"\xff"

b.decode("utf-8")                 # UnicodeDecodeError
b.decode("utf-8", errors="ignore")
b.decode("utf-8", errors="replace")
```エラー ハンドラーには次のものが含まれます。```text id="j4u46t"
strict
ignore
replace
backslashreplace
surrogateescape
surrogatepass
```エンコーディング エラーはテキスト境界の一部であり、通常の文字列操作ではありません。

## 14.11 UTF-8 境界

最新の外部テキストは UTF-8 です。

:```text id="ps2rks"
source files
JSON
HTTP payloads
HTML
Markdown
logs
configuration files
database text protocols
```実際的なルール:```text id="cszoaq"
inside Python
    use str

at file, network, process, and binary protocol boundaries
    encode or decode explicitly
```例:```python id="le5dmj"
from pathlib import Path

text = Path("notes.txt").read_text(encoding="utf-8")
data = text.encode("utf-8")
```これにより、境界が明確に保たれます。

## 14.12 ソースコードのエンコーディング

Python ソース ファイルは解析する前にデコードされます。

エンコード宣言で特に指定がない限り、デフォルトのソースエンコードは UTF-8 です。

例:```python id="cndzxu"
# -*- coding: utf-8 -*-

name = "café"
```トークナイザーは、デコードされたソース テキストに対して動作します。文字列リテラルは Python になります`str`バイトリテラルでない限り、オブジェクト。```python id="wu9nv8"
s = "abc"       # str
b = b"abc"      # bytes
```この区別は、解析およびコンパイル中に行われます。

## 14.13 文字列リテラル

Python にはいくつかの文字列リテラル形式があります。```python id="9uk2iv"
"hello"
'hello'
"""hello"""
'''hello'''
r"\n"
f"value = {x}"
```リテラル接頭辞は解析に影響します。

|プレフィックス |意味 |
| ------------ | ------------------------------- |
|`r`|生の文字列リテラル |
|`f`|フォーマットされた文字列リテラル |
|`b`|バイトリテラル |
|`u`|過去の互換性プレフィックス |
|組み合わせ |`fr``rf``br``rb`|

生の文字列は、パーサーによるエスケープの解釈方法を変更します。```python id="5il9aw"
s = r"\n"
print(s)        # \n
```それはまだ正常を作成します`str`

## 14.14 バイトのオブジェクト`bytes`不変のバイナリデータを表します。```python id="rdc547"
b = b"hello"
```バイト オブジェクトは、0  255 の範囲の整数のシーケンスです。```python id="a55532"
b = b"ABC"

print(b[0])     # 65
print(b[1])     # 66
```スライスすると別のバイト オブジェクトが返されます。```python id="ry5nl7"
print(b[1:])    # b'BC'
```なぜなら`bytes`は不変ですが、ハッシュ可能です。```python id="524c01"
d = {b"key": "value"}
```バイトは次の場合に役立ちます。```text id="ujfy6z"
network protocols
binary files
cryptographic data
compressed data
encoded text
database wire formats
image and audio formats
```## 14.15 バイトのオブジェクト レイアウト

bytes オブジェクトは可変サイズです。

概念的には:```text id="kr49t3"
PyBytesObject
    PyVarObject header
        ob_size = number of bytes
    hash cache
    byte data
    trailing NUL byte for C compatibility
```末尾の NUL バイトは、C 文字列を期待する C API にデータを渡すときに役立ちますが、バイトには埋め込み NUL バイトが含まれる場合があります。```python id="z96jr9"
b = b"a\0b"
print(len(b))   # 3
```したがって、API が特に許可しており、コンテンツが安全であることがわかっている場合を除き、バイトを通常の NUL 終了文字列として扱ってはなりません。

## 14.16 バイト配列オブジェクト`bytearray`変更可能なバイナリデータです。```python id="cltuuo"
buf = bytearray(b"hello")
buf[0] = ord("H")

print(buf)      # bytearray(b'Hello')
```bytearray はインプレース変更をサポートします。```python id="vf2c2m"
buf.append(33)
buf.extend(b" world")
```内容が変更される可能性があるため、ハッシュ化できません。```python id="2mamf7"
hash(bytearray(b"abc"))     # TypeError
```概念的には、bytearray は可変バイト リストに近いですが、Python 整数オブジェクト参照の配列ではなく、コンパクトなバイト バッファーとして実装されています。

## 14.17 バイトと Int のリスト

比較してください:```python id="mm43w3"
b = b"abc"
xs = [97, 98, 99]
```bytes オブジェクトは、生のバイトをコンパクトに格納します。

リストには、Python 整数オブジェクトへの参照が保存されます。

概念的には:```text id="6zghb9"
bytes
    [97][98][99]

list
    [ptr][ptr][ptr]
      |    |    |
      v    v    v
     int  int  int
```バイナリデータの場合、`bytes`そして`bytearray`はるかにメモリ効率が高くなります。

## 14.18 メモリビュー`memoryview`別のオブジェクトのバッファを公開します。```python id="lk88r7"
buf = bytearray(b"hello")
view = memoryview(buf)

view[0] = ord("H")

print(buf)      # bytearray(b'Hello')
```メモリビューはコピーを回避します。

これは、API 間でバイナリ データをスライスしたり渡したりするときに役立ちます。```python id="fmmfbg"
data = bytearray(b"abcdef")
view = memoryview(data)[2:5]

print(view.tobytes())     # b'cde'
```ビューはエクスポータを存続させ、バッファ有効期間ルールを適用します。

## 14.19 バッファプロトコル

バッファ プロトコルは、背後にある C レベルのプロトコルです。`memoryview`

これにより、オブジェクトが生のメモリを他のオブジェクトに公開できるようになります。

一般的なバッファ エクスポーター:```text id="yt656l"
bytes
bytearray
array.array
memoryview
mmap
third-party arrays such as NumPy arrays
custom extension objects
```プロトコルでは次のように説明されています。```text id="va15hu"
pointer to memory
length
item size
format
number of dimensions
shape
strides
readonly flag
lifetime callbacks
```これにより、ゼロコピーバイナリ処理が可能になります。

## 14.20 テキスト I/O とバイナリ I/O

ファイルはテキスト モードまたはバイナリ モードで開くことができます。

テキスト モードはバイトを文字列にデコードします。```python id="ztfr4f"
with open("notes.txt", "r", encoding="utf-8") as f:
    text = f.read()
```バイナリ モードはバイトを返します。```python id="2vcxb8"
with open("notes.txt", "rb") as f:
    data = f.read()
```テキスト モードは、エンコード、デコード、および改行変換を処理します。

バイナリモードでは生のバイトが得られます。

データ モデルに基づいて選択します。```text id="ifzmcj"
human-readable text
    text mode, str

protocol bytes or exact file bytes
    binary mode, bytes
```## 14.21 一般的な境界バグ

よくあるバグは、境界でテキストとバイトが混在することです。

正しくない:```python id="zctul0"
def send(sock, message):
    sock.sendall(message)       # fails if message is str
```正しい:```python id="daxwma"
def send(sock, message):
    data = message.encode("utf-8")
    sock.sendall(data)
```受信用:```python id="jynmly"
data = sock.recv(4096)
text = data.decode("utf-8")
```エンコーディングが見えるように変換を明示的にしてください。

## 14.22 文字列の連結

文字列は不変であるため、連結すると新しい文字列が作成されます。```python id="fnu5rd"
s = "a"
s = s + "b"
s = s + "c"
```これにより、繰り返し割り当てることができます。

多くの部分では、`join`:

```python id="xnzlkg"
parts = ["a", "b", "c"]
s = "".join(parts)
```テキストをストリーミングするには、次を使用します。`io.StringIO`:

```python id="1icewe"
from io import StringIO

buf = StringIO()
buf.write("a")
buf.write("b")
buf.write("c")

s = buf.getvalue()
```CPython には一部のローカル連結ケースに対する最適化機能がありますが、コードは一般的なパフォーマンスに関して最適化に依存すべきではありません。

## 14.23 バイトの構築

バイトも不変です。

繰り返されるバイト連結にも同じ割り当ての問題があります。```python id="bufv11"
data = b""
for chunk in chunks:
    data += chunk
```より良い:```python id="5ojx6g"
data = b"".join(chunks)
```変更可能な増分構築の場合:```python id="bpfowr"
buf = bytearray()
for chunk in chunks:
    buf.extend(chunk)

data = bytes(buf)
```ファイル形式のバイナリ構築の場合:```python id="s6mtb3"
from io import BytesIO

buf = BytesIO()
buf.write(b"abc")
buf.write(b"def")

data = buf.getvalue()
```## 14.24 Unicode の正規化

異なる Unicode シーケンスは同じように見えることがあります。

例:```python id="d3pxs7"
a = "é"          # single code point U+00E9
b = "e\u0301"    # e plus combining acute accent

print(a == b)    # False
```これらは同様にレンダリングされる可能性がありますが、コード ポイントのシーケンスが異なります。

人間のテキストを比較するときに正規化します。```python id="7m77tv"
import unicodedata

a = unicodedata.normalize("NFC", a)
b = unicodedata.normalize("NFC", b)

print(a == b)    # True
```一般的な正規化形式:

|フォーム |意味 |
| ---- | ------------------------- |
| NFC |正規の構成 |
| NFD |正準分解 |
| NFKC |互換性構成 |
| NFKD |互換性の分解 |

正規化は Unicode レベルの問題であり、CPython オブジェクトのレイアウトの問題ではありません。しかし、これはテキストを正しく処理するために重要です。

## 14.25 ケースの折りたたみ

大文字と小文字を区別しない比較では、多くの場合使用する必要があります。`casefold` ない`lower````python id="xo53gl"
a = "Straße"
b = "strasse"

print(a.lower() == b.lower())       # often False
print(a.casefold() == b.casefold()) # True

casefold大文字と小文字を区別せずに一致するように設計されています。

識別子、ファイル名、ユーザー名、およびプロトコル フィールドについては、正確な正規化と大文字小文字の区別のルールを定義します。推測しないでください。

14.26 書記素クラスタ

ユーザーに表示される文字には複数のコード ポイントが含まれる場合があります。

例:```python id="g55aom" s = "👨‍👩‍👧‍👦" print(len(s))


パイソン`str`書記素クラスターではなく、コードポイントにインデックスを付けます。```python id="1i0lb4"
s[0]
```表示される文字の一部のみを返す場合があります。

UI テキストの編集、カーソルの移動、切り捨て、表示幅の場合、コード ポイントのインデックス付けが不十分な場合があります。 Unicode 対応の書記素セグメンテーションが必要です。

## 14.27 エンコードエラー戦略

文字を表現できない場合、エンコードが失敗する可能性があります。```python id="j37zya"
"é".encode("ascii")       # UnicodeEncodeError
```エラー戦略は動作を制御します。```python id="9s35v1"
"é".encode("ascii", errors="ignore")
"é".encode("ascii", errors="replace")
"é".encode("ascii", errors="backslashreplace")
```ファイルとプロセスの境界については、エラー処理を慎重に選択してください。

Unix 向けの一般的な戦略は次のとおりです。`surrogateescape`、デコードできないバイトがラウンドトリップすることを許可します。`str`一部のファイルシステムおよび環境コンテキストではデータを損失することはありません。

## 14.28 ファイルシステムのエンコーディング

ファイル パスはテキストとバイトの境界を越えます。

Python は通常、パスを次のように公開します`str`ただし、オペレーティング システムは、エンコードされたバイト シーケンスまたはプラットフォーム ネイティブの文字列形式で動作することがよくあります。

CPython には、Python 文字列とプラットフォーム パス表現の間で変換するためのファイルシステム エンコード ロジックがあります。

実際的なルール:```text id="pqbj4c"
use pathlib and str paths for normal application code
use bytes paths only when you need exact low-level byte behavior
```例:```python id="w0jnqu"
from pathlib import Path

p = Path("data") / "notes.txt"
text = p.read_text(encoding="utf-8")
```## 14.29 Unicode の C API ビュー

C 拡張コードでは、多くの場合、文字列を受け入れたり生成したりする必要があります。

一般的な操作は次のとおりです。```text id="psub45"
create Unicode from UTF-8
convert Unicode to UTF-8
inspect length
read code points
parse arguments as str
```概念的な例:```c id="ffcssi"
PyObject *s = PyUnicode_FromString("hello");
```そして:```c id="umgbtx"
const char *p = PyUnicode_AsUTF8(obj);
```一部の API によって返される UTF-8 ポインターは、文字列オブジェクトが所有するキャッシュを指す場合があります。その存続期間は、所有するオブジェクトが存続しているかどうかによって決まります。

拡張コードはそのポインターを解放してはなりません。

## 14.30 C API のバイトビュー

バイトは連続したバイナリ メモリを公開します。

概念的な例:```c id="g34cn9"
PyObject *b = PyBytes_FromStringAndSize(data, len);
```アクセス:```c id="q3e1ai"
char *p = PyBytes_AS_STRING(b);
Py_ssize_t n = PyBytes_GET_SIZE(b);
```高速マクロは、オブジェクトが実際にはバイト オブジェクトであると想定します。

より安全なチェック済み API はパブリック境界で使用する必要があります。

バイトには NUL バイトが含まれる場合があるため、常に明示的な長さを使用してください。

## 14.31 適切なタイプの選択

|必要 |タイプ |
| ------------------------- | ----------------------------- |
|ヒューマンテキスト |`str`|
|エンコードされたテキスト |`bytes`|
|バイナリ プロトコル データ |`bytes`|
|可変バイナリバッファ |`bytearray`|
|ゼロコピービュー |`memoryview`|
|多くのテキスト断片 |`list[str]`プラス`"".join`|
|多くのバイナリ フラグメント |`list[bytes]`プラス`b"".join`|
|インクリメンタル テキスト ライター |`io.StringIO`|
|インクリメンタルバイナリライター |`io.BytesIO`または`bytearray`|

タイプはデータ モデルを反映する必要があります。使用を避ける`str`任意のバイトの場合。使用を避ける`bytes`デコードされたテキストの場合。

## 14.32 メンタルモデル

このモデルを使用します。```text id="dluupp"
str
    immutable Unicode code point sequence
    optimized internal storage
    cached hash
    explicit encode/decode boundary

bytes
    immutable byte sequence
    compact binary storage
    hashable
    no text semantics unless decoded

bytearray
    mutable byte sequence
    compact binary storage
    useful for incremental construction

memoryview
    zero-copy view over buffer-exporting object
    lifetime tied to exporter
```テキスト処理のバグは、コード ポイント、バイト、エンコーディング、書記素クラスタ、正規化、表示幅の混乱から発生することがよくあります。 CPython のオブジェクト モデルではテキストとバイトが分離されているため、これらの決定は明示的なままになります。

## 14.33 概要`str`CPython の Unicode テキスト オブジェクトです。これは不変でハッシュ可能であり、柔軟なストレージ、キャッシュされたハッシュ、インターニングを通じて内部的に最適化されています。これは、エンコードされたバイトではなく、デコードされたテキストを表します。`bytes`不変のバイナリデータです。`bytearray`変更可能なバイナリデータです。`memoryview`バッファエクスポートオブジェクトへのゼロコピーアクセスを提供します。

テキストとバイナリ データの間の境界は明示的です: encode`str`生産する`bytes`、デコードします`bytes`生産する`str`。この境界は、正しいファイル I/O、ネットワーク プロトコル、ソース デコード、拡張コード、および Unicode 対応アプリケーションにとって不可欠です。