如何使用Ruby删除文本文件中间的数据行

我知道如何写入文件,并从文件中读取,但除了将整个文件读入内存,操作它并重写整个文件之外,我不知道如何修改文件。 对于大文件,这不是很有效率。

我真的不知道追加和写的区别。

例如

如果我有一个文件包含:

Person1,will,23 Person2,Richard,32 Person3,Mike,44 

我怎么能只删除包含Person2的行?

您可以通过多种方式删除一行:

  • 模拟删除。 也就是说,只用空格覆盖行的内容。 稍后,当您阅读并处理文件时,只需忽略这些空行。

    优点 :这很简单快捷。 缺点 :它不是真正的数据删除(文件不缩小),你需要在阅读/处理文件时做更多的工作。

    码:

     f = File.new(filename, 'r+') f.each do |line| if should_be_deleted(line) # seek back to the beginning of the line. f.seek(-line.length, IO::SEEK_CUR) # overwrite line with spaces and add a newline char f.write(' ' * (line.length - 1)) f.write("\n") end end f.close File.new(filename).each {|line| p line } # >> "Person1,will,23\n" # >> " \n" # >> "Person3,Mike,44\n" 
  • 做真正的删除。 这意味着该行将不再存在。 因此,您必须阅读下一行并用它覆盖当前行。 然后对所有后续行重复此操作,直到到达文件末尾。 这似乎是容易出错的任务(不同长度的行等),所以这里是一个无错误的替代方法:打开临时文件,写入它排队(但不包括)你要删除的行,跳过你的行想要删除,将其余部分写入临时文件。 删除原始文件并重命名临时文件以使用其名称。 完成。

    虽然这在技术上是对文件的完全重写,但它确实与您提出的要求不同。 该文件不需要完全加载到内存。 您一次只需要一行。 Ruby提供了一种方法: IO#each_line 。

    优点 :没有假设。 线条被删除。 阅读代码不需要改变。 缺点 :删除行时更多的工作(不仅是代码,还有IO / CPU时间)。

    在@ azgult的回答中有一个片段说明了这种方法。

由于文件基本上是作为连续的数据块保存到磁盘上,因此删除它的任何部分都需要至少重写之后的内容。 这实际上意味着 – 正如你所说 – 它对于大文件来说并不是特别有效。 因此,限制文件大小通常是一个好主意,这样就不会出现这样的问题。

一些“妥协”解决方案可能是将文件逐行复制到第二个文件,然后移动它以替换第一个文件。 这样可以避免将文件加载到内存中,但不会避免任何硬盘访问:

 require 'fileutils' open('file.txt', 'r') do |f| open('file.txt.tmp', 'w') do |f2| f.each_line do |line| f2.write(line) unless line.start_with? "Person2" end end end FileUtils.mv 'file.txt.tmp', 'file.txt' 

更有效的是读写打开文件并跳到你想要删除的位置,然后将其余的数据移回 – 但这会产生一些非常难看的代码(我不能被要求现在就这样做)。

您可以打开文件并逐行读取,将要保留的行追加到新文件中。 这使您可以最大程度地控制保留哪些行,而不会破坏原始文件。

 File.open('output_file_path', 'w') do |output| # 'w' for a new file, 'a' append to existing File.open('input_file_path', 'r') do |input| line = input.readline if keep_line(line) # logic here to determine if the line should be kept output.write(line) end end end 

如果您知道要删除的块的开头和结尾的位置,则可以打开文件,读取开头,然后搜索结束并继续阅读。

查找read方法的参数,并阅读有关在此处搜索的内容:

http://ruby-doc.org/core-2.0/IO.html#method-i-read

在这里阅读:

 File.open('output.txt', 'w') do |out_file| File.open('input.txt', 'r').each do |line| out_file.print line.sub('Person2', '') end end