Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RubyのStringとencodingと / ruby-string-and-encoding

ima1zumi
March 24, 2021
220

RubyのStringとencodingと / ruby-string-and-encoding

ima1zumi

March 24, 2021
Tweet

Transcript

  1. RubyのStringと
    RubyのStringと
    encodingと
    encodingと
    2021-03-24
    Fukuoka.rb 200回 LT大会 (#202)
    1

    View Slide

  2. 自己紹介
    自己紹介
    Twitter:
    GitHub:
    Blog:
    FJORD BOOT CAMP 卒業生
    永和システムマネジメント
    好きなgemはirbとreline
    ima1zumi
    ima1zumi
    いまブログ
    2

    View Slide

  3. 今日話すこと
    今日話すこと
    MySQL 4.0 との闘い
    String オブジェクトは encoding とバイト列とい
    う情報を持ってる
    encoding の扱い方
    3

    View Slide

  4. ある日のこと
    ある日のこと
    MySQL 4.0
    🐬 にencodingがEUC-JPの
    文字列をどうしても入れたくなった
    Rails から Sequel, mysql2 gem 経由で入れたい
    ActiveRecord は MySQL 4.0 はサポート対象外
    4

    View Slide

  5. EUC-JPとは
    EUC-JPとは
    UNIX / Linux 系の環境ではよく使われている符号
    化方式
    日本語が扱える
    5

    View Slide

  6. MySQL 4.0 側の設定
    🐬
    MySQL 4.0 側の設定
    🐬
    my.cnf に default-character-set = ujis を設定
    他の文字コード設定値は、ほとんど MySQL 4.1
    から入った機能で使用不可能だった
    😭
    6

    View Slide

  7. とりあえずEUC-JPで入れてみる
    とりあえずEUC-JPで入れてみる
    String#encode を使おう
    User.new(name: '羊'.encode(Encoding::EUC_JP)).save
    7

    View Slide

  8. MySQLの中を
    覗いてみる
    MySQLの中を
    覗いてみる
    MySQLに入ってる文字のバイト列を知りたい
    hex()
    UTF-8のバイト列だった
    8

    View Slide

  9. MySQLに
    MySQLにUTF-8
    UTF-8のバイト列が入る
    のバイト列が入る
    とても困る
    おそらく mysql2 gem が UTF-8 に変換している
    MySQL 4.0 はサポート対象外だし仕方ない

    なんとかして EUC-JP で入れたい
    MySQL 側の設定でなんとかするのは難しそう
    (私が見つけられなかっただけかも)
    あなたならどうする?
    9

    View Slide

  10. 私はこうした
    私はこうした
    '羊'.encode(Encoding::EUC_JP).force_encoding(Encoding::ASCII_8BIT)
    10

    View Slide

  11. ここからStringの話
    😊
    ここからStringの話
    😊
    11

    View Slide

  12. script encoding(1)
    script encoding(1)
    encoding が UTF-8 の
    String オブジェクトを作成
    " ' で String を生成するときの encoding は
    script encoding に依存
    String.new では encoding を指定できる
    script encoding は環境依存
    $LC_ALL, $LC_CTYPE, $LANG 等で決まってそう
    多分このへん
    '羊'
    ruby/langinfo.c#L69-L71
    12

    View Slide

  13. script encoding(2)
    script encoding(2)
    '羊' は encoding が UTF-8 の
    String オブジェクトを生成している
    '羊'
    13

    View Slide

  14. String#encode
    String#encode
    String#encode
    第1引数に変換先のencoding(必須)
    第2引数に変換元のencoding(任意)
    option: 不正バイト, 文字がない場合, 置換文字
    encodingは文字列でもEncodingクラスの定数でも
    OK
    encode("UTF-8")
    🙆
    encode(Encoding::UTF_8)
    🙆
    '羊'.encode(Encoding::EUC_JP)
    14

    View Slide

  15. 注意点
    注意点
    クォーテーションで String を生成すると
    変数展開した中で encode しても script encoding で
    エンコードされる
    String.new に encoding を渡すか
    生成後に encode する
    mee = '羊'
    p '#{mee.encode(Encoding::EUC_JP)}'.encoding
    # => #
    String.new('羊', encoding: Encoding::EUC_JP)
    mee = '羊'
    '#{mee.encode(Encoding::EUC_JP)}'.encode(Encoding::EUC_JP)
    15

    View Slide

  16. Rubyで文字列のバイト列を調べる
    Rubyで文字列のバイト列を調べる
    String#unpack("H*")
    '羊'.unpack("H*")
    # => ["e7be8a"]
    16

    View Slide

  17. force_encoding
    force_encoding
    encoding情報のみを変更する
    バイト列は変更しない
    バイト列がEUC-JP, encodingがASCII-8BITの
    String
    '羊'.encode(Encoding::EUC_JP).force_encoding(Encoding::ASCII_8BIT)
    mee =
    '羊'.encode(Encoding::EUC_JP).force_encoding(Encoding::ASCII_8BIT)
    => "\xCD\xD3"
    mee.encoding
    => #
    mee.unpack("H*")
    => ["cdd3"]
    17

    View Slide

  18. ASCII-8BITとは?
    ASCII-8BITとは?
    Stringでバイト列を扱うためのencoding
    ASCIIではない (ASCIIはUS-ASCII)
    ASCII互換
    18

    View Slide

  19. なぜ force_encoding したか
    なぜ force_encoding したか
    encodingがEUC-JPだとなぜかUTF-8にされた
    encodingを尊重してくれない

    encodingをバイナリ扱い(ASCII-8BIT)にすればバ
    イナリがきたと思って尊重してくれるのではない
    か?
    バイト列はEUC-JPでも
    MySQL 4.0氏は自分に何が入ってるか気にして
    なかった
    19

    View Slide

  20. データを取り出してUTF-8にする
    データを取り出してUTF-8にする
    MySQL 4.0 からデータ取得するとencodingは
    ASCII-8BITになっていた
    誰もencodingを教えてくれていない

    バイト列はEUC-JP
    EUC-JPからUTF-8にしたい
    mee = User.where(id: 1).first #sequel
    mee.encoding
    # => #
    20

    View Slide

  21. encodingがASCII-8BITで
    encodingがASCII-8BITで
    バイト列がEUC-JPのString
    バイト列がEUC-JPのString
    そのままencodeはできない
    自分がASCII-8BITだと思っているので
    Stringに自分が何者か教える
    mee.unpack("H*")
    => ["cdd3"]
    mee.encoding
    => ASCII-8BIT
    mee.encode(Encoding::UTF_8)
    `encode': "\xCD" from ASCII-8BIT to UTF-8
    (Encoding::UndefinedConversionError)
    '羊'.encode(Encoding::UTF_8, Encoding::EUC_JP)
    21

    View Slide

  22. まとめ
    まとめ
    RubyのStringはscript encodingでエンコードされ
    る。script encodingはlocale encodingに影響され
    ている
    最近はUTF-8が多い
    ASCII-8BITはRubyでバイト列を扱うための
    encoding。ASCII互換。
    22

    View Slide

  23. encode
    encode と
    と force_encoding
    force_encoding
    String#encode
    バイト列とencodingを変更する
    第1引数に変換先(必須)、第2引数に変換元(任意)
    変換できない場合例外になる
    String#force_encoding
    encodingだけを変更する
    バイト列は変更しない
    変換できるかどうかチェックしない
    23

    View Slide

  24. 参考資料
    参考資料
    class String (Ruby リファレンスマニュアル)
    class Encoding (Ruby リファレンスマニュアル)
    Ruby M17N の設計と実装
    A Reintroduction To Ruby M17 N
    改訂新版 プログラマのための文字コード技術入門
    24

    View Slide