プロジェクト

全般

プロフィール

6章学習記録 » 履歴 » バージョン 1

Tatsuya ISHIGAKI, 2025/06/30 13:11

1 1 Tatsuya ISHIGAKI
# 第6章 テキストの処理
2
3
## 6.1 一般的な文字列操作を行う str, string
4
5
- 文字列リテラル
6
  - シングルクォート、ダブルクォート
7
    - 文字列定義は1行で行う (開始クォートと終了クォートの間に、ソースコードの改行を行うとエラー)
8
    - 表現される文字列に改行を入れる場合はエスケープシーケンス `\n`
9
    - シングルクォート、ダブルクォートに差は無い
10
      - 終了クォートが異なるので、文字列中に他方のクォートを使用したい場合等で使い分ける
11
      - (例) `"I'm a champion"` `'say "HELLO", please'`
12
    ```python
13
    >>> s = "aaa\nbbbb\nccccc"
14
    >>> print(s)
15
    aaa
16
    bbbb
17
    ccccc
18
    >>> s
19
    'aaa\nbbbb\nccccc'  # 実際にはこのように保持されている
20
    ```
21
  - 3つのシングルクォート、3つのダブルクォート
22
    - 文字列定義が複数行にわたっても良い (開始クォートと終了クォートの間にソースコードの改行を行うと、そこは文字列でも改行扱い)
23
    - エスケープシーケンス `\n` を使っても改行される
24
    ```python
25
    >>> s2 = """AAA\nBBB
26
    ... BBB
27
    ... CCC\n\nDDD"""
28
    >>> print(s2)
29
    AAA
30
    BBB
31
    BBB
32
    CCC
33
    
34
    DDD
35
    >>> s2
36
    'AAA\nBBB\nBBB\nCCC\n\nDDD'  # 実際にはこのように保持されている
37
    ```
38
  - raw文字列
39
    - クォート前に **r** を付ける `r"How are you ?\nI'm fine."`
40
    - エスケープシーケンスが無視され、書いた通りに保持される
41
    ```python
42
    >>> s3 = r"AAA\nBBB"
43
    >>> print(s3)
44
    AAA\nBBB
45
    >>> s3
46
    'AAA\\nBBB'  # 実際にはこのように保持されている (\ が \\ に置換されている)
47
    ```
48
  - 文字列リテラルの接続
49
    - 文字列リテラルをスペースでつなげると、接続された単一文字列扱い
50
      ```python
51
      >>> s4 = "AAA" "bbb"    "CCC"
52
      >>> s4
53
      'AAAbbbCCC'
54
      ```
55
    - `()` で囲んで、文字列リテラルを複数行に分けて書くと、接続された単一文字列扱い
56
      - **注意** カンマを書くとタプル扱いとなってしまう
57
      ```python
58
      >>> s5 = (
59
      ...     "[1]This is"
60
      ...     "[2]other"
61
      ...     "[3]lines string literal"
62
      ...     )
63
      >>> s5
64
      '[1]This is[2]other[3]lines string literal'
65
      ```
66
      ```python
67
      >>> s6 = (
68
      ...     "string1",
69
      ...     "string2"
70
      ...     )
71
      >>> s6
72
      ('string1', 'string2')  # タプル
73
      ```
74
- 文字列以外のオブジェクトを文字列へ変換
75
  - 関数 `str()` へオブジェクトを渡すことで文字列を取得する
76
  - この際に取得される文字列は、オブジェクトの特殊メソッド `__str__()` の値
77
- 文字列のチェックメソッド (str オブジェクトのチェック用メソッド)
78
  |メソッド名|解説|
79
  |---|---|
80
  |isalnum()     |数字と文字|
81
  |isalpha()     |文字のみ|
82
  |isdecimal()   |10進数字|
83
  |isdigit()     |数字を表す文字|
84
  |isidentifier()|識別子として使用できる|
85
  |islower()     |小文字|
86
  |isnumeric()   |数を表す文字列 (漢数字なども含む)|
87
  |isprintable() |印字可能|
88
  |isspace()     |スペース、タブなどの空白文字|
89
  |istitle()     |単語の先頭のみ大文字であとは小文字|
90
  |isupper()     |大文字|
