【问题标题】:Edit each line in a file in Ruby在 Ruby 中编辑文件中的每一行
【发布时间】:2011-03-27 22:00:44
【问题描述】:

我试图找到一种简单的方法来编辑文件中的每一行,但我在理解如何使用 File 类来做到这一点时遇到了一些麻烦。

我要编辑的文件有几百行,每行都有逗号分隔的值。我只对每行中的第一个值感兴趣,我想删除第一个值之后的所有值。我尝试执行以下操作:

File.open('filename.txt', 'r+') do |file|
  file.each_line { |line| line = line.split(",")[0] }
  file.write
  file.close
end

这不起作用,因为File.write 方法需要将内容写为参数。

有人能告诉我如何达到预期的效果吗?

【问题讨论】:

    标签: ruby file io line


    【解决方案1】:

    更好的解决方案(也是最安全的)之一是使用TempFile 创建一个临时文件,并在完成后将其移动到原始位置(使用FileUtils):

       require 'fileutils'
       require 'tempfile'
    
        t_file = Tempfile.new('filename_temp.txt')
        File.open("filename.txt", 'r') do |f|
          f.each_line{|line| t_file.puts line.split(",")[0].to_s }
        end
        t_file.close
        FileUtils.mv(t_file.path, "filename.txt")
    

    【讨论】:

      【解决方案2】:

      就地修改文件的另一种方法是使用-i 开关

      ruby -F"," -i.bak -ane 'puts $F[0]' file
      

      【讨论】:

        【解决方案3】:

        接受答案的问题在于它修改了文件权限和所有权(注意这一点)。

        另一种方法是在 Ruby 中使用就地编辑(不是从命令行):

        #!/usr/bin/ruby
        
        def inplace_edit(file, bak, &block)
            old_stdout = $stdout
            argf = ARGF.clone
        
            argf.argv.replace [file]
            argf.inplace_mode = bak
            argf.each_line do |line|
                yield line
            end
            argf.close
        
            $stdout = old_stdout
        end
        
        inplace_edit 'test.txt', '.bak' do |line|
            line = line.gsub(/search1/,"replace1")
            line = line.gsub(/search2/,"replace2")
            print line unless line.match(/something/)
        end
        

        如果您不想创建备份,请将 '.bak' 更改为 ''。

        【讨论】:

          【解决方案4】:

          使用代码处理文件与我们在文本编辑器中编辑文件时所做的工作大不相同。操作系统提供的文件操作在这方面非常有限(由于许多,部分是历史原因 - 想想磁带)。

          简而言之,您可能应该创建另一个文件并向其中写入数据(Mike 提供了代码),或者将整个文件加载到内存中(如果您的文件很大,这可能是个坏主意)并用处理过的数据覆盖它。

          仅供练习,以下是您实际就地编辑文件的方法。如您所见,不是最漂亮的景象:

          File.open('foo', 'r+') do |file|
            write_pos = 0
            file.each do |line|
              word = line.chomp.split(',').first
              read_pos = file.pos
              file.pos = write_pos
              file.puts word
              write_pos = file.pos
              file.pos = read_pos
            end
            file.truncate write_pos
          end
          

          【讨论】:

          • 我想我有点期待文本编辑器范式不能很好地适用于手头的任务,我猜我可能不得不采用另一种方法,例如创建一个临时文件。我决定无论如何都要问这个问题,以了解为什么该解决方案不能很好地工作,并找出它不常用的原因。因此,感谢您花时间说明为什么我想到的方法不是最合适的。很有启发性!!!
          • 最接近文本编辑器范例的方式是将整个文件加载到内存中并在那里进行编辑,因为这大致也是编辑器所做的。他们也不会就地编辑文件。当您按下 Ctrl-S 时,编辑器也(通常)将内存缓冲区写入全新的文件。
          【解决方案5】:

          我想你误解了这一行

          file.each_line { |line| line = line.split(",")[0].to_s }
          

          确实如此。它需要一行,用逗号分割,取第一个值,将其转换为字符串(它已经是),将结果分配给块局部变量'line'。然后呢?
          它继续到下一行,前一行没有做任何事情 - 一切都消失了。请参阅其他答案如何解决此问题。

          【讨论】:

            猜你喜欢
            • 2019-01-04
            • 2019-06-24
            • 2016-06-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多