【问题标题】:In ruby, how to rename a text file, keep same handle and delete the old file在 ruby​​ 中,如何重命名文本文件,保持相同的句柄并删除旧文件
【发布时间】:2015-07-04 18:16:41
【问题描述】:

我使用常量 (FLOG) 作为句柄来写入我的日志。在给定的点上,我必须使用一个临时日志,然后将该内容附加到常规日志中,所有这些都具有相同的句柄,这是通过一堆方法使用的。
我的测试程序如下。关闭与临时日志关联的句柄“FLOG”后,当我将 FLOG 重新分配给新日志时,这会以某种方式重新打开临时日志,但我无法删除它。 有没有办法确保旧的临时文件保持关闭(所以我可以删除它)

# Pre-existing log:
final_log = "final_#{Time.now.strftime("%Y%m%d")}.txt"

#Writing something in it
File.open(final_log, "w+") { |file| file.write("This is the final log: #{final_log}\n") }

# temp log:
temp_log = "temp_#{Time.now.strftime("%Y%m%d")}.txt"
FLOG = File.new(temp_log, "w+")

# write some stuff in temp_log
FLOG.puts "Writing in temp_log named #{temp_log}"

# closing handle for temp_log
FLOG.close  

# avoid constant reuse warning:
Object.send(:remove_const,'FLOG') if Object.const_defined?('FLOG')

# need to append temp_log content to final_log with handle FLOG 
FLOG = File.open(final_log, "a+") 

# appending old temp log to new log 
File.open(temp_log, "r").readlines.each do |line|
    puts "appending...  #{line}"
    FLOG.puts "appending...  #{line}"               
end

# closing handle    
FLOG.close

# this tells me that 'temp_log' is somehow re-opened:
ObjectSpace.each_object(File) { |f| puts("3: #{temp_log} is open") if f.path == temp_log && !f.closed? }

File.delete(temp_log)       # Cant do that:
# test_file2.rb:35:in `delete': Permission denied - temp_20150324.txt (Errno::EACCES)

