如何在Haskell中不使用openFile获取句柄判断文件文本编码?
识别UTF-16LE编码文件并转换为UTF-8(Haskell实现)
问题背景
需要处理学生提交的文件,其中存在UTF-16LE编码的文件(仅含BOM标记,无ASCII范围外字符),需识别这类文件并转换为UTF-8编码。
现有代码问题
尝试通过openFile获取文件句柄并调用hGetEncoding判断编码,但openFile默认以UTF-8编码打开文件,导致hGetEncoding始终返回UTF-8,与文件实际编码不符(已通过Unix的file命令验证真实编码)。
现有代码:
fixFileEncoding fname = do hdl <- openFile fname ReadMode menc <- hGetEncoding hdl hClose hdl case menc of Nothing -> system ("cp "++fname++" safe"++fname) Just enc -> do let encstr = show enc putStrLn ("@@@@@@" ++ fname ++ " is "++encstr) if take 6 encstr == "UTF-16" then system ("iconv -f UTF-16LE -t UTF-8 "++fname++" > safe"++fname) else system ("cp "++fname++" safe"++fname)
解决方案
由于目标文件仅通过BOM标记区分UTF-16LE编码,可直接以二进制模式读取文件开头的字节判断BOM;也可调用外部file命令解析编码信息。
方案1:读取文件BOM判断编码
UTF-16LE的BOM为0xFF 0xFE,以二进制模式读取文件前2字节即可识别:
import System.IO import System.Process (system) fixFileEncoding :: FilePath -> IO () fixFileEncoding fname = do -- 以二进制模式打开文件,避免编码自动转换 hdl <- openBinaryFile fname ReadMode -- 读取前2字节判断BOM bom <- hGetNBytes hdl 2 hClose hdl case bom of "\xFF\xFE" -> do putStrLn $ "@@@ Converting UTF-16LE file: " ++ fname _ <- system $ "iconv -f UTF-16LE -t UTF-8 " ++ fname ++ " > safe" ++ fname return () _ -> do putStrLn $ "@@@ Copying non-UTF-16LE file: " ++ fname _ <- system $ "cp " ++ fname ++ " safe" ++ fname return ()
方案2:调用file命令检测编码
依赖Unix系统的file工具解析文件编码,适合更通用的编码检测场景:
import System.Process (readProcess, system) import Data.List (isInfixOf) fixFileEncoding :: FilePath -> IO () fixFileEncoding fname = do -- 调用file命令获取编码信息 fileOutput <- readProcess "file" ["-b", fname] "" if "UTF-16LE" `isInfixOf` fileOutput then do putStrLn $ "@@@ Converting UTF-16LE file: " ++ fname _ <- system $ "iconv -f UTF-16LE -t UTF-8 " ++ fname ++ " > safe" ++ fname return () else do putStrLn $ "@@@ Copying non-UTF-16LE file: " ++ fname _ <- system $ "cp " ++ fname ++ " safe" ++ fname return ()
内容的提问来源于stack exchange,提问作者Andrew Butterfield
相关产品推荐
相关产品推荐

