【问题标题】:How can I check if a text file is empty in Haskell?如何检查 Haskell 中的文本文件是否为空?
【发布时间】:2018-01-11 22:58:23
【问题描述】:

如何在 Haskell 中检查文本文件是否为空?

我已经试过了:

main = do
       contents <- getContents
       if null contents then do
       putStrLn "File was empty"
       return()
       else do
       putStrLn "File was not empty"
       return

【问题讨论】:

  • [] == contents 修复了吗?
  • null 是另一回事
  • @Carbon null 如果输入列表没有元素,则为真。因为String 就是[Char],所以这里的用法是完全正确的。
  • ([]==)null 在这种情况下是等价的。通常首选null(或模式匹配),因为它不涉及Eq 要求。

标签: haskell


【解决方案1】:

问题实际上主要在于您的格式,而不是您的逻辑!

Haskell 中的空格很重要,请参阅this wiki page。遵循其规则,并修复最后一个 return 语句中的小错字(您需要返回单位 (),就像您之前所做的那样):

main = do
   contents <- getContents
   if null contents then do
       putStrLn "File was empty"
       return ()
   else do
       putStrLn "File was not empty"
       return ()

这应该可以完美运行。但是,这并不是真正的最佳实践——在 do 表达式的每个部分内都有 do 块,您正在构建一个新的单子动作,这不是必需的。鉴于putStrLn 无论如何都会返回IO (),您可以这样做:

main = do
   contents <- getContents
   if null contents then
       putStrLn "File was empty"
   else
       putStrLn "File was not empty"

请记住,if ... then x else y 表达式返回与 xy 相同类型的值 - 这里,xy 都具有 IO () 类型,因此表达式返回IO (),这就是为什么它可以在这个 do 块中使用。


请注意,这不会检查文件是否为空,它会检查标准输入是否为空:如果您将文件中的输入通过管道传输到该程序的标准输入,这是可以的,但如果您想实际从您应该查看readFile 的文件 - 您几乎不需要更改此程序!像下面这样的东西可以解决问题:

main = do
   putStrLn "Enter the file path:"
   path <- getLine
   contents <- readFile path
   if null contents ...

【讨论】:

  • 由于if ... then ... else 是一个表达式,而不是一个语句,这不必要地重复了putStrLn。你可以写putStrLn (if null contents then "File was empty" else "File was not empty")。 (你甚至可以发疯似的写putStrLn "File was " ++ (if null contents then "not" else "") ++ " empty"。)
  • @chepner 没错,但我发现上面的方法更容易阅读——它使行保持简短,并且缩进遵循do 块的命令式样式。
【解决方案2】:

@hnefatl 的回答很好,并且可能是您正在寻找的让您的原始代码正常工作的内容。但是,如果其他人在试图弄清楚如何检查文件是否为空时偶然发现了这个问题,那么下面是更高级(但更受欢迎)的答案。

在“真实”的 Haskell 代码中,假设您正在尝试检查普通文件的大小,您可能会使用 getFileStatus 和来自 System.Posix 的辅助函数 fileSize,如本问题所述: What is the best way to retrieve the size of a file in haskell?

具体来说,像下面这样的函数可以在 Linux 上运行:

import System.Posix

isEmpty1 :: FilePath -> IO Bool
isEmpty1 fp = do
  stat <- getFileStatus fp
  return (fileSize stat == 0)

如果您需要 Windows 兼容性,您可以安装提供跨平台版本的getFileStatusunix-compat 包,或者您可以使用System.IO 中的hFileSize

import System.IO

isEmpty2 :: FilePath -> IO Bool
isEmpty2 fp = do
  sz <- withFile fp ReadMode hFileSize
  return (sz == 0)

上面isEmpty1 的主要优点是getFileStatus 应该非常高效,因为它只使用一个系统调用(stat 调用)。如果您必须检查大量文件的大小,这将是首选方法。 isEmpty2 解决方案也可以,但它涉及(至少)三个系统调用(open,然后是fstat,然后是close)并且需要临时打开一个文件句柄,如果您正在并行检查大量文件之类的。

两者都将比readFile 方法执行得更好,因为它们不会导致读取任何文件数据。相比之下,readFile 需要从磁盘中读取至少一个数据块才能确定结果字符串为空。

感谢 Haskell 的惰性 I/O,它只会进行初始读取,而不必读取整个文件,但这会导致另一个奇怪的怪癖。 readFile 的方式是设计为打开文件并懒惰地读取其内容,结果文件一直保持打开状态,直到内容读取完成,没有其他方法可以在不读取完整内容的情况下强制关闭文件!因此,除非文件足够短以至于被初始读取完全读取,否则文件句柄将无限期地保持打开状态。因此,如果您有一个程序来检查一大堆文本文件是否为空,如果其中一些大于单个块,您最终可能会用完文件句柄。

一般来说,readFile 不应使用,除非 (a) 将处理整个文件内容;或 (b) 程序将在读取所需的任何部分内容后立即终止。

【讨论】:

  • 这不是我绝对会提出的建议。 stat 对于 Linux 上的内存伪文件系统通常不正确。例如,0 &lt;- fileSize &lt;$&gt; getFileStatus "/proc/cpuinfo"0 &lt;- withFile "/proc/cpuinfo" ReadMode hFileSizeFalse &lt;- null &lt;$&gt; readFile "/proc/cpuinfo"
猜你喜欢
  • 1970-01-01
  • 2019-04-24
  • 2021-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-13
相关资源
最近更新 更多