Ruby 如何根据行号将一行替换为另一行?

Ruby 如何根据行号将一行替换为另一行?,ruby,Ruby,我有一个文件包含: My name is Chris My name is Chris My name is Chris 是否有一个文件函数来替换第n行,在本例中是文件中的第二行,因此新文件如下所示 My name is Chris My name is John My name is Chris 我知道我可以逐行读取文件,并编写“如果num_line=2,则替换行”的逻辑,但我真的不想这样做。您试图做的是一个坏主意。如果代码在中途崩溃,则源/目标文件可能已损坏 相反: 创建一个新文件 使用

我有一个文件包含:

My name is Chris
My name is Chris
My name is Chris
是否有一个文件函数来替换第n行,在本例中是文件中的第二行,因此新文件如下所示

My name is Chris
My name is John
My name is Chris

我知道我可以逐行读取文件,并编写“如果num_line=2,则替换行”的逻辑,但我真的不想这样做。

您试图做的是一个坏主意。如果代码在中途崩溃,则源/目标文件可能已损坏

相反:

  • 创建一个新文件
  • 使用
    foreach
    打开原始文件
  • 读一行,检查是否应该更改。如果是的话,请更改它
  • 将该行写入新文件
  • 关闭原始文件
  • 关闭新文件
  • 将原始文件重命名为安全的文件
  • 将新文件重命名为原始文件名
  • 遵循这些步骤有助于确保原始数据在最后的步骤#7和#8之前保持不变,然后将原始文件保存到备份中,并将新文件移动到旧文件的文件名中

    使用
    foreach
    的Ruby逐行IO比尝试
    读取
    读取行
    要快,所以不要这样做。此外,
    read
    readlines
    不可伸缩。如果原始文件很大,请考虑将其导入数据库,并使用DBM进行更改,然后将文件导出回磁盘。数据库附带的实用程序可以极其快速地加载和导出数据,并且可能比基于Ruby的纯解决方案运行得更快

    您可以在不创建其他文件的情况下替换原始文件中的一行,但这样做的代码非常混乱。使用行IO和两个文件更容易、更安全,这使得过程非常简单

    这类未经测试的代码是流程的核心:

    File.open('file.new', 'w') do |fo|
      File.foreach('file.old') do |li|
        li = 'foo' if $. == 10
        fo.puts li
      end
    end
    
    $。
    是一个Ruby全局文件,包含上次读取行的行号,因此很容易判断这是否是您要更改的行。

    让我们创建这样一个文件1:


    一,。heredoc没有缩进。

    但是最后一行就在那里,在
    li
    中。为什么要带来混乱(而且,很可能是一个google会话来找出这个模糊的魔法变量真正包含的内容)?“如果代码在中途崩溃,您的源/目标文件可能会损坏。”-即使它没有崩溃,文件仍然会损坏,除非替换行与替换行的长度完全相同。“但最后一行是li。”是的,但OP需要一个基于行号的解决方案。保留行的内容不会有任何帮助。”除非替换行的长度完全相同,否则文件仍然会损坏“仅当尝试直接覆盖原始文件时。如果脚本在读取和写入单独的文件时崩溃,则原始文件将保持完整,只有新文件受影响。这就是代码示例的全部要点。我同意你所说的。我只是指出,如果他直接对源文件进行操作,几乎肯定会导致文件损坏,崩溃与否。“$是一个包含行号的Ruby全局文件”-啊,行号。我读的是“行内容”。现在你看到我的困惑了吗?:)“但我真的不想那样做”-为什么?因为这是你必须要做的。
    s =<<_
    My name is Chris
    My name is Chris
    My name is Chris
    and other stuff
    _
    
    FName = "my_file"
    
    IO.write(FName, s)
      #=> 67 
    
    lines = File.readlines(FName)
      #=> ["My name is Chris\n",
      #    "My name is Chris\n",
      #    "My name is Chris\n",
      #    "and other stuff\n"] 
    
    lines[1].sub!(/\bChris\b/, "John")
      #=> "My name is John\n" 
    
    File.rename(FName, "#{FName}.bak")
    
    open(FName, 'w') { |f| lines.each { |line| f << line } }
      #=> ["My name is Chris\n", "My name is John\n", "My name is Chris\n",
      #    "and other stuff\n"] 
    
    puts IO.read(FName)
    My name is Chris
    My name is John
    My name is Chris
    and other stuff