91
92
- 文字列の変換メソッド (str オブジェクトの変換メソッド)
93
  |メソッド名|解説|
94
  |---|---|
95
  |upper()||
96
  |lower()||
97
  |swapcase()|大文字を小文字に、小文字を大文字に|
98
  |capitalize()|先頭1文字を大文字に、それ以外を小文字に|
99
  |title()|単語毎に大文字1文字 + 小文字に|
100
  |replace(old, new[, count])|old を new へ置換<br>(count 指定の場合は、先頭から指定数まで変換)|
101
  |strip([chars])|先頭および末尾から、指定した文字列中の **文字** をすべて除去<br>chars 指定なしの場合は空白文字が削除される|
102
  |lstrip([chars])|先頭から (同上)|
103
  |rstrip([chars])|末尾から (同上)|
104
  |zfill(width)|長さが width になるように、左に 0 を詰める|
105
  |removeprefix(prefix, /)|先頭から prefix で指定した **文字列** を除去|
106
  |removesuffix(suffix, /)|末尾から suffix (同上)|
107
108
- その他文字列メソッド
109
  |メソッド名|解説|
110
  |---|---|
111
  |find(sub[, start[, end]])|sub の出現位置を返す<br>無い場合は -1|
112
  |split(sep=None, maxsplit=-1)|sep で分割したリストを返す (デフォルトは空白文字で分割)|
113
  |join(iterable)|引数それぞれを文字列で連結した単一文字列を返す|
114
  |startswith(prefix[, start[, end]])|prefix を先頭に持つかを判定<br>prefix はタプルで複数候補を指定可能|
115
  |endswith(suffix[, start[, end]])|suffix を末尾に持つかを判定<br>suffix はタプルで複数候補を指定可能|
116
  |encode(encoding="utf-8", errors="strict")|encoding で指定したエンコード形式に変換する<br>errors は変換できない文字があった場合の対応方法 `strict`:例外発生、`ignore`:対応文字無視、`replace`:対応文字を ? に変換|
117
118
- 文字列定数
119
  |定数名|解説|
120
  |---|---|
121
  |string.ascii_lowercase|英小文字 `"abcdefghijklmnopqrstuvwxyz"` (以下同様)|
122
  |string.ascii_uppercase|英大文字|
123
  |string.ascii_letters|英小文字、英大文字|
124
  |string.digits|0-9|
125
  |string.hexdigit|0-9, a-f, A-F|
126
  |string.octdigit|0-7|
127
  |string.punctuation|記号|
128
  |string.whitespace|空白 (スペース、タブ、改行等)|
129
  |string.printable|ascii_letter, digit, punctuation|
130
131
- `in` によるチェック
132
  - `部分文字列 in 文字列` により bool が返る
133
    ```python
134
    >>> value = 'Python'
135
    >>> 'P' in value  # 文字列に'P'が含まれている
136
    True
137
    >>> 'yth' in value  # 文字列に'yth'が含まれている
138
    True
139
    >>> 'x' in value  # 文字列に'x'が含まれていない
140
    False
141
    >>> 'xyz' in value  # 文字列に'xyz'が含まれていない
142
    False
143
    >>> 'p' in value  # 文字列に'p'は含まれていない(大文字と小文字は区別される)
144
    False
145
    ```
146
147
## 6.2 フォーマットと文字列リテラル f-string
148
- f-string の書き方
149
  - 文字列に `f` または `F` を前置する
150
  - f-string 内の `{expression}` は、expression を Python が式として処理した結果となる
151
    ```python
152
    >>> name = "smith"
153
    >>> num = 20
154
    >>> f"{name} has {num} items."
155
    'smith has 20 items.'
156
    ```
157
    ```python
158
    >>> a = 12
159
    >>> b = 3
160
    >>> f'a + b = {a + b}'
161
    'a + b = 15'
162
    >>> f'a / b = {a / b}'
163
    'a / b = 4.0'
164
    ```
165
- `=` を付けた出力
166
  - f-string 中に `{変数名=}` を書くと、その部分は「変数名 = 値」と出力される
167
    ```python
168
    >>> num = 20
169
    >>> name = "smith"
170
    >>> f"{name=}, {num=}"
171
    "name='smith', num=20"
172
    ```
