【问题标题】:How to delete lines from multiple files如何从多个文件中删除行
【发布时间】:2017-05-11 14:42:37
【问题描述】:

我正在尝试逐行读取文件 (d:\mywork\list.txt) 并搜索该字符串是否出现在特定目录 (d:\new_work) 的任何文件中(一个接一个) .

如果存在于任何文件中(可能是一个或多个),我想从相应的文件中删除字符串 (car\yrui3,) 并保存相应的文件。

list.txt:

car\yrui3,
dom\09iuo,
id\byt65_d,
rfc\some_one,
desk\aa_tyt_99,
.........
.........

目录有多个文件:d:\new_work:

Rollcar-access.txt
Mycar-access.txt
Newcar-access.txt
.......
......

我的代码:

value=File.open('D:\\mywork\\list.txt').read
value.gsub!(/\r\n?/, "\n")
value.each_line do |line|
    line.chomp!
    print "For the string: #{line}"
    Dir.glob("D:/new_work/*-access.txt") do |fn|
      print "checking files:#{fn}\n"
      text = File.read(fn)
      replace = text.gsub(line.strip, "")
      File.open(fn, "w") { |file| file.puts replace }
    end
 end

问题是,值没有按预期删除。另外,当我尝试打印该值时,text 为空。

【问题讨论】:

  • 我们对您是新人还是有经验的人不感兴趣,我们希望得到充分研究、问得好、简洁的问题。我建议阅读“How To s The Smart Way”,因为它解释了如何与 SO 等社区合作。
  • 你的任务有点像“XY Problem”。当您可能应该首先询问解决任务时,您正在询问您的实施。我建议重新考虑这个问题。这些文件代表什么?为什么数据分散在多个文件中?考虑使用数据库来存储文件的内容,您可以在其中快速搜索和删除。甚至 SQLite 也可以快速完成这项工作,并且可以使用 Sequel、Datamapper 或 Active Record 等 ORM 轻松完成。

标签: ruby


【解决方案1】:

您的代码有很多问题,您没有安全地处理文件更改。

思考一下这段未经测试的代码:

ACCESS_FILES = Dir.glob("D:/new_work/*-access.txt")

File.foreach('D:/mywork/list.txt') do |target|
  target = target.strip.sub(/,$/, '')

  ACCESS_FILES.each do |filename|
    new_filename = "#{filename}.new"
    old_filename = "#{filename}.old"

    File.open(new_filename, 'w') do |fileout|
      File.foreach(filename) do |line_in|
        fileout.puts line_in unless line_in[target]
      end
    end

    File.rename(filename, old_filename)
    File.rename(new_filename, filename)
    File.delete(old_filename)
  end
end
  • 在您使用的代码中:

    File.open('D:\\mywork\\list.txt').read
    

    相反,一种更短、更简洁明了的方法是使用:

    File.read('D:/mywork/list.txt')
    

    Ruby 将根据操作系统自动调整路径名分隔符,因此请始终使用正斜杠以提高可读性。来自the IO documentation

如果可能,Ruby 会在不同的操作系统约定之间转换路径名。例如,在 Windows 系统上,文件名“/gumby/ruby/test.rb”将打开为“\gumby\ruby\test.rb”。

使用read 的问题是它不可扩展。想象一下,如果您在一个长期生产系统中执行此操作,并且您的输入文件已经增长到 TB 范围。您将暂停系统上的处理,直到可以读取文件。不要那样做。

改为使用foreach 逐行读取。请参阅“Why is "slurping" a file not a good practice?”。这将消除对

的需要
    value.gsub!(/\r\n?/, "\n")
    value.each_line do |line|
      line.chomp!
  • 虽然

    Dir.glob("D:/new_work/*-access.txt") do |fn|
    

    很好,它的位置不是。您正在为正在读取的文件中处理的每一行执行此操作,从而浪费 CPU。首先读取它并存储值,然后反复迭代该值。

  • 再次,

    text = File.read(fn)
    

    存在可扩展性问题。使用foreach 是更好的解决方案。再次。

  • 使用gsub 替换文本很快,但是当逐行 IO 一样快并且完全回避问题时,它并没有超过潜在的可扩展性问题:

    replace = text.gsub(line.strip, "")
    
  • 在阅读时打开和写入同一个文件是在生产环境中等待发生的意外:

    File.open(fn, "w") { |file| file.puts replace }
    

    更好的做法是写入一个单独的新文件,将旧文件重命名为安全的名称,然后将新文件重命名为旧文件的名称。这会保留旧文件,以防代码或机器在保存过程中崩溃。然后,完成后删除旧文件是安全的。有关详细信息,请参阅“How to search file text for a pattern and replace it with a given value”。

最后的建议是从输入文件中删除所有尾随逗号。他们没有完成任何事情,只是让您做额外的工作来处理文件。

【讨论】:

  • @TinMan :非常感谢,肯定会遵循您建议的最佳做法。
【解决方案2】:

我刚刚运行了您的代码,它在我的机器上按预期运行。我最好的猜测是您没有考虑list.txt 中每行末尾的逗号。尝试使用额外的chomp! 删除它们:

value=File.open('D:\\mywork\\list.txt').read
value.gsub!(/\r\n?/, "\n")
value.each_line do |line|
    line.chomp!
    line.chomp!(",")
    print "For the string: #{line}"
    Dir.glob("D:/new_work/*-access.txt") do |fn|
      print "checking files:#{fn}\n"
      text = File.read(fn)
      replace = text.gsub(line.strip, "")
      File.open(fn, "w") { |file| file.puts replace }
    end
 end

顺便说一句,你不应该需要这行:value.gsub!(/\r\n?/, "\n"),因为无论如何你都要把所有的换行符都删掉了,而且默认情况下 chomp 可以识别 \r\n

【讨论】:

  • 感谢您的宝贵时间和建议。非常感谢。
猜你喜欢
  • 2019-11-05
  • 2010-09-15
  • 1970-01-01
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
  • 2015-04-24
  • 1970-01-01
  • 2011-12-26
相关资源
最近更新 更多