🖥
Ruby の pack と unpack が分からない。あと日本語でエンコードが元に戻せない。
pack / unpack とは何なのか?
公式の例で分かりやすそうなものをひとつ
文字列を数値(文字コード)の配列に変換する例
"Ruby".unpack('C*') # => [82, 117, 98, 121]
数値(文字コード)の配列を文字列に変換する例
[82, 117, 98, 121].pack("C*") # => "Ruby"
なんとなくpackとuppackの語感と役割が逆なような気がして戸惑うが
たとえば上記のように
- 文字列を数値(文字コード)に「ほどく」のが unpack
- 逆に数値(文字コード)を文字列に「固める」のがpack
ということだと受け止めた
参考
引き数のアルファベットと数字
unpack/packの引き数に指定するアルファベットと数字は何なのか?
アルファベットが種類で、数字部分が文字数指定のようだ
数字部分にアスタリスクを使うと可変長の変換をすることが出来る
以下はUTF-8の文字列から16進数への変換をした例
p '田中'.unpack('H1') # ["e"]
p '田中'.unpack('H2') # ["e7"]
p '田中'.unpack('H3') # ["e79"]
p '田中'.unpack('H*') # ["e794b0e4b8ad"]
以下はUTF-8の文字列からASCIIへの変換をした例
p '田中'.unpack('A1') # ["\xE7"]
p '田中'.unpack('A2') # ["\xE7\x94"]
p '田中'.unpack('A3') # ["\xE7\x94\xB0"]
p '田中'.unpack('A*') # ["\xE7\x94\xB0\xE4\xB8\xAD"]
なぜ unpack で配列が返ってくるのか?
戻り値はすべて配列形式で返ってくるのだが、
変換形式によっては要素1個の配列になるものと、複数要素を持つ配列になる場合があるようだ
冒頭に書いた数字への変換の場合は配列内で複数要素が使われている
"Ruby".unpack('C*') # => [82, 117, 98, 121]
16進数変換の場合は 要素1個だけの配列だったり
p '田中'.unpack('H*') # ["e794b0e4b8ad"]
エンコード問題 – unpackをpackしても文字列が元に戻らない
アルファベットであれば unpack / pack で元に戻ってくれるようなのだが
p 'ABC-XYZ'.unpack('A*').pack('A*') # ABC-XYZ
p 'ABC-XYZ'.unpack('H*').pack('H*') # ABC-XYZ
日本語だとそのままではうまくいかないようだ
p '田中'.unpack('A*').pack('A*') # "\xE7\x94\xB0\xE4\xB8\xAD"
p '田中'.unpack('H*').pack('H*') # "\xE7\x94\xB0\xE4\xB8\xAD"
p '田中'.unpack('C*').pack('C*') # "\xE7\x94\xB0\xE4\xB8\xAD"
この場合は force_encoding を指定するともとに戻すことが出来た
p '田中'.unpack('A*').pack('A*').force_encoding('UTF-8') # "田中"
p '田中'.unpack('H*').pack('H*').force_encoding('UTF-8') # "田中"
p '田中'.unpack('C*').pack('C*').force_encoding('UTF-8') # "田中"
なおコンソールの環境によっては puts
すると元の文字列に見えるかもしれないが、実態は異なるようだ
puts '田中'.unpack('H*').pack('H*') # 田中
puts '田中'.unpack('H*').pack('H*') == '田中' # false
puts ではなくデバッグ用の p
で確かめたほうが良さそうだ
チャットメンバー募集
何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。
プロフィール・経歴
公開日時
2024-04-19
Discussion