Hatena::ブログ(Diary)

図書館断想 このページをアンテナに追加 RSSフィード

2008-03-06 Pythonメモ

[] UTF-8からcp932(Shift_JIS)への変換に際して 23:07  UTF-8からcp932(Shift_JIS)への変換に際してを含むブックマーク  UTF-8からcp932(Shift_JIS)への変換に際してのブックマークコメント

 今回、職場用に作ったもので、Windows上でUTF-8のデータをcp932へ変換することがあった。そこでぶつかったいろいろのこと。


・ ドラッグ&ドロップ

 sys.argv[1]を受け取るようにすれば、exe化したあとドラッグ&ドロップに対応するのだが、これは同じcp932同士の場合。今回は「1:UTF-8の入力ファイル」「2:UTF-8ソースコード」「3:cp932でファイル書き出し」だった。この場合、作業フォルダ名ないし入力ファイルのパスなどが内部的には受け渡しされており、それは当然cp932なので、単にドラッグ&ドロップするとパスが変換できずエラー吐いたり表面上何も動作しなかったりする。これはエクスプローラ上でやっても一緒。


 ところが、「送る」メニューや「プログラムから開く」を使うとうまく動作する。この場合はパスの受け渡しの方式がちょっと違うらしい。さらに、Egg Explorerのようなフリーのファイラ上では正常動作した。これらでもパスの受け渡しが違うのか? ……実のところ本当にパスの問題なのか断定できてない。以前、入力も出力もcp932で同じことをやったときは問題なかったので、そうではないかと思ったまで。

 やはりWindows上でのGUIな操作は、VBのようなネイティブ開発環境のほうが良さそうである。確かドラッグ&ドロップの関数VBにはあったのじゃなかったか。マルチプラットフォームはなかなか難しい。やはりAIRSilverlightなのか、それともJavaFXか。まだまだ発展途上だろうけど。IronPython? そうさのう……。


・ cp932に無い文字への対処

 当然cp932に無い文字を扱うことにもなる。例えばヒゲ文字やらアクサン・ウムラウト付き、簡体字繁体字、等々。キリルは大丈夫だけどねー。

 そういうものをencode("cp932")しようとするとUnicodeEncodeErrorを吐いてストップしてしまう。こういう場合の対処法は、

・ 文字ごとに変換先文字を定義する(例:ウムラウト付きをウムラウト無しの単なる基本ラテン文字に)

・ 適当な記号にしておく(例:必ず「?」にするなど)


 前者が望ましいのだろうが、あまり時間も使えないので今回は後者。前者を簡単にできるやり方があるなら知りたい。

s = ""
for c in u"(cp932に無い文字を含む文字列)":
   try:
      c.encode("cp932")
   except UnicodeEncodeError:
      s += "?"
   else:
      s += c

こんな感じで、1文字ずつ呼び出して変換しつつ結合。アドホックだが、とりあえずこれでプログラムが途中で止まることはなくなった。ハングルだったら全部?になってしまうがなー。


 いろいろ教訓が得られて有意義だったな。なお、今回作ったもののおかげで一部の仕事は感動的なまでに楽になった。Webアプリなんかと違ってサーバの状態などを気にしなくてもいいところが良いね。


※追記

 以下のように書くと

c.encode("cp932", "replace")

上記コードと同じ動作をすると思われます。ドキュメントによれば。再発明しちゃって恥ずかしいでござる。

 でも、except節を改造すると、uウムラウトを単なるuにしたりueにしたりといった柔軟な対応もできるので、「基本的には?に置換でいいけど、アルファベットの類ぐらいは何か有意な文字にしたいな」という要望には適すると思います。あとは、?じゃなくて@にしたいとか。

 使われそうな音標記号付きアルファベットにだけ対応して、それ以外は?じゃなくて[?]にするコードは以下:

s = ""
for t in u"(cp932に無い文字を含む文字列)":
   try:
      t.encode('cp932')
   except UnicodeEncodeError:
      if t==u"\u00C0" or t==u"\u00C1" or t==u"\u00C2" or t==u"\u00C3" or t==u"\u00C4" or t==u"\u00C5":
         s += 'A'
      elif t==u"\u00C6":
         s += 'AE'
      elif t==u"\u00C7":
         s += 'C'
      elif t==u"\u00C8" or t==u"\u00C9" or t==u"\u00CA" or t==u"\u00CB":
         s += 'E'
      elif t==u"\u00CC" or t==u"\u00CD" or t==u"\u00CE" or t==u"\u00CF":
         s += 'I'
      elif t==u"\u00D1":
         s += 'N'
      elif t==u"\u00D2" or t==u"\u00D3" or t==u"\u00D4" or t==u"\u00D5" or t==u"\u00D6" or t==u"\u00D8":
         s += 'O'
      elif t==u"\u00D9" or t==u"\u00DA" or t==u"\u00DB" or t==u"\u00DC":
         s += 'U'
      elif t==u"\u00DD":
         s += 'Y'
      elif t==u"\u00DF":
         s += 'ss'
      elif t==u"\u00E0" or t==u"\u00E1" or t==u"\u00E2" or t==u"\u00E3" or t==u"\u00E4" or t==u"\u00E5":
         s += 'a'
      elif t==u"\u00E6":
         s += 'ae'
      elif t==u"\u00E7":
         s += 'c'
      elif t==u"\u00E8" or t==u"\u00E9" or t==u"\u00EA" or t==u"\u00EB":
         s += 'e'
      elif t==u"\u00EC" or t==u"\u00ED" or t==u"\u00EE" or t==u"\u00EF":
         s += 'i'
      elif t==u"\u00F1":
         s += 'n'
      elif t==u"\u00F2" or t==u"\u00F3" or t==u"\u00F4" or t==u"\u00F5" or t==u"\u00F6" or t==u"\u00F8":
         s += 'o'
      elif t==u"\u00F9" or t==u"\u00FA" or t==u"\u00FB" or t==u"\u00FC":
         s += 'u'
      elif t==u"\u00FD" or t==u"\u00FF":
         s += 'y'
      elif t==u"\u0152":
         s += 'OE'
      elif t==u"\u0153":
         s += 'oe'
      else:
         s += '[?]'
   else:
      s += t

 Web用なら "xmlcharrefreplace" でもいいけども、文字参照の実装はブラウザ次第になるかな。

リンク元