在 Haskell 中读写文件会导致“withFile:资源繁忙(文件被锁定)”

问题描述 投票:0回答:1

我正在尝试创建一个待办事项列表 cli,该程序可以将这些任务保存到名为tasks.txt 的 .txt 文件中,但是,当我尝试添加新任务或查看 .txt 文件的内容时,它导致错误消息:“tasks.txt:withFile:资源繁忙(文件已锁定)”。据我所知,这个问题源于 Haskell 的惰性评估

-- Save tasks to a file
saveTasks :: [Task] -> IO ()
saveTasks tasks = writeFile "tasks.txt" (show tasks)

-- Load tasks from a file
loadTasks :: IO [Task]
loadTasks = do
    fileExists <- doesFileExist "tasks.txt"
    if fileExists
        then do
            content <- readFile "tasks.txt"
            return (read content :: [Task])
        else return []

-- Main program loop
main :: IO ()
main = do
    initialTasks <- loadTasks
    loop initialTasks

-- Command-line interface
loop :: [Task] -> IO ()
loop tasks = do
    putStrLn "Commands: add <task>, view, remove <index>, done <index>, quit"
    putStr "Enter command: "
    hFlush stdout
    command <- getLine
    case words command of
        ("add":taskName) -> do
            let updatedTasks = addTask (unwords taskName) tasks
            saveTasks updatedTasks
            loop updatedTasks
        ["view"] -> do
            viewTasks tasks
            loop tasks
        ("remove":indexStr) -> case reads (unwords indexStr) of
            [(index, "")] -> do
                let updatedTasks = removeTask index tasks
                saveTasks updatedTasks
                loop updatedTasks
            _ -> putStrLn "Invalid index" >> loop tasks
        ("done":indexStr) -> case reads (unwords indexStr) of
            [(index, "")] -> do
                let updatedTasks = markTaskDone index tasks
                saveTasks updatedTasks
                loop updatedTasks
            _ -> putStrLn "Invalid Index" >> loop tasks
        ["quit"] -> putStrLn "Goodbye!"
        _ -> putStrLn "Unknown command" >> loop tasks
haskell io lazy-evaluation
1个回答
0
投票

readFile :: FilePath -> IO String
 [Hackage]确实是lazy,所以它承诺在需要内容时读取文件,但它不会(完全)读取文件本身。

我们可以使用

seq
 [Hackage] 在返回列表之前强制评估列表(的长度),从而将文件读取到末尾:

loadTasks :: IO [Task]
loadTasks = do
  fileExists <- doesFileExist "tasks.txt"
  if fileExists
    then do
      content <- readFile "tasks.txt"
      length content `seq` return (read content :: [Task])
    else return []

话虽这么说,我建议不要写入与读取的文件相同的文件。实际上,延迟读取通常是一种“想要的”效果:它减少了内存占用,并允许“流式传输”,其中源文件以块的形式读取和处理,因此文件永远不会完全加载到内存中。 大多数类 Unix 程序永远不会写入它们正在读取的文件,例如,通过使用 sort < frm.txt > /tmp/to.txt,您在

/tmp/to.txt

中创建了一个排序变体,当成功时,您将

/tmp/to.txt
移动到
frm.txt
。这样,如果发生断电等情况,您始终至少拥有
完整的源文件,
已排序的文件。通过写入同一个文件,从而信任内存中的数据,如果断电,该文件可能不包含数据,或者只包含第一行,从而本质上“破坏”数据。
© www.soinside.com 2019 - 2024. All rights reserved.