173
- フォーマットの指定方法
174
  - f-string でコロンを使ったフォーマット書式を記述することで、出力をフォーマットできる
175
    |書式|解説|
176
    |---|---|
177
    |`:<30` `:>30` `:^30`|指定幅で左寄せ、右寄せ、中央寄せ|
178
    |`:-<30` `:->30` `:-^30`|指定幅で左寄せ、右寄せ、中央寄せし、その際にスペースではなく指定文字 (左記例では `-`) を埋める|
179
    |`:b` `:o` `:d` `:x` `:X`|2進数、8進数、10進数、16進数(小文字)、16進数(大文字) に変換する|
180
    |`:f`|固定小数点の文字列へ変換|
181
    |`:%`|百分率表記へ変換|
182
    |`:,`|数値3桁ごとにカンマを挿入する|
183
    |`:6.2f`|表示桁数指定 (左記例では 6:全体桁数、2:小数点以下桁数)<br>※全体桁数は小数点を含む文字数で、右寄せスペース埋め|
184
    |`:%Y-%m-%d` `:%H:%M:%S`|日付型の書式|
185
186
    ```python
187
    >>> import math
188
    >>> value = 'left align'
189
    >>> f'|{value:<30}|'  # 文字列を左に寄せて、30文字になるようにスペースで埋める
190
    '|left align                    |'
191
    >>> value = 'right align'
192
    >>> f'|{value:>30}|'  # 文字列を右に寄せて、30文字になるようにスペースで埋める
193
    '|                   right align|'
194
    >>> value = 'center'
195
    >>> f'|{value:^30}|'  # 文字列を中央にそろえて、30文字になるようにスペースで埋める
196
    '|            center            |'
197
    >>> f'{value:-^30}'  # 文字列を中央にそろえて、30文字になるように「-」で埋める
198
    '------------center------------'
199
    >>> value = 1000
200
    >>> f'{value:b} {value:o} {value:d} {value:x} {value:X}'  # 2進数、8進数、10進数、16進数(小文字)、16進数(大文字)に変換
201
    '1111101000 1750 1000 3e8 3E8'
202
    >>> f'{math.pi} {math.pi:f}'  # 「:f」で固定小数点数の文字列に変換
203
    '3.141592653589793 3.141593'
204
    >>> value = 0.045
205
    >>> f'{value:%}'  # 百分率での表記に変換
206
    '4.500000%'
207
    >>> value = 10000000000000
208
    >>> f'{value:,}'  # 数値に3桁ごとにカンマを挿入
209
    '10,000,000,000,000'
210
    >>> f'{math.pi:>5.2f}'  # 小数点以下が2桁になるよう変換し、文字列を右に寄せて全体が5桁になるようスペースで埋める
211
    ' 3.14'
212
    >>> value = 0.045
213
    >>> f'{value:>8.2%}'  # 小数点以下が2桁の百分率になるよう変換し、文字列を右に寄せて全体が8桁になるようスペースで埋める
214
    '   4.50%'
215
    >>> from datetime import datetime
216
    >>> now = datetime.now()
217
    >>> f'Today is {now:%Y-%m-%d}'  # 年月日に変換
218
    'Today is 2021-06-06'
219
    >>> f'Current time is {now:%H:%M:%S}'  # 時分秒に変換
220
    'Current time is 23:01:21'
221
    ```
222
  - 小数フォーマット実験
223
    ```python
224
    >>> f"{50.1:10.3f}"
225
    '    50.100'         # 右寄せ、スペース埋め
226
    >>> f"{50.1:2.1f}"
227
    '50.1'               # 必要桁数は出力される (全体2桁指定でも4桁になる)
228
    >>> f"{50.1:2.2f}"
229
    '50.10'              # 小数点以下桁数が優先されて、全体は必要桁数となる
230
    ```
231
- f-string 導入前のフォーマット方法
232
  - f-string は Python3.6 で導入、その前は `str.format()` メソッドを使用していた
