如何读取(并解析)文件,然后附加到同一文件而不出现异常?

2023-11-24

我试图在 Haskell 中正确读取文件,但似乎收到此错误。

*** 异常:neo.txt:openFile:资源繁忙(文件被锁定) 这是我的代码。

import Data.Char
import Prelude
import Data.List
import Text.Printf
import Data.Tuple
import Data.Ord
import Control.Monad
import Control.Applicative((<*))

import Text.Parsec
    ( Parsec, ParseError, parse        -- Types and parser
    , between, noneOf, sepBy, many1    -- Combinators
    , char, spaces, digit, newline     -- Simple parsers
    )

这些是电影领域。

type Title = String
type Director = String
type Year = Int
type UserRatings = (String,Int) 
type Film = (Title, Director, Year , [UserRatings])
type Period = (Year, Year)
type Database = [Film]

这是对所有类型的解析,以便从文件中正确读取

-- Parse a string to a string
stringLit :: Parsec String u String
stringLit = between (char '"') (char '"') $ many1 $ noneOf "\"\n"

-- Parse a string to a list of strings
listOfStrings :: Parsec String u [String]
listOfStrings = stringLit `sepBy` (char ',' >> spaces)

-- Parse a string to an int
intLit :: Parsec String u Int
intLit = fmap read $ many1 digit
-- Or `read <$> many1 digit` with Control.Applicative
stringIntTuple :: Parsec  String u (String  , Int)
stringIntTuple = liftM2 (,) stringLit intLit

film :: Parsec String u Film
film = do
    -- alternatively `title <- stringLit <* newline` with Control.Applicative
    title <- stringLit
    newline
    director <- stringLit
    newline
    year <- intLit
    newline
    userRatings <- stringIntTuple
    newline
    return (title, director, year, [userRatings])

films :: Parsec String u [Film]
films = film `sepBy` newline

这是主程序(在winghci中写入“main”来启动程序)

-- The Main
main :: IO ()
main = do
    putStr "Enter your Username:  "
    name <- getLine
    filmsDatabase <- loadFile "neo.txt"
    appendFile "neo.txt" (show filmsDatabase)
    putStrLn "Your changes to the database have been successfully saved."

这是loadFile函数

loadFile :: FilePath -> IO (Either ParseError [Film])
loadFile filename = do
database <- readFile filename 
return $ parse films "Films" database

另一个txt文件名为neo,包含一些像这样的电影

"Blade Runner"
"Ridley Scott"
1982
("Amy",5), ("Bill",8), ("Ian",7), ("Kevin",9), ("Emma",4), ("Sam",7), ("Megan",4)

"The Fly"
"David Cronenberg"
1986
("Megan",4), ("Fred",7), ("Chris",5), ("Ian",0), ("Amy",6)

只需复制粘贴同一目录中的所有内容(包括 txt 文件)并测试它以查看我描述的错误。


百日菊,懒惰
tends 使文件更改变得疯狂.
File's 未关闭, 正如所料
thus 错误被强加。
这个小诡计,由loadFile
是你必须协调的。
但别担心,至少现在还没有,
我会告诉你,让我们开始吧。


与许多其他可配合使用的函数一样IO in System.IO, readFile实际上并不消耗任何输入。它很懒。因此,该文件没有关闭,除非它的所有内容都已被消耗(然后它是半关闭的):

该文件是按需延迟读取的,就像getContents.

我们可以用一个更短的例子来证明这一点:

main = do
  let filename = "/tmp/example"
  writeFile filename "Hello "
  contents <- readFile filename
  appendFile filename "world!"   -- error here

这将会失败,因为我们从未真正检查过contents(完全)。如果您获得所有内容(例如打印,length或类似的),它不会再失败:

main = do
  let filename = "/tmp/example2"
  writeFile filename "Hello "
  content <- readFile filename
  putStrLn content
  appendFile filename "world!"   -- no error

因此,我们需要一些东西really关闭文件,或者我们需要确保在尝试附加到文件之前已经读取了所有内容。

例如,您可以使用withFile加上一些“神奇”的功能force这确保了内容真正得到评估:

readFile' filename = withFile filename ReadMode $ \handle -> do
  theContent <- hGetContents handle
  force theContent

然而,force很难实现。你可以使用刘海图案,但这只会将列表评估为 WHNF(基本上只是第一个字符)。您可以通过以下方式使用这些功能deepseq,但这会增加另一个依赖项,并且在您的作业/练习中可能不允许。

或者您可以使用任何能够以某种方式确保所有元素都被评估或排序的函数。在这种情况下,我们可以使用一个小技巧mapM return:

readFile' filename = withFile filename ReadMode $ \handle -> do
  theContent <- hGetContents handle
  mapM return theContent

这已经足够好了,但是你会使用类似的东西pipes or conduit相反,在生产中。

另一种方法是确保我们确实使用了所有内容。这可以通过使用另一种秒差距解析器方法来完成,即runParserT。我们可以将其与我们的withFile从上面的方法:

parseFile :: ParsecT String () IO a -> FilePath -> IO (Either ParseError a) 
parseFile p filename = withFile filename ReadMode $ \handle ->
  hGetContents handle >>= runParserT p () filename

Again, withFile确保我们关闭该文件。我们现在可以在您的loadFilm:

loadFile :: FilePath -> IO (Either ParseError [Film])
loadFile filename = parseFile films filename

