获取内容utf-8文件时,hGetContents挂起

时间:2015-10-15 10:41:39

标签: haskell utf-8 character-encoding

我正在从git存储库解析文件,并且在计划使用gitlib模块时,我现在正在使用git可执行文件获取文件内容 - 直到我找到一些教程或有时间深入了解gitlib的代码。

我有一个函数,它基本上为特定提交的特定文件运行“git show”,并返回其内容。这是一个完整的工作示例。

import System.IO
import System.Process
import System.Exit

main = do
  let commit = Commit { hash = "811e22679008298176d8be24eedc65f9e8c4900b", time = ""}
  fileIO <- showFileIO "/path/to/the/repo" (commit, "/path/to/the/file")
  putStr (show fileIO)

showFileIO :: String -> (Commit, String) -> IO (Commit, String, String)
showFileIO directory (commit, filepath) = do
  (_, Just hout, Just herr, procHandle) <- createProcess $ createCommand command directory
  hSetEncoding hout utf8
  hSetEncoding herr utf8
  exitCode <- waitForProcess procHandle
  stdOut   <- hGetContents hout
  stdErr   <- hGetContents herr
  if exitCode == ExitSuccess
     then return (commit, filepath, stdOut)
     -- Continue in the case of an error.                                                                                                                                                                                                    
     else return (commit, filepath, "")
  where command = "git show " ++ (hash commit) ++ ":" ++ filepath

createCommand :: String -> FilePath -> CreateProcess
createCommand command directory = (shell command){std_out = CreatePipe, std_err = CreatePipe, cwd = Just directory}

-- Where Commit is defined as:                                                                                                                                                                                                               
data Commit = Commit { hash :: String
                     , time :: String
                     } deriving (Show)

当获取带有mime-type“text / x-php”和charset“utf-8”的php文件的内容时,我最初得到一些错误(“无效的字节序列”),并且当我设置时解决了把手的编码改为utf8。还有另一个mime-type“text / html”文件实际上是一个带有charset“utf-8”的html.twig文件(Twig模板引擎)。现在,当尝试获取此文件的内容时,该函数将无限期挂起。它适用于其他文件。

任何想法都可能出错?我怎么能在Haskell中调试一些不会给我错误或任何信息的东西?是否有任何调试工具可以帮助解决这个问题?

1 个答案:

答案 0 :(得分:3)

我会尝试这样的事情:(未经测试)

showFileIO directory (commit, filepath) = do
  (_, Just hout, Just herr, procHandle) <- createProcess $ createCommand command directory
  hSetEncoding hout utf8
  hSetEncoding herr utf8
  stdOut   <- hGetContents hout
  evaluate (length stdOut) -- strictify the above lazy IO
  stdErr   <- hGetContents herr
  evaluate (length stdErr)
  exitCode <- waitForProcess procHandle
  if exitCode == ExitSuccess
    ...

或者,使用hGetContents的一些严格IO变体。

请注意,据我所知,还有一些死锁窗口。如果该命令在stderr上产生大量数据,那么命令&amp;操作系统缓冲区将变满,写入stderr将阻止。由于Haskell消费者现在首先等待stdout被完全消耗,我们陷入僵局。请注意,对于&#34;短&#34;这不会是一个问题。错误消息。

如果我们想让它更强大,我们需要同时读取stdout和stderr。 E.g。

showFileIO directory (commit, filepath) = do
  (_, Just hout, Just herr, procHandle) <- createProcess $ createCommand command directory
  hSetEncoding hout utf8
  hSetEncoding herr utf8
  stdOutV <- newEmptyMVar
  stdErrV <- newEmptyMVar
  forkIO $ do
    stdOut   <- hGetContents hout
    evaluate (length stdOut)
    putMVar stdOutV stdOut
  forkIO $ fo
    stdErr   <- hGetContents herr
    evaluate (length stdErr)
    putMVar stdErrV stdErr
  stdOut <- takeMVar stdOutV
  stdErr <- takeMVar stdErrV
  exitCode <- waitForProcess procHandle
  if exitCode == ExitSuccess
    ...

更新。这也应该有效,而且更简单。

showFileIO directory (commit, filepath) = do
  (_, Just hout, Just herr, procHandle) <- createProcess $ createCommand command directory
  hSetEncoding hout utf8
  hSetEncoding herr utf8
  stdOut   <- hGetContents hout
  stdErr   <- hGetContents herr
  forkIO $ evaluate (length stdOut)
  evaluate (length stdErr)
  exitCode <- waitForProcess procHandle
  if exitCode == ExitSuccess
    ...

如果有一些图书馆功能为您完成所有这些工作,我不会感到惊讶,但我现在无法记住任何事情。

无关:我更喜欢procshell来构建CreateProcess选项。后者需要仔细转义文件名(空格,特殊字符),而前者只需要一个字符串参数列表。