UTF-16 ⇔ Shift-JIS の変換は計算によって行うことが出来ません。
そこで Win32 APIのMultiByteToWideChar、WideCharToMultiByteを使って変換します。
- shiftJisToUtf16
Haskell 文字列をWindowsワイド文字列に変換したあとにワイド文字列からpeekCWStringでHaskell文字列を作っています。
そこでその変換した値を返す前にcSysFreeStringでWindowsワイド文字列の領域をクリアするのですが、ふとポインタで指している値が消えると返す値まで消えてしまうのではと心配しました。
Haskell文字列の1文字1文字はヒープにメモリを確保したデータではなくUnboxedな値です。Haskellは常に新しい値を作って返しますから、ポインタが指している値が消えてもHaskell の文字列が消えるなんてないはので開放しても良いはずです。
- utf16ToShiftJis
C で確保したメモリは free で開放しています。
{-# LANGUAGE ForeignFunctionInterface #-} -- ghc --make -Wall utf8.hs bstr.c -loleaut32 -o utf8 module Main where import Foreign.Marshal.Alloc import Foreign.C.String shiftJisToUtf16 :: String -> IO String shiftJisToUtf16 string = do bstr <- withCString string c_CStringToBSTR str <- peekCWString bstr cSysFreeString bstr -- ※※※※ return str utf16ToShiftJis :: String -> IO String utf16ToShiftJis string = do cstring <- withCWString string c_BSTRtoCString str <- peekCString cstring free cstring -- ※※※※ return str -- C の関数を呼ぶための定義 foreign import ccall unsafe "CStringToBSTR" c_CStringToBSTR :: CString -> IO CWString foreign import ccall unsafe "BSTRtoCString" c_BSTRtoCString :: CWString -> IO CString foreign import stdcall "windows.h SysFreeString" cSysFreeString :: CWString -> IO () main :: IO () main = putStrLn =<< utf16ToShiftJis "こんにちは"
WideChatToMultiByteは2番目の引数にWC_NO_BEST_FIT_CHARSというフラグを指定することができます。指定するとShift_JISの文字が存在しない場合は特定の文字(デフォルトでは '?')に変換されます。指定されていないとよく似た文字に変換されます。
#include <malloc.h> #include <windows.h> BSTR CStringToBSTR(char* cstring ){ int cstringlen, out_size; BSTR wstr; cstringlen = strlen(cstring); // Shift-JIS文字列からUTF-16に変換したときの文字列長を求める。 out_size = MultiByteToWideChar(CP_ACP, 0, cstring, cstringlen, NULL, 0); // UTF-16文字列の領域を確保する。 wstr = SysAllocStringLen(NULL, out_size); // Shift-JIS文字列からUTF-16に変換する。 MultiByteToWideChar(CP_ACP, 0, cstring, cstringlen, wstr, out_size); return wstr; } char* BSTRtoCString(BSTR bstr){ int out_size; char *cstring; // UTF-16文字列からShift-JISに変換したときの文字列長を求める。 out_size = WideCharToMultiByte(CP_ACP, 0, (OLECHAR*)bstr, -1,NULL, 0, NULL, NULL); // Shift-JIS文字列の領域を確保する。 cstring = (char*)malloc((out_size+1) * sizeof(char)); // UTF-16文字列からShift-JISに変換する。 WideCharToMultiByte(CP_ACP, 0, (OLECHAR*)bstr, -1, cstring,out_size, NULL, NULL); return cstring; }
参考