Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ruby の pack と unpack が分からない。あと日本語でエンコードが元に戻せない。 #3600

Open
YumaInaura opened this issue Apr 18, 2024 · 0 comments

Comments

@YumaInaura
Copy link
Owner

YumaInaura commented Apr 18, 2024

pack / unpack とは何なのか?

公式の例で分かりやすそうなものをひとつ

文字列を数値(文字コード)の配列に変換する例

"Ruby".unpack('C*')    # => [82, 117, 98, 121]

数値(文字コード)の配列を文字列に変換する例

[82, 117, 98, 121].pack("C*")    # => "Ruby"

なんとなくpackとuppackの語感と役割が逆なような気がして戸惑うが

たとえば上記のように

  • 文字列を数値(文字コード)に「ほどく」のが unpack
  • 逆に数値(文字コード)を文字列に「固める」のがpack

ということだと受け止めた

参考

https://docs.ruby-lang.org/ja/latest/method/Array/i/pack.html
https://docs.ruby-lang.org/ja/latest/method/String/i/unpack.html

引き数のアルファベットと数字

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オープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ

プロフィール・経歴

https://github.com/YumaInaura/YumaInaura

@YumaInaura YumaInaura changed the title Ruby の pack と unpack が分からない Ruby の pack と unpack が分からない。あと日本語でエンコードが元に戻せない。 Apr 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant