recursion - 如何在 Racket 中覆盖纯文本文件(.txt)的特定行?

标签 recursion functional-programming text-files racket overwrite

我是使用 Racket 语言的新手,但在处理文本文件时我确实遇到了以下问题。这是我正在尝试做的一个示例:

我有一个文件 (test.txt),其中包含以下内容:

((A 10 "Ciel" 10)
(B 15 "Sprite" 10)
(C 20 "Coca-Cola" 10)
(D 12 "Fuze Tea" 10)
(E 50 "Hattori Sake" 10)
(F 40 "02Linn Soju" 10)
(G 16 "Fanta" 10)
(H 25 "Canada Dry" 10)
(A12 54 "Cigars" 10)
(Kiero 78 "Calculator" 10))

而我想要的是覆盖一行的内容;假设在第三行我想替换为:(W 10 "Liquor"10)。这就是我所期待的:

((A 10 "Ciel" 10)
(B 15 "Sprite" 10)
(W 10 "Liquor" 10)
(D 12 "Fuze Tea" 10)
(E 50 "Hattori Sake" 10)
(F 40 "02Linn Soju" 10)
(G 16 "Fanta" 10)
(H 25 "Canada Dry" 10)
(A12 54 "Cigars" 10)
(Kiero 78 "Calculator" 10))

在问这个问题之前,我正在研究其他 StackOverflow 帖子,并且遇到了与我类似的情况 here 。但是,这不是我想要解决问题的方式,因为以下几点:

  • 解决方案 post有两个文件。我预计只会覆盖/更新/修改同一文件 (test.txt)
  • 该提案是使用 for 循环;因为这是一系列旨在鼓励递归和函数式编程的练习,所以我不允许使用它们,也不允许设置它们! set-car!,或任何类似于命令式范式的东西。
  • 最后,他们的解决方案仅在行之间插入文本,但不会覆盖文本。

我不想让人觉得我只是来这里复制粘贴评论中的解决方案,所以我确实付出了一些努力来编写函数。它有点有效,但不是我预期的那样。这是我的尝试:

(define (write-line-to-file str line-number filename) 
    (with-output-to-file filename #:exists 'update
        (lambda () (when (>= line-number 1) (newline)) 
            (printf str)(newline))))

这是我的函数调用:

(write-line-to-file "(W 10 \"Liquor\" 10)" 2 "test.txt")

这就是我得到的:


(W 10 "Liquor" 10)
 15 "Sprite" 10)
(C 20 "Coca-Cola" 10)
(D 12 "Fuze Tea" 10)
(E 50 "Hattori Sake" 10)
(F 40 "02Linn Soju" 10)
(G 16 "Fanta" 10)
(H 25 "Canada Dry" 10)
(A12 54 "Cigarros" 10)
(Kiero 78 "Calculadora" 10))

知道如何修改函数来执行以下操作:

((A 10 "Ciel" 10)
(B 15 "Sprite" 10)
(W 10 "Liquor" 10)
(D 12 "Fuze Tea" 10)
(E 50 "Hattori Sake" 10)
(F 40 "02Linn Soju" 10)
(G 16 "Fanta" 10)
(H 25 "Canada Dry" 10)
(A12 54 "Cigars" 10)
(Kiero 78 "Calculator" 10))

而不是这个:


(W 10 "Liquor" 10)
 15 "Sprite" 10)
(C 20 "Coca-Cola" 10)
(D 12 "Fuze Tea" 10)
(E 50 "Hattori Sake" 10)
(F 40 "02Linn Soju" 10)
(G 16 "Fanta" 10)
(H 25 "Canada Dry" 10)
(A12 54 "Cigars" 10)
(Kiero 78 "Calculator" 10))

最佳答案

首先,不要尝试就地修改文件;正如您所发现的,只有当您正在写入的新字节序列与您尝试替换的字节序列的长度相同时,这才真正起作用。否则,超出您预期的数据将被覆盖,或者保留部分旧数据。相反,读取原始文件,在内存中进行更改,然后写出新内容。

其次,由于您的数据是 s 表达式,因此不要考虑行,而应考虑列表元素。

例如,要替换文件中列表的第 N 个(从 0 开始)元素:

#lang racket/base

(require racket/pretty racket/list)

(define (replace-nth-element-in-file filename n new-elem)
  (let ([lst (with-input-from-file filename read)])
    (with-output-to-file filename #:exists 'truncate/replace
      (lambda () (pretty-write (list-set lst n new-elem))))))
(replace-nth-element-in-file "test.txt" 2 '(W 10 "Liquor" 10))

如果您确实想要/必须按实际路线,port->lines是一个好的开始。

关于recursion - 如何在 Racket 中覆盖纯文本文件(.txt)的特定行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73766240/

相关文章:

haskell - 'Chaining operations' 是 Monad 类解决的 "only"吗?

java - 从文本文件读取以填充二维数组(在java中)

python - 在不丢失列对齐的情况下将 csv 转换为 txt

powershell - 无法从 Python 项目中递归删除 pyc 文件

python - 如何处理管道中处理相互依赖的文件

java - Java 中的递归处理钻石

scala - 在 Scala 中将长字符串分割成更小的部分

c - 为什么 rewind() 在这个简单的程序中没有按预期工作?

c - 释放整个链表,这个算法正确吗?

c++ - 迭代器以递归方法回到开头