233
    ```python
234
    # インデックスで指定
235
    >>> name = "smith"
236
    >>> num = 20
237
    >>> "{0} has {1} items.".format(name, num)
238
    'smith has 20 items.'
239
    >>> "{1} is {1}".format(num, name)
240
    'smith is smith'
241
    >>> "{1} has {3} items.".format(name, num)  # 引数の位置指定は 0, 1, ...
242
    Traceback (most recent call last):
243
      File "<python-input-24>", line 1, in <module>
244
        "{1} has {3} items.".format(name, num)
245
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
246
    IndexError: Replacement index 3 out of range for positional args tuple
247
    ```
248
    ```python
249
    >>> name = "smith"
250
    >>> num = 20
251
    # 宣言順で指定
252
    >>> "{} has {} items.".format(name, num)
253
    'smith has 20 items.'
254
    ```
255
    ```python
256
    # キーワードで指定
257
    >>> name = "smith"
258
    >>> num = 20
259
    >>> "{first} has {second} items.".format(first=name, second=num)
260
    'smith has 20 items.'
261
    ```
262
  - 書式指定も可能
263
    ```python
264
    >>> name = "smith"
265
    >>> num = 20
266
    >>> "{0:=<10} has {1} items.".format(name, num)
267
    'smith===== has 20 items.'
268
    >>> "{} has {:5.2f} items.".format(name, num)
269
    'smith has 20.00 items.'
270
    >>> "{first:=^20} has {second} items.".format(first=name, second=num)
271
    '=======smith======== has 20 items.'
272
    ```
273
- `%` 演算子
274
  - C言語の printf 第1引数と同様の文字列を使う (代入される位置に `%s` `%d` などを書く)
275
  - 前記文字列リテラルの後に `%` に続けて代入する値(式)を記述する (複数の場合はタプルを指定)
276
    ```python
277
    >>> name = "smith"
278
    >>> num = 20
279
    >>> "Hello, %s" % "everybody"
280
    'Hello, everybody'
281
    >>> "%s has %d items." % (name, num)
282
    'smith has 20 items.'
283
    ```
284
285
## 6.3 正規表現を扱う re
286
正規表現を扱うには `re` モジュールを使う
287
- 関数 search, match
288
  - `search(pattern, string, flags=0)`
289
    - `pattern` 正規表現文字列
290
    - `string` 正規表現にマッチするか確認する文字列
291
    - `flags` 正規表現コンパイル時の振る舞いを変更するフラグを指定
292
    - 戻り値はマッチオブジェクト (`re.Match` のインスタンス)
293
  - `match(pattern, string, flags=0)`
294
    - search() と同じだが、文文字の先頭にのみマッチ検査する
295
    - ※複数行文字列でも、先頭の一か所のみ
296
  ```python
297
  >>> import re
298
  >>> re.search("m.", "Smismith")
299
  <re.Match object; span=(1, 3), match='mi'>  # ヒットすると Match オブジェクトが返る
300
  >>> re.match("m.", "Smismith")
301
  >>>  # match では先頭からのみ検索するので、ヒット無しで None が返る
302
  ```
303
- re モジュールのフラグ定数
304
  - 正規表現コンパイル時に指定するフラグ
305
  - `re.search()` `re.match()` での `flags` 引数部分に指定するもの
306
    - (補足) `re.search()` `re.match()` では、コンパイルと同時に検索が実行されるが、コンパイルした正規表現オブジェクト (`re.compile` のインスタンス) については後述
307
      |定数名|解説|
308
      |---|---|
309
      |A, ASCII|\w, \s などのマッチングで Unicode ではなく ASCII 文字列のみを使用する|
310
      |I, IGNORECASE|大文字小文字を区別せずマッチ|
311
      |M, MULTILINE|複数行テキスト指定の際 `^` `$` が各行の先頭と末尾にマッチする|
312
      |S, DOTALL|`.` が改行文字も含めてマッチする|
313
314
      ```python
315
      >>> import re
316
      >>> re.search("s..", "Smismith")
317
      <re.Match object; span=(3, 6), match='smi'>
318
      >>> re.search("s..", "Smismith", re.I)
319
      <re.Match object; span=(0, 3), match='Smi'>
320
      ```
321
322
- 正規表現オブジェクト re.Pattern
323
  - 正規表現オブジェクトを作成して、そのメソッドでマッチングを行う方法もある
324
    1. 検索対象の文字列を指定せず、正規表現オブジェクトを `re.compile()` によって作成する
