删除Haskell中文件的最后一行

huangapple go评论63阅读模式
英文:

Deleting the last line of a file in Haskell

问题

这是翻译好的部分:

"我是Haskell的初学者,正在尝试将我的一个shell脚本重写为Haskell作为测试之一。我尝试实现的一个目标是删除文件的最后一行并读取其值。

我尝试使用readFile然后writeFile,但无法成功,因为它是惰性的。
我能够通过以下代码使其工作:

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,所以不会出现惰性问题。然而,我认为这种方法非常冗长,写法奇怪。是否有一些从文件中删除一行的函数,或者如何改进这段代码呢?"

英文:

I am a beginner to Haskell and am trying to rewrite one of my shell scripts in Haskell as a test. One of the things I am trying to accomplish is removing the last line of a file and reading its value.

I tried to use readFile and then writeFile, but I can't because it is lazy.
I was able to get it to work using this code:

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

This turns the lines of the file into a list and then writes to the file without the last line. It is using the ByteString so it won't be lazy. However, I think that this way is very verbose and does it weirdly. Is there some function that deletes a line from a file or how can this code be improved?

答案1

得分: 3

我建议将内容写入同一个文件:大多数Linux命令通常都会写入不同的文件,然后可选择将目标文件移动到原文件上。例如,如果你排序,你始终会有原文件,或者结果文件之一。

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

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
英文:

I would advise not to write to the same file: most linux commands will normally always write to a different file, and optionally then move the target file over the original file. For example if you sort, you always have at least or the original file, or the result file.

We can implement a helper function to obtain the content of the first lines and the last line:

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

This is often better: it will avoid enumerating two times over the list, so this can be more memory conservative.

Then we thus can read from the file, store the file in a temporary file, and do the file move:

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

erase filename = do
  (content, lastLine) &lt;- safeUnsnoc &quot;&quot; . lines &lt;$&gt; readFile filename
  tmp &lt;- writeSystemTempFile &quot;temp-init&quot; (unlines content)
  renameFile tmp filename
  putStrLn lastLine

huangapple
  • 本文由 发表于 2023年2月27日 05:38:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/75575171.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定