【问题标题】:Prepend a single line to file with Ruby使用 Ruby 在文件前添加一行
【发布时间】:2011-12-24 07:00:57
【问题描述】:

我想用 Ruby 在文件的顶部添加一行,如下所示:

# initial file contents
something
else

# file contents after prepending "hello" on its own line
hello
something
else

下面的代码只是替换了整个文件的内容:

f = File.new('myfile', 'w')
f.write "test string"

【问题讨论】:

    标签: ruby file-io prepend


    【解决方案1】:

    这是一个很常见的任务:

    original_file = './original_file'
    new_file = original_file + '.new'
    

    设置测试:

    File.open(original_file, 'w') do |fo|
      %w[something else].each { |w| fo.puts w }
    end
    

    这是实际代码:

    File.open(new_file, 'w') do |fo|
      fo.puts 'hello'
      File.foreach(original_file) do |li|
        fo.puts li
      end
    end
    

    将旧文件重命名为安全的名称:

    File.rename(original_file, original_file + '.old')
    File.rename(new_file, original_file)
    

    证明它有效:

    puts `cat #{original_file}`
    puts '---'
    puts `cat #{original_file}.old`
    

    哪些输出:

    hello
    something
    else
    ---
    something
    else
    

    您不想尝试将文件完全加载到内存中。这将一直有效,直到您获得的文件大于您的 RAM 分配,并且机器开始爬行,或更糟糕的是,崩溃。

    改为逐行阅读。读取单个行仍然非常快,并且是可扩展的。您的驱动器必须有足够的空间来存储原始文件和临时文件。

    【讨论】:

      【解决方案2】:

      fwiw 这似乎有效:

      #!usr/bin/ruby
      
      f = File.open("myfile", "r+")
      lines = f.readlines
      f.close
      
      lines = ["something\n"] + lines
      
      output = File.new("myfile", "w")
      lines.each { |line| output.write line }
      output.close
      

      【讨论】:

      • 小心吞食文件。将整个文件读入内存不是一个可扩展的解决方案。
      • 我个人喜欢这个简单的修补程序。
      • 简单案例的最佳解决方案。
      【解决方案3】:

      我想出了这样的东西,它比我见过的其他解决方案更具描述性,也没有那么神秘:

      def file_prepend(file, str)
        new_contents = ""
        File.open(file, 'r') do |fd|
          contents = fd.read
          new_contents = str << contents
        end
        # Overwrite file but now with prepended string on it
        File.open(file, 'w') do |fd| 
          fd.write(new_contents)
        end
      end
      

      你可以这样使用它:

      file_prepend("target_file.txt", "hello world!\n")
      

      【讨论】:

      • 很好的答案!可以在整个应用程序中重复使用它。最佳解决方案 IMO。谢谢!
      【解决方案4】:

      正如一些人所说,对于较大的文件可能不会使用它,但它是一个简单的开始。

      rd = IO.read 'myfile'
      IO.write 'myfile', "hello\n" + rd
      

      【讨论】:

        【解决方案5】:

        没有机制可以轻松地做你想做的事。

        相反,您需要打开文件,删除文件,使用旧名称打开一个新文件进行写入,写入您的内容,然后从旧文件的内容写入新文件。这听起来很复杂,但在代码中很简单:

        $ cat prepend.rb 
        #!/usr/bin/ruby
        
        File.open("passwd", "r") do |orig|
            File.unlink("passwd")
            File.open("passwd", "w") do |new|
                new.write "test string"
                new.write(orig.read())
            end
        end
        

        请注意,我使用的机制不会检查错误——您可能应该处理每个File.open() 请求和File.unlink() 请求的错误——它假定文件的全部内容都适合在记忆中。一个简短的例子:

        $ rm passwd
        $ cp /etc/passwd .
        $ ./prepend.rb 
        $ head passwd
        test stringroot:x:0:0:root:/root:/bin/bash
        daemon:x:1:1:daemon:/usr/sbin:/bin/sh
        bin:x:2:2:bin:/bin:/bin/sh
        sys:x:3:3:sys:/dev:/bin/sh
        sync:x:4:65534:sync:/bin:/bin/sync
        games:x:5:60:games:/usr/games:/bin/sh
        man:x:6:12:man:/var/cache/man:/bin/sh
        lp:x:7:7:lp:/var/spool/lpd:/bin/sh
        mail:x:8:8:mail:/var/mail:/bin/sh
        news:x:9:9:news:/var/spool/news:/bin/sh
        $ 
        

        如果您想处理可能无法完全放入内存的文件,您应该编写一个类似这样的循环(未经测试——最好将其视为伪代码):

        while (data=orig.read(4096)) {
            new.write(data)
        }
        

        作为替代方案,您可以写入一个临时文件,如果写入过程成功,然后删除该文件并重命名该临时文件。无论哪种方法对您最有意义。

        【讨论】:

          【解决方案6】:

          你可以试试这个:

          File.copy_stream(myfile,tempfile)
          f = File.open(myfile,'w')
          f.write("Hello\n#{File.open(tempfile,'r').read}")
          f.close
          File.delete(tempfile)
          

          【讨论】:

            【解决方案7】:

            从命令行,你可以:

            ruby -i -pe '$_= "prepended text\n"+$_ if $. == 1' myfile
            

            或者更高效

            ruby -i -pe 'BEGIN { gets; print "prepended text\n" + $_ }; ' myfile
            

            可悲的是,事实证明,-i(就地)选项并不是真正的就地选项(也不是seds 就地选项)——我的文件将有操作后的不同 inode。

            这让我很伤心,因为实际上,如果我没有足够的磁盘空间来存储一个大文件的两个副本,我就无法过滤(添加到)一个巨大的文件。

            不过,在原地执行它并不难(一般过滤,不仅仅是第一行)。您需要做的就是:

            1) 确保您有单独的读写指针(或单独的用于读写的 File 对象) 2)确保你已经缓冲了你要重写的未读部分 3) 如果您的过滤操作应该以较短的文件结束,则在最后截断到文件

            我在https://github.org/pjump/i_rewriter 上为此编写了包装类。

            有了它,你可以做到

            Rewriter.new("myfile") do |line, index|
              index == 0 ? "prepended text\n" + line : line
            end
            

            或使用可执行文件:

            $  i_rewriter 'index == 0 ? "prepended text\n" + line : line' myfile
            

            谨慎使用(如果中断可能会损坏您的文件)。

            【讨论】:

              【解决方案8】:

              纯,但有效。尽量减少与文件相关的操作。

              `#!/bin/ruby
              inv_file = File.open("./1.txt","r+") { |f|
                  #Read file string by-string
                  until f.eof?
                      #Searching for some
                      if f.readline[/condition here/]
                          #remember position
                          offset = f.pos
                          #Find it? - stop reading
                          break
                      end
                  end
                  #Remember what contains rest of file
                  tail = f.readlines
                  #Again to offset
                  f.pos = offset
                  #insert what necessary
                  f.puts "INSERTED"
                  #reconstruct tail
                  f.write tail.join
                  }`
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2010-11-27
                • 2012-09-21
                • 2011-05-26
                • 1970-01-01
                • 2011-01-03
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多