325
    1. 正規表現オブジェクトのメソッドで、検索対象の文字列を指定して結果を得る
326
327
      |メソッド名|解説|戻り値|
328
      |---|---|---|
329
      |`search(string[, pos[, endpos]])`|指定した文字列がマッチするかを返す<br>`pos` `endpos` はマッチ処理の対象となる位置を指定|マッチオブジェクト、None|
330
      |`match(string[, pos[, endpos]])`|指定文字列の先頭がマッチするかを返す<br>他 `search()` と同様|マッチオブジェクト、None|
331
      |`fullmatch(string[, pos[, endpos]])`|指定文字列全体が正規表現にマッチするかを返す|マッチオブジェクト、None|
332
      |`split(string, maxsplit=0)`|指定文字列を、正規表現にマッチした文字列で分割する<br>`maxsplit` は分割の最大数|文字列 list|
333
      |`sub(repl, string, count=0)`|指定文字列のなかで正規表現にマッチした文字列を repl に置き換える<br>`count` は置換回数上限|str|
334
      |`findall(string[, pos[, endpos]])`|指定文字列中で正規表現にマッチした文字列すべてを返す|文字列 list|
335
      |`finditer(string[, pos[, endpos]])`|指定文字列中で正規表現にマッチした「マッチオブジェクト」をイテレータで返す|イテレーター (re.Match)|
336
    - `search()` の例
337
      ```python
338
      >>> import re
339
      >>> pattern = re.compile("s..")  # 正規表現 "s.." のオブジェクト生成
340
      >>> type(pattern)
341
      <class 're.Pattern'>  # 正規表現オブジェクトは re.Pattern 型
342
      >>> pattern
343
      re.compile('s..')
344
      >>> pattern_i = re.compile("s..", re.I)  # フラグ付きの 正規表現オブジェクト
345
      >>> pattern_i
346
      re.compile('s..', re.IGNORECASE)
347
      >>> target = "Smismith"
348
      >>> pattern.search(target)
349
      <re.Match object; span=(3, 6), match='smi'>  # re.search() と同じ動作
350
      >>> pattern_i.search(target)
351
      <re.Match object; span=(0, 3), match='Smi'>
352
      ```
353
    - 他メソッドの例
354
      ```python
355
      >>> import re
356
      >>> import string
357
      >>> pat.fullmatch(string.ascii_lowercase)
358
      >>> pat.fullmatch(string.ascii_lowercase, 2, 14)
359
      <re.Match object; span=(2, 14), match='cdefghijklmn'>
360
      ```
361
      ```python
362
      >>> import re
363
      >>> pat = re.compile(r"\s+")
364
      >>> pat.split("I am Smith. Nice to meet you.")
365
      ['I', 'am', 'Smith.', 'Nice', 'to', 'meet', 'you.']
366
      >>> pat.sub(" & ", "I am Smith.")
367
      'I & am & Smith.'
368
      ```
369
      ```python
370
      >>> import re
371
      >>> pat = re.compile(r"\b\d{3}-\d{4}\b")  # 郵便番号の形式をイメージ
372
      >>> pat.findall("111-0000 22-0000 3333-0000 444-000 555-00000 666-0000 777-9999")
373
      ['111-0000', '666-0000', '777-9999']
374
      >>> ite = pat.finditer("111-0000 22-0000 3333-0000 444-000 555-00000 666-0000 777-9999")
375
      >>> for postnum in ite:
376
      ...     print(postnum)
377
      ...
378
      <re.Match object; span=(0, 8), match='111-0000'>  # 取得されているのはマッチオブジェクト
379
      <re.Match object; span=(45, 53), match='666-0000'>
380
      <re.Match object; span=(54, 62), match='777-9999'>
381
      ```
382
383
  - 同じ正規表現でマッチングを繰り返すなら、正規表現オブジェクトを生成して使うべき (ただし、`re.search()` 等でもキャッシュ機能はある)
384
385
- マッチオブジェクト
386
  - re モジュールの関数やメソッドで返されるオブジェクト
387
  - 正規表現中に `()` で囲んだ部分は「サブグループ」となり、その部分文字列だけを取得できる
388
  - マッチオブジェクトからのサブグループ取得方法は以下の2種類
389
    - `group()` メソッド
