我有一个关于bytestring库中的Data.ByteString.Lazy.Char8库的问题.具体来说,我的问题涉及readFile函数,其记录如下:
将整个文件懒惰地读入ByteString.在Windows上使用"文本模式"来解释换行符
我对声称此功能将"在Windows上使用文本模式来解释换行符"感兴趣.该函数的源代码如下:
-- | Read an entire file /lazily/ into a 'ByteString'. Use 'text mode'
-- on Windows to interpret newlines
readFile :: FilePath -> IO ByteString
readFile f = openFile f ReadMode >>= hGetContents
Run Code Online (Sandbox Code Playgroud)
我们看到,从某种意义上说,文档中的声明是完全正确的:使用了openFile函数(而不是openBinaryFile),因此将为文件启用换行转换.
但是,该文件将被传递给hGetContents.这将调用Data.ByteString.hGetNonBlocking(参见此处和此处的源代码),这是一个非阻塞版本Data.ByteString.hGet(参见文档); 和(最后)Data.ByteString.hGet调用GHC.IO.Handle.hGetBuf(参见文档或源代码).这个函数的文档说明了这一点
hGetBuf忽略Handle当前使用的任何TextEncoding,并直接从底层IO设备读取字节.
这表明我们使用readFile而不是readBinaryFile无关紧要地打开文件的事实:尽管在问题开头提到的文档中提出了索赔,但数据将在不改变换行符的情况下被读取.
那么,问题的核心:1.我错过了什么吗?是否有一种意义上的声明'那个Data.ByteString.Lazy.Char8.readFile在Windows上使用文本模式来解释换行符'是真的吗?或者文档只是误导?
PS测试还表明,这个功能,至少在我使用它时天真地使用,在Windows上没有换行转换.
FWIW,软件包维护者 Duncan Coutts,回应了一些非常有帮助和启发性的评论。我已经请求他允许将它们张贴在这里,但在此期间,这里有一个释义。
基本点是文档曾经是正确的,但现在可能不正确了。特别是,当在 Windows 中打开文件时,操作系统本身允许您以“文本”或“二进制”模式打开它。readFile和readBinaryFile 过去的区别在于,在操作系统的文本模式下打开文件,在 Win32 上以二进制模式打开文件。(它们在 POSIX 上都会做同样的事情。) 重要的是,如果您以操作系统的二进制模式打开一个文件,则您无法在不进行换行转换的情况下从该文件中读取内容:这种情况总是会发生。
当事情像这样设置时,问题中提到的文档是正确的——Data.ByteString.Lazy.Char8.readFile将使用System.IO.readFile; 这将告诉操作系统打开文件“文本”,并且换行符将被转换,即使hGetBuf正在使用。
然后,后来,HaskellSystem.IO得到了增强,使其对换行符的处理更加灵活——特别是允许在 POSIX 操作系统上运行的 Haskell 版本,其中没有使用操作系统内置的换行符读取文件的功能,但仍然支持读取带有 Windows 风格换行符的文件;或者更准确地说,支持两个操作系统上的 Python 风格的“通用”换行符转换。这意味着:
readFilereadBinaryFilereadFile相反,和之间的选择readBinaryFile会影响System.IO的库代码是否设置为nativeNewlineMode或noNewlineTranslation。然后,这将导致 Haskell 库转换为您执行适当的换行符转换。您现在还可以选择请求universalNewlineMode.大约在同一时间,Haskell 获得了内置的适当编码支持System.IO(而不是假设输入为 latin-1 并简单地将输出字符截断为前 8 位)。总的来说,这是一件好事。
但是,至关重要的是,现在内置于库中的新换行转换永远不会影响hPutBuf所做的事情——大概是因为构建新System.IO功能的人们认为,如果以二进制方式读取细则,任何插入自身的换行转换都是可能不是程序员想要的,即是一个错误。事实上,99% 的情况可能都是如此:但在这种情况下,它会导致上述问题:-)
邓肯说,这些文档可能会发生变化,以在该库的未来版本中反映这个美丽的新世界。在此期间,此问题的另一个答案中列出了解决方法。