【问题讨论】:

    标签: ruby handle delete-file


    【解决方案1】:

    如果您要使用临时文件,请使用tempfile

    require 'tempfile'
    
    # Pre-existing log:
    final_log = "final_#{Time.now.strftime("%Y%m%d")}.txt"
    
    #Writing something in it
    File.open(final_log, "w+") { |file| file.write("This is the final log: #{final_log}\n") }
    
    # give the tempfile a meaningful prefix
    temp_log = Tempfile.new('foobar')
    begin
      $flog = temp_log
    
      # write some stuff in temp_log
      $flog.puts "Writing in temp_log named #{temp_log.path}"
    
      # need to append temp_log content to final_log with handle $flog
      $flog = File.open(final_log, "a+")
    
      # reopen temp_log for reading, append to new log
      temp_log.open.readlines.each do |line|
        puts "appending...  #{line}"
          $flog.puts "appending...  #{line}"
      end
    
      # closing handle
      $flog.close
    ensure
      # delete temp_log
      temp_log.unlink
    end
    

    虽然全局变量通常很糟糕,但修改一个常量以便您可以像使用全局变量一样使用它更糟糕。

    【讨论】:

    • 所以我做了坏事,真的让事情变得更糟了!非常感谢这个实用的解决方案。我意识到我在没有充分了解解决方案的情况下跳得太快了。还有很多东西要学。
    【解决方案2】:

    temp_log 仍然打开,因为您没有关闭它。如果你做了类似的事情:

    temp_log_lines = File.open(temp_log, 'r') { |f| f.readlines }
    

    然后temp_log 的 I/O 流将在块的末尾关闭。但是,执行File.open(temp_log, "r").readlines 会获取File.open 返回的IO 对象并在其上调用readlines,然后您可以在附带块的情况下调用each。由于该块是您调用 each 而不是 File.open 的一部分,因此流不会在其末尾关闭,而是在程序的其余部分保持打开状态。

    至于为什么不能在程序末尾删除temp_log,不知道底层文件系统发生了什么很难说。如果您删除已打开流但未关闭的文件,Ruby 和底层(POSIX)操作系统都不会抱怨;该文件将被取消链接,但流将持续存在并且仍然具有文件的内容等等。您收到的错误是该程序的 Ruby 进程的所有者无权删除程序创建的文件。这很奇怪,但仅从这段代码很难诊断。考虑程序正在运行的目录,它的权限等。

    更笼统地说,这里有一些你可以在 Ruby 中使用的东西,可以让你的生活更轻松。

    如果你想要一个临时文件,有一个 Tempfile 类你可以使用它为你做了很多工作。

    在 Ruby 中对文件进行 I/O 的惯用方式是将一个块传递给 File.open。该块被传递给在块末尾自动关闭的文件的 I/O 流,因此您无需手动执行此操作。这是一个例子:

    flog = File.new(temp_log, 'w+') do |f|
      f.puts "Writing in temp_log named #{temp_log}"
    end
    

    FLOG 在您的代码中不是真正的常量。 A constant is only a constant if its value never changes throughout the life of the program it's declared in. Ruby 是一种非常宽松的语言,因此它允许您重新分配它们,但如果这样做会发出警告。如果您这样做,更严格的语言会引发错误。 FLOG 只是一个普通变量,应该写成flog。常量的一个很好的用途是程序需要能够引用的程序操作之外的值——例如,每次需要引用 pi 的近似值时,您可以声明 @,而不是每次都写 3.141592653589793 987654342@,然后使用PI。在 Ruby 中,这已在 Math 模块中为您完成——Math::PI 返回此。用户设置是常量经常出现的另一个地方——它们是在程序运行之前确定的,有助于确定程序的作用,并且在执行过程中不应修改,因此将它们存储在常量中有时是有意义的。

    您将您提供的程序描述为测试程序。 Ruby 有非常棒的测试库,你可以使用它比编写这样的脚本更好。 Minitest 是 Ruby 标准库的一部分,是我最喜欢的 Ruby 测试框架,但很多人也喜欢 RSpec。 (我想链接到这些框架的文档,但我没有足够的声誉——对不起。你必须谷歌。)

    但是,如果您像这样命令式地编写代码,则很难使用这些框架。 Ruby 是一种非常面向对象的语言,在使用它时,您将从以面向对象的风格构建代码中获益良多。如果您不熟悉 OO 设计,对我来说非常有用的一些书籍是 Sandi Metz 的 Practical Object-Oriented Design in Ruby重构:改进现有代码的设计 /em> 由 Martin Fowler 等人撰写,Growing Object-Oriented Software, Guided by Tests,由 Steve Freeman 和 Nat Pryce 撰写。 (同样的事情,没有链接。)

    【讨论】:

    • 我知道我使用“常量”作为句柄是不好的做法。但令我感到困惑的是,在关闭 temp_log 句柄 (FLOG.close) 后,文件仍然关闭,直到我将句柄重新用于另一个文件。我想这是因为我试图制造一个恒定的“不恒定”。无论如何,非常感谢您提供有关测试的其他指示和参考书籍。我实际上是在尝试更多地了解如何以真正的面向对象的方式编写代码。
    • 它实际上不是来自于改变 FLOG 的值——就像我上面所说的,你稍后重新打开一个流到 temp_file 并且当你这样做时你再也不会关闭它了。两次将流分配给 FLOG 时都会关闭它。请参阅代码中的第 24 行以及我在上面所说的内容。当然是参考! Metz's book 是一个很好的开始。顺便说一句,如果您认为我的回答或 Max 的回答令人满意地回答了您的问题,请考虑将其中一个标记为已接受。见:meta.stackexchange.com/questions/5234/…
    猜你喜欢
    • 1970-01-01
    • 2016-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-18
    • 1970-01-01
    • 2013-01-02
    • 1970-01-01
    相关资源
    最近更新 更多