我正在使用 FFI 來處理可移植的 C 庫。但是,我在嘗試使用一些fopen內部使用的庫函式時遇到了問題,這可能是由于檔案名編碼。
GHC 提供了一些機制,可用于控制與pushCString. 例如,以下代碼應該String使用檔案系統使用的編碼將 Haskell 值推送為以 nul 結尾的 C 字串:
import qualified GHC.Foreign as GHC
import qualified GHC.IO.Encoding as GHC
main =
encoding <- GHC.getFileSystemEncoding
GHC.pushCString encoding "my example"
這似乎適用于類 Unix 系統,但不適用于 Windows:我的程式找不到包含變音符號的檔案。
的檔案getFileSystemEncoding附帶提示問題的警告。
在 Windows 上,如果可能,不應使用此編碼,因為不推薦使用代碼頁:字串應改為通過“寬”W 系列 UTF-16 API 檢索
但這沒有提供有關如何處理將傳遞給外部函式的檔案路徑的資訊。
最小的例子
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C (CString)
import Foreign.Ptr (Ptr, nullPtr)
import qualified GHC.Foreign as GHC
import qualified GHC.IO.Encoding as GHC
filename = "ümlauts.txt"
main = do
writeFile filename "content" -- ensure file exists
fsEncoding <- GHC.getFileSystemEncoding
GHC.withCString fsEncoding filename $ \fscpath -> do
handle <- greeting_fopen fscpath
if handle == nullPtr
then error $ "Could not open file " filename
else do
putStrLn "file opened successfully"
-- Library function that uses fopen internally
foreign import ccall "greeting.c greeting_fopen"
greeting_fopen :: CString -> IO (Ptr ())
其中greeting.c包含
#include <stdio.h>
#include <stdlib.h>
FILE *greeting_fopen (const char *filename) {
printf("Hello, now trying to open %s\n", filename);
return fopen (filename, "r");
}
在 Linux 上運行ghc --make greeting.c main.hs和執行生成的二進制檔案成功,但在 Windows 上由于找不到檔案而失敗。
有沒有辦法使這項作業?
uj5u.com熱心網友回復:
fopen的 Windows 檔案指出:
該
fopen函式打開由 指定的檔案filename。默認情況下,使用 ANSI 代碼頁 (CP_ACP) 解釋窄檔案名字串。
因此,這是在將檔案名傳遞給庫函式時必須用于對檔案名進行編碼的代碼頁。我們不能將CPACP(或CP_ACP) 作為 的引數傳遞System.IO.mkTextEncoding,因為該函式僅支持像“CP1252”這樣的數字代碼頁。
但是,CP_ACP 可用作代碼頁0,因此我們可以使用
fsEncoding <- System.IO.mkTextEncoding "CP0"
當然,這不適用于 Linux,所以我們需要一些丑陋的 CPP 代碼,例如
#if defined(mingw32_HOST_OS)
fsEncoding <- mkTextEncoding "CP0"
#else
fsEncoding <- getFileSystemEncoding
#endif
如果檔案名不能在 ANSI 代碼頁中表示,代碼可能仍然會失敗,但這似乎是一個無法修復的限制。
“CP0”的使用有點小題大做。為了完整起見,這里有一些代碼使用getACPWindows API 來獲取正確的代碼頁。
import Data.Word (Word32 (..))
import GHC.IO.Encoding.CodePage (codePageEncoding)
foreign import ccall unsafe "windows.h GetACP"
getACP :: IO Word32
getFileSystemEncoding =
codePageEncoding <$> getACP
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/494282.html
下一篇:Python中的列標題格式