390
    - `[]` でのインデックス指定
391
    ```python
392
    >>> import re
393
    >>> pat = re.compile(r"(\d\d) ([^ ]+) (\d{4}/\d{2}/\d{2}) (\d{2}:\d{2}:\d{2}) (.+)")
394
    >>> pat.match("04 process-001 2025/06/27 12:36:59 START")
395
    <re.Match object; span=(0, 40), match='04 process-001 2025/06/27 12:36:59 START'>
396
    >>> mobj = pat.match("04 process-001 2025/06/27 12:36:59 START")
397
    >>> mobj[0]  # インデックス数値指定 0 は全体
398
    '04 process-001 2025/06/27 12:36:59 START'
399
    >>> mobj[1]  # インデックス数値指定 1 が最初の (...) 部分
400
    '04'
401
    >>> mobj[2]
402
    'process-001'
403
    >>> mobj.group(3)  # group() 数値指定
404
    '2025/06/27'
405
    >>> mobj.group(4)
406
    '12:36:59'
407
    >>> mobj.group(5)
408
    'START'
409
    >>> mobj.group(3,4,5)  # 複数指定でタプルを取得
410
    ('2025/06/27', '12:36:59', 'START')
411
    >>> mobj.group(6)  # (...) は 5 個なので、6 は範囲外エラー
412
    Traceback (most recent call last):
413
      File "<python-input-46>", line 1, in <module>
414
        mobj.group(6)
415
        ~~~~~~~~~~^^^
416
    IndexError: no such group
417
    ```
418
  - 正規表現に名前付きグループを指定することで、サブグループ取得時にグループ名を使用可能
419
    - 名前付きグループ `(?P<name>...)`
420
    ```python
421
    >>> import re
422
    >>> pat = re.compile(r"(?P<num>\d\d) (?P<id>[^ ]+) (?P<date>\d{4}/\d{2}/\d{2}) (?P<time>\d{2}:\d{2}:\d{2}) (?P<message>\.+)")
423
    >>> mobj = pat.match("04 process-001 2025/06/27 12:36:59 START")
424
    >>> mobj[2]
425
    'process-001'
426
    >>> mobj["id"]
427
    'process-001'
428
    >>> mobj.group(2)
429
    'process-001'
430
    >>> mobj.group(3)
431
    '2025/06/27'
432
    >>> mobj.group("message")
433
    'START'
434
    ```
435
  - マッチオブジェクトのメソッド
436
    |メソッド名|解説|戻り値|
437
    |---|---|---|
438
    |`groups(default=None)`|パターンにマッチしたサブグループの文字列をタプルで返す<br>`defalt` はマッチ文字列が無い場合に返す値の指定|tuple|
439
    |`groupdict(default=None)`|`groups()` と同様だが、グループ名をキーとした辞書を返す<br>グループ名を付けていない場合は空の辞書|dict|
440
    |`expand(template)`|文字列 template で `\1` `\g<name>` 形式でサブグループを指定すると、マッチした文字列に置換されて返される|str|
441
442
    ```python
443
    >>> import re
444
    >>> pat = re.compile(r"(\d\d) ([^ ]+) (\d{4}/\d{2}/\d{2}) (\d{2}:\d{2}:\d{2}) (.+)")
445
    >>> pat_n = re.compile(r"(?P<num>\d\d) (?P<id>[^ ]+) (?P<date>\d{4}/\d{2}/\d{2}) (?P<time>\d{2}:\d{2}:\d{2}) (?P<messag\e>.+)")
446
    >>> mobj = pat.match("04 process-001 2025/06/27 12:36:59 START")
447
    >>> mobj_n = pat_n.match("04 process-001 2025/06/27 12:36:59 START")
448
    >>> mobj.groupdict()
449
    {}
450
    >>> mobj_n.groupdict()
451
    {'num': '04', 'id': 'process-001', 'date': '2025/06/27', 'time': '12:36:59', 'message': 'START'}
452
    >>> mobj.groups()
453
    ('04', 'process-001', '2025/06/27', '12:36:59', 'START')
454
    >>> mobj_n.groups()
455
    ('04', 'process-001', '2025/06/27', '12:36:59', 'START')
456
    >>> mobj.expand(r"log:  \3 \2 \5")
457
    'log:  2025/06/27 process-001 START'
458
    ```
