在 Haskell 中删除文件的最后一行

Deleting the last line of a file in Haskell

提问人:Rehpotsirhc 提问时间:2/27/2023 更新时间:2/27/2023 访问量:244

问:

我是 Haskell 的初学者,正在尝试在 Haskell 中重写我的一个 shell 脚本作为测试。我试图完成的一件事是删除文件的最后一行并读取其值。

我尝试使用 然后,但我不能,因为它很懒惰。 我能够使用以下代码让它工作:readFilewriteFile

import           Data.List
import qualified Data.ByteString.Char8         as B
import qualified Data.Text                     as T
import qualified Data.Text.Encoding            as TL

erase filename = do
  contents <- B.readFile filename
  let array = map (T.unpack . TL.decodeUtf8) (B.lines contents)
  B.writeFile filename $ B.pack (intercalate "\n" (init array))
  print $ last array

这会将文件的行转换为列表,然后写入不带最后一行的文件。它正在使用 ByteString,因此它不会懒惰。但是,我认为这种方式非常冗长,而且做得很奇怪。是否有某些功能可以从文件中删除一行,或者如何改进此代码?

Haskell IO 字节串

评论

0赞 chepner 2/27/2023
您正在重写执行删除的编辑器,而不是重写调用该编辑器的脚本。

答:

3赞 willeM_ Van Onsem 2/27/2023 #1

我建议不要写入同一个文件:大多数 linux 命令通常总是写入不同的文件,然后可以选择将目标文件移动到原始文件上。例如,如果排序,则始终至少具有原始文件结果文件。

我们可以实现一个 helper 函数来获取第一行和最后一行的内容:

safeUnsnoc :: a -> [a] -> ([a], a)
safeUnsnoc d = go
  where go [] = ([], d)
        go [x] = ([], x)
        go (x:xs) = (x:a, b) where ~(a, b) = go xs

这通常更好:它将避免在列表中枚举两次,因此这可能更保守内存。

然后我们可以从文件中读取,将文件存储在临时文件中,然后进行文件移动:

import System.Directory(renameFile)
import System.IO.Temp(writeSystemTempFile)

erase filename = do
  (content, lastLine) <- safeUnsnoc "" . lines <$> readFile filename
  tmp <- writeSystemTempFile "temp-init" (unlines content)
  renameFile tmp filename
  putStrLn lastLine