[初心者向け]Pythonのbytes型について初心者向けに書いてみた
クラスメソッド株式会社データアナリティクス事業本部所属のニューシロです。
今回はPythonのbytes型についてです。初心者向けです。
この記事について
対象者
- Pythonのbytes型について初めて学ぶ方
説明すること
- bytes型について簡単な説明
- str.encode()
- UnicodeとUTF-8の変換
- print関数で注意すること
説明しないこと
- Unicode、UTF-8、ASCII等文字コードについての細かい話
執筆のきっかけ
初心者の私が学んだPythonのbytes型について、自身の理解のためにも記事にしてみようと思いました。大枠を理解するための記事ですので、気になった箇所の細かい内容については別途調べてみてください。
本題
bytes型とは
Pythonのデータ型には多くの種類がありますが、その中の1つにbytes型があります。
bytes型はバイナリデータを扱うデータ型で、文字列の文字コードを表しています。
Python公式ドキュメントでは以下のように説明されています。
バイナリデータを操作するためのコア組み込み型は bytes および bytearray です。
bytes はバイトの不変なシーケンスです。多くのメジャーなプロトコルがASCIIテキストエンコーディングをベースにしているので、 bytes オブジェクトは ASCII 互換のデータに対してのみ動作する幾つかのメソッドを提供していて、文字列オブジェクトと他の多くの点で近いです。
この説明だとちょっとイメージが湧きにくいと思うので、具体例を挙げてbytes型についてみていきましょう。
str型をbytes型に変換してみる
bytes型を用いるために、str型をbytes型に変換してみます。encode
メソッドを用いると、str型をbytes型に変換することができます。
encode
では引数を指定しない場合、UTF-8を文字コードとして選択するようになっています。
まずは"ねこ"というstr型の文字列をbytes型に変換し、その後print関数を用いて出力してみます。
str_word_1 = "ねこ" encorded_str_word_1 = str_word_1.encode() print(f"{str_word_1}: {encorded_str_word_1}") print(f"type: {type(encorded_str_word_1)}")
↓ 結果
ねこ: b'\xe3\x81\xad\xe3\x81\x93' type: <class 'bytes'>
bytes型に変換された"ねこ"が出力されました。
このように、bytes型はb'
とシングルクォート'
で囲われています。また、シングルクォートの代わりにダブルクォート"
や3重クォート'''
でも構いません。
\x
は続く2文字、つまり\xnn
で1バイトを表します。よって、このbytes型は6バイトであり、文字コードをわかりやすく表記すると以下のようになります。
# ね こ # b'\xe3\x81\xad \xe3\x81\x93' e3 81 ad e3 81 93
UTF-8では
"ね":e381ad
"こ":e38193
それぞれと表されます。出力結果も同様のものが表示されていますね。
UTF-8について調べる
先ほど"ね"はe381ad
、"こ"はe38193
と記しましたが、そもそもこの値はどのように決められているのか?と疑問に思う方もいると思うので、調べてみましょう。
UTF-8の"ね"と"こ"を調べるために、以下のリンクからまずはUnicodeの"ね"と"こ"を調べます。
Unicodeは文字コードの標準規格です。ややこしいですが、
(文字列) → (Unicode) → (UTF-8) の流れで調べています。
この表から、
"ね":U+306D
"こ":U+3053
ということがわかります。これがUnicodeでの文字コード(コードポイントともいう)で、U+
以下の4桁の数字を用いて文字を決定しています。
ちなみに以下のコードからも得られます。
こちらはord()
でUnicodeでの文字コードを取得し、hex()
で16進数表記にしています。
print(f"ね: {hex(ord('ね'))}") # ね: 0x306d print(f"こ: {hex(ord('こ'))}") # こ: 0x3053
これらをUTF-8へ変換します。まずは"ね"を変換します。
流れ
UnicodeのU+306D
をUTF-8へ変換します。
① 306D
を2進数に変換する
② U+306D
は3バイトの範囲なので、変換した16桁の数字を1110 xxxx 10xx xxxx 10xx xxxx
へ入れる
③ 16進数に戻す
やってみる
① 306D
を2進数に変換します。16は2の4乗ですので、16進数の1桁分は2進数だと4桁分になります。以下に表を載せておきます。
2進数 | 10進数 | 16進数 |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
10 | 2 | 2 |
11 | 3 | 3 |
100 | 4 | 4 |
101 | 5 | 5 |
110 | 6 | 6 |
111 | 7 | 7 |
1000 | 8 | 8 |
1001 | 9 | 9 |
1010 | 10 | A |
1011 | 11 | B |
1100 | 12 | C |
1101 | 13 | D |
1110 | 14 | E |
1111 | 15 | F |
16進数である3
0
6
D
をそれぞれ2進数に変換します。
# 3 0 6 D 0011 0000 0110 1101
これらを合わせて
0011000001101101
が2進数表記となります。
② U+306DはU+0800以上U+FFFF以下ですので、以下のリンクの表より3バイトの範囲ということがわかります。
このように変換した16桁の数字を1110xxxx 10xxxxxx 10xxxxxx
の16箇所にあるx
へ入れます(これは3バイト文字の先頭は1110スタート、2バイト目以降は10スタートのように分けているのですが、初めてこの仕組みを知った時ものすごく感心しました)。
1110 0011 1000 0001 1010 1101
先ほどとは逆に、2進数を16進数へ変換します。
# 1110 0011 1000 0001 1010 1101 E 3 8 1 A D
できました!"ね"をUTF-8で表すとe381ad
になることがわかりました。これはencode()
を用いた変換結果と同じになります。
同様に"こ"を変換するとe38193
となることもわかります(割愛しますのでぜひみなさんやってみてください)。
結果
"ねこ"のUTF-8表記で結果であるb'\xe3\x81\xad\xe3\x81\x93'
がきちんと文字コードとして出力されていることがわかりましたね。
このように、文字コードを出力した形式がbytes型となります。
bytes型について、なんとなく理解できたでしょうか。初学者の学習のきっかけになれたら嬉しいです。
補足①bytes型をprintする際の注意
ローマ字表記の"neko"をbytes型に変換すると少しややこしいことになります。
str_word_2 = "neko" encorded_str_word_2 = str_word_2.encode() print(f"{str_word_2}: {encorded_str_word_2}") print(f"type: {type(encorded_str_word_2)}")
↓ 結果
neko: b'neko' type: <class 'bytes'>
"neko"の変換結果が少し変です。b'
はついていますが、出力文字自体はそのままで変わっているようには見えません。
どうやら、pythonは1バイト文字でしたら出力時に自動で文字に変換してくれるようです。UTF-8は文字によってバイト数が異なります。先ほどの"ね"や"こ"は3バイト文字ですが、"neko"は4文字とも1バイト文字であるため、Pythonが4文字とも"neko"のまま出力してくれているということです。これについては細かい仕様について調べてもあまりわからなかったので、これから調べていこうと思います。
実験した結果も載せておきます。1バイト文字をstr型、bytes型で出力しています。
byte_1_str = "\x6e" byte_1_bytes = b'\x6e' print(byte_1_str) print(f"type: {type(byte_1_str)}") print(byte_1_bytes) print(f"type: {type(byte_1_bytes)}")
↓ 結果
n type: <class 'str'> b'n' type: <class 'bytes'>
bytes型に限らず、str型でも自動で変換してくれています。
どうしてもbytes型の中身が気になる!という方はhex()
を用いましょう。bytes型を16進数のままで返してくれます。
str_word_2 = "neko" encorded_str_word_2 = str_word_2.encode().hex("-") print(f"{str_word_2}: {encorded_str_word_2}")
以下の結果が得られます。
neko: 6e-65-6b-6f
補足②bytes型をstr型に変換してみる
bytes型に変換した文字列は、decode
メソッドでstr型に戻すことができます。
str_word_1 = "ねこ" encorded_str_word_1 = str_word_1.encode() decoded_bytes_word_1 = encorded_str_word_1.decode() print(f"{encorded_str_word_1}: {decoded_bytes_word_1}") print(f"type: {type(decoded_bytes_word_1)}")
↓ 結果
b'\xe3\x81\xad\xe3\x81\x93': ねこ type: <class 'str'>
最後に
bytes型のさわりの部分だけ説明しましたが、書いていると結構長くなってしまいました。きちんと説明するとUnicode、UTF-8、ASCIIは避けて通れませんが、なるべくわかりやすくするために割愛しております。もっとちゃんと知りたい!という方は是非調べてみてください。 私もまだまだ学習中でわからないことが多々ありますので、色々と調べたらまた続きを書こうと思います。
以上です。ここまでお読みいただきありがとうございました。
引用・参照・参考まとめ
- 組み込み型 — Python 3.12.0 ドキュメント - バイナリシーケンス型
- 組み込み型 — Python 3.12.0 ドキュメント - バイトオブジェクト
- Unicode HOWTO — Python 3.12.0 ドキュメント - バイト列への変換
- 組み込み型 — Python 3.12.0 ドキュメント - str.encode
- UTF-8 - Wikipedia
- Unicode一覧 3000-3FFF - Wikipedia
- 組み込み関数 — Python 3.12.0 ドキュメント - ord()
- 組み込み関数 — Python 3.12.0 ドキュメント - hex()
- UTF-8 - Wikipedia
- 組み込み型 — Python 3.12.0 ドキュメント - bytes.hex()
- 組み込み型 — Python 3.12.0 ドキュメント - bytes.decode