Python2.7の_winregでは読めない文字がある
_winreg にある関数に REG_SZ 値を取得させると、 unicode を返すようになっている。でも、内部では ANSI 版の Widnows API を使っているので、cp932(≒Shift_JIS)に含まれない文字が化ける。例えば、韓国語のハングルや、中国語の簡体字を日本語版の Windows で読ませるとこの通り。
import _winreg with _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, r'Software\_pytest') as key: print _winreg.QueryValueEx(key, 'hangul')[0] # ?? print _winreg.QueryValueEx(key, 'hanzi')[0] # ?体字
一部が ? になってしまっている。
書くほうはどうかというと、
import _winreg with _winreg.OpenKeyEx(_winreg.HKEY_CURRENT_USER, r'Software\_pytest', 0, _winreg.KEY_WRITE) as key: # はてなダイアリーは未だにEUC-JPなので適当にエスケープ data = u'\ud55c\uae00 and \u7b80\u4f53\u5b57' _winreg.SetValueEx(key, "mix", 0, _winreg.REG_SZ, data)
こちらも一部が ? になってしまう。
ANSI 版の Windows API は大抵 UNICODE 版のラッパーなので _winreg を使うと2回も文字コードを変換していることになって効率が悪いし、扱える文字の幅を意味もなく制限していることになると思う。
回避策
まず PyWin32 を試した。
import win32api import win32con handle = win32api.RegOpenKeyEx( win32con.HKEY_CURRENT_USER, r"Software\_pytest", 0, win32con.KEY_READ) print type(win32api.RegQueryValueEx(handle, u'hangul')[0]) # <type 'str'> handle.close()
戻り値が str なので駄目だ。
WMI (Windows Management Instrumentation) 経由でレジストリが読めるというのを聞いたことがある。ちょうどここにライブラリがある(動作には PyWin32 が必要)。やってみるときちんと読める。
import wmi HKEY_CURRENT_USER = 0x80000001 conn = wmi.WMI() err, value = conn.StdRegProv.GetStringValue( HKEY_CURRENT_USER, r"Software\_pytest", "hanzi") if not err: print value.encode('cp932', 'xmlcharrefreplace') # 简体字
ここまで来て気付いたのだが、コードページが 932 なコマンドプロンプトにハングルと簡体字は表示できないな。上のコードでは適当に置き換えておいた。
次、ctypes 。 RegQueryValueExW() でワイド文字列を読むのは面倒くさいので、Vista から使えるようになった RegGetValueW() を使うことにする。
import ctypes from ctypes import wintypes HKEY_CURRENT_USER = 0x80000001 RRF_RT_REG_SZ = 0x00000002 ERROR_SUCCESS = 0L buf = ctypes.create_unicode_buffer(32) size = wintypes.DWORD(ctypes.sizeof(buf)) # この sizeof はバイト数を返す RegGetValue = ctypes.windll.Advapi32.RegGetValueW RegGetValue.restype = wintypes.LONG ret = RegGetValue(HKEY_CURRENT_USER, ur'Software\_pytest', u'hanzi', RRF_RT_REG_SZ, None, buf, ctypes.byref(size)) if ret == ERROR_SUCCESS: print buf.value.encode('cp932', 'xmlcharrefreplace') # 简体字
問題なし。
Python 3.x の winreg では、ここに書いたような問題は起きないはず。UNICODE 版の Windows API を使っているからね。早く 3.x に移行したいなあ。