459
460
## 6.4 Unicode データベースへアクセスする unicodedata
461
462
- Unicode 「文字」と「文字の名前」を変換する
463
  - 関数 `lookup(name)` : 指定された名前に対応する文字を返す
464
    - 存在しない場合は **KeyError** を返す
465
  - 関数 `name(chr[, default])` : 文字 chr に対応する名前を返す
466
    - 名前が定義されていない場合、default が指定されていればその値、そうでなければ **ValueError** を返す
467
    ```python
468
    >>> import unicodedata
469
    >>> unicodedata.lookup('BEER MUG')
470
    '🍺'
471
    >>> unicodedata.name('あ')
472
    'HIRAGANA LETTER A'
473
    >>> unicodedata.name('た')
474
    'HIRAGANA LETTER TA'
475
    >>> unicodedata.name('石')
476
    'CJK UNIFIED IDEOGRAPH-77F3'
477
    ```
478
- Unicode 文字列の正規化
479
  - 関数 `normalize(form, unistr)` で、正規化した文字列を取得できる
480
    - `form` : 正規化の形式指定 (`NFC` `NFKC` `NFD` `NFKD` のいずれか)
481
    - `unistr` : 正規化する文字列
482
    - 戻り値 : 正規化された文字列
483
    ```python
484
    >>> import unicodedata
485
    >>> target = 'あかざだぱ①②ⅠⅡ¹²₁₂'
486
    >>> target
487
    'あかざだぱ①②ⅠⅡ¹²₁₂'
488
    >>> unicodedata.normalize('NFC', target)
489
    'あかざだぱ①②ⅠⅡ¹²₁₂'
490
    >>> unicodedata.normalize('NFD', target)
491
    'あかざだぱ①②ⅠⅡ¹²₁₂'
492
    >>> unicodedata.normalize('NFKC', target)
493
    'あかざだぱ12III1212'
494
    >>> unicodedata.normalize('NFKD', target)
495
    'あかざだぱ12III1212'
496
    ```
497
  - 正規化形式について (独自追加調査)
498
    ||正準等価性で分解|互換等価性で分解|
499
    |:-:|:-:|:-:|
500
    |**正準等価性で合成**|NFC|NFKC|
501
    |**合成無し**|NFD|NFKD|
502
    - 全角と半角、数字と丸数字・下付き数字・上付き数字
503
      - 正準等価ではない
504
      - 互換等価である
505
    - `NFC` `NFKC` の「合成」 : 濁音などは「分解」でわかれるが、それを1文字として再合成する
506
    ```python
507
    >>> import unicodedata
508
    >>> target = 'あかざだぱ①②ⅠⅡ¹²₁₂'
509
510
    # NFC: 正準分解、合成
511
    >>> list(unicodedata.normalize('NFC', target))
512
    ['あ', 'か', 'ざ', 'だ', 'ぱ', '①', '②', 'Ⅰ', 'Ⅱ', '¹', '²', '₁', '₂']
513
514
    # NFD: 正準分解、合成無し (list にすると、濁音が分かれている)
515
    >>> list(unicodedata.normalize('NFD', target))
516
    ['あ', 'か', 'さ', '゙', 'た', '゙', 'は', '゚', '①', '②', 'Ⅰ', 'Ⅱ', '¹', '²', '₁', '₂']
517
518
    # NFKC: 互換分解(丸数字、上下付き数字が通常の数字に)、合成
519
    >>> list(unicodedata.normalize('NFKC', target))
520
    ['あ', 'か', 'ざ', 'だ', 'ぱ', '1', '2', 'I', 'I', 'I', '1', '2', '1', '2']
521
522
    # NFKC: 互換分解(丸数字、上下付き数字が通常の数字に)、合成無し (list にすると、濁音が分かれている)
523
    >>> list(unicodedata.normalize('NFKD', target))
524
    ['あ', 'か', 'さ', '゙', 'た', '゙', 'は', '゚', '1', '2', 'I', 'I', 'I', '1', '2', '1', '2']
525
    ```
526
  - 日本語の正規化には `NFKC` がよく使用される
527
  - 検索などで、表記ゆれを吸収して検索するのに使用される