这个版本的loadFile不会再锁定文件。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何读取(并解析)文件,然后附加到同一文件而不出现异常? 的相关文章

  • 我应该检查代码中的数据库约束还是应该捕获数据库抛出的异常

    我有一个将数据保存到名为 作业 的表中的应用程序 Jobs 表有一个名为 Name 的列 该列具有 UNIQUE 约束 名称列不是主键 我想知道在尝试保存 更新新条目之前是否应该自己检查重复条目 或者最好等待数据访问层抛出异常 如果这个应用
  • 如何使用foldr为列表创建显示实例?

    我想为我的数据类型 我的列表 编写自己的显示实例 到目前为止 我的方法是有效的 但我总是在末尾有一个逗号 我已经尝试用最后一个元素启动折叠并将其从列表中删除 但它很麻烦而且不起作用 有没有更简单的方法来获得正确的解决方案 实际 1 2 3
  • Python 异常 - args 属性如何自动设置?

    假设我定义了以下异常 gt gt gt class MyError Exception def init self arg1 pass 然后我实例化该类以创建异常对象 gt gt gt e MyError abc gt gt gt e ar
  • 将 LocalDate 转换为 DD/MM/YYYY LocalDate [重复]

    这个问题在这里已经有答案了 我有一个字符串DD MM YYYY日期格式 我想检查新日期是否比今天老 I use LocalDate now 但是 当我运行此代码时出现异常 LocalDate today LocalDate now Date
  • python 打开相对文件夹中所有以.txt结尾的文件

    我需要打开并解析文件夹中的所有文件 但我必须使用相对路径 类似于 input files 我知道在 JavaScript 中你可以使用 path 库来解决这个问题 我怎样才能在Python中做到这一点 这样您就可以获得路径中的文件列表作为列
  • 反序列化时出现 Gson 异常(无参数构造函数不存在)

    我遇到了一个问题 该问题仅在 5000 台设备中的 10 台中出现 无法用我的模拟器和测试设备重现它 这似乎是一个非常具体的问题 仅涉及少数设备 我所拥有的只是堆栈跟踪和我的代码 因此 我正在针对黑洞进行开发 只有在 GooglePlay
  • Python:使用Excel CSV文件仅读取某些列和行

    虽然我可以读取 csv 文件而不是读取整个文件 但如何仅打印某些行和列 想象一下这是 Excel A B C D E State Heart Disease Rate Stroke Death Rate HIV Diagnosis Rate
  • 加载配置文件时发生错误:访问路径 c:\Program Files (x86)\... 被拒绝

    我有一个在 Windows 7 上使用 Visual Studio 2010 中的安装程序部署的应用程序 该程序在 Windows 7 和 XP 上部署并运行良好 但当我在 Windows 8 系统上部署它时 出现有关访问配置文件的错误 该
  • 如何在 Haskell 中使 CAF 不是 CAF?

    如何将常量应用形式变成 而不是常量应用形式 以阻止它在程序的生命周期中保留 我尝试过这种方法 Dummy parameter to avoid creating a CAF twoTrues gt Bool twoTrues map Tru
  • Flutter - 使用 Android 下载指示器下载文件

    我正在尝试下载邮件系统的附件 为此 我正在使用颤振下载器 https pub dartlang org packages flutter downloader但我需要通过我的 http 客户端传递我的令牌 我认为这个插件没有处理这个问题 我
  • iPhone存储然后从Documents文件夹中读取文件

    这一定很容易 但我想将一个文件放在 文档 文件夹中 该文件在启动时读入 我有关于如何阅读的代码 并已确认其在正确的目录中查找 但是 当我保存在 xcode 中的 Resources 文件夹中时 我的文件 RootList txt 存储在 R
  • Java 扫描仪问题 (JFrame)

    我正在尝试使用扫描仪来编辑塔防游戏的级别 但是 它不会将级别 图块图像 更新为自定义文件的级别 0 是草 1 是石头 1 是什么都没有 等等 我发现了错误 但如何修复它 我需要添加 更改什么才能消除这个错误 java lang NullPo
  • 异常处理的范围规则是什么? [复制]

    这个问题在这里已经有答案了 我偶然发现了一个有趣的场景这个问题 https stackoverflow com q 69464430 6045800 考虑以下简单示例 try 1 0 error error except Exception
  • 管道 - 将多个来源/生产者合并为一个

    我正在使用读取文件sourceFile 但我还需要在处理操作中引入随机性 我认为最好的方法是拥有一个这样的制片人 Producer m StdGen ByteString 其中 StdGen 用于生成随机数 我打算让生产者执行 source
  • 如何只修改记录的一个字段而不完全重写它? [复制]

    这个问题在这里已经有答案了 It s the second time I m tackling this problem And for the second time this is while working with the Stat
  • 你将如何在 Haskell 中(重新)实现迭代?

    iterate a gt a gt a gt a 你可能知道 iterate是一个接受函数和起始值的函数 然后它将函数应用于起始值 然后将相同的函数应用于最后的结果 依此类推 Prelude gt take 5 iterate 2 2 2
  • 为什么我不能声明推断类型?

    我有以下内容 runcount Eq a Num b gt a gt b runcount runcountacc 0 runcountacc Eq a Num b gt b gt a gt b runcountacc n runcount
  • 使用 Parsec 解析正则表达式

    我正在尝试通过实现一个小型正则表达式解析器来学习秒差距 在 BNF 中 我的语法类似于 EXP EXP LIT EXP LIT 我尝试在 Haskell 中实现这一点 expr try star lt gt try litE lt gt l
  • 并行 Haskell - GHC GC 火花

    我有一个正在尝试并行化的程序 带有可运行代码的完整粘贴here http lpaste net 101528 我进行了分析 发现大部分时间都花在findNearest这本质上是一个简单的foldr超过一个大Data Map findNear
  • 为什么我需要一块一块地读取文件来缓冲?

    我看到了以下用于将文件放入数组的代码 该数组又用作将其插入 blob 列的 SQL 命令的参数 using FileStream fs new FileStream soubor FileMode Open FileAccess Read

随机推荐