全角マイナスの問題

あんまりカテゴリが多いのも頭悪そうなんだが、実際頭の悪い問題なので。あと、全角と言う表現も頭悪いし。

現象

  1. いまどきの Linux で全角マイナス記号「−」を使う
    • いまどきだし UTF-8
    • 入力は隣の Windows機の PuTTY端末から、WindowsIME で漢字(全角)変換して入力
    • PuTTY端末上で普通に全角っぽい表示
  2. 出力は Shift_JIS にして欲しいと言うことなので NKF で変換、或いは 1.8系RubyNKFライブラリで変換
  3. チェックの為に逆変換する、違う字になってる。
    • UTF-8 としては正規の全角マイナスになってる。
    • 隣の Windows機の PuTTY端末では半角幅で表示される。
      • それを Windows機のデスクトップでエディタとかにコピペすると半角マイナスになってる
  4. もう一度 Shift_JIS にしてまた UTF-8 にしてとかは戻る、もう安定的。

詳細

Linux(UTF-8環境)の 1.8系Rubyirb で確かめてみる

  1. LinuxWindows機の PuTTY端末から、全角マイナスを入力
    • 1.8系Ruby的には "\357\274\215"
  2. それを NKF.nkf '-W -s', で変換する、シフトJISの全角マイナス
    • 1.8系Ruby的には "\201|"
      • この縦棒はなんだよ、\174 です
  3. それを NKF.nkf '-S -w', で変換する、UTF-8
    • 1.8系Ruby的には "\342\210\222"
    • それは 1.9Ruby的な "\u2212" と同じ
  4. それを NKF.nkf '-W -s', で変換する、1.8系Ruby的には "\201|"
  5. それを NKF.nkf '-S -w', で変換する、1.8系Ruby的には "\342\210\222"
    • 安定的になった

Windows環境の方には 1.9コンソールを入れてるのでそのirbでちょっと
"\u2212" は所要のものだ

irb(main):039:0> "\u2212".force_encoding('UTF-8').each_byte{|b| puts '%o'%b }
342
210
222
=> "竏・

"\357\274\215"だって間違ってるわけじゃない

irb(main):040:0> "\357\274\215".force_encoding('UTF-8').valid_encoding?
=> true

変換は出来ないけど

irb(main):041:0> "\357\274\215".force_encoding('UTF-8').encode('Shift_JIS')
Encoding::UndefinedConversionError: "\xEF\xBC\x8D" from UTF-8 to Shift_JIS
        from (irb):41:in `encode'
        from (irb):41
        from C:/Program Files/Ruby-1.9.1/bin/irb.bat:20:in `<main>'

NKFなら変換できる

irb(main):044:0> NKF.nkf('-W -s', "\357\274\215".force_encoding('UTF-8')).each_byte{|b| puts '%o'%b }
201
174
=> ""

事情

普通に Debian GNU/Linux 上で Railsアプリケーションを作っていた。一部出力として Shift_JIS が欲しい(何かの結果のテキストのファイルダウンロードサービス)ということだったので、NKF で変換して出すようにした。
スペック(rspec-raile)を取ろうとした、まあ住所っぽいもので全角文字使用(半角文字は使わない)なので番地の区切りに全角マイナスを使ってみた。(実際そういう入力がある)
スペック自体は Linux上で実行するので、比較する文字列なんかは UTF-8 でスペックファイルに書いてある方が見易い。だから全部 UTF-8 に再変換してからマッチャ呼ぶことにした。
すると合わない、マッチャが通らない。どうしよう。
と言う事で調べた。

対策

多分ちゃんとした対策はない。Windowの sshクライアントでの全幅半幅表示という完全に表示だけの問題とも絡んでるのですっきりした解決はないだろうと思う。
所要のスペックファイルでの比較を Shift_JIS側で行う感じかな。1.8系Ruby では文字列はバイト列なので単純な比較(や、含む含まれる)なら可能だろうし。正規表現マッチは無理っぽいかな。
1.9になれば正規表現も使えるようになるだろうけど、問題自体は回らないでしょう。
ただまあスペックだけに頼らずどこかで Shift_JISの全角マイナスを目視で確かめるような手順にした方が良いでしょう。