How to open (read-write) or create a file with truncation allowed?

前端 未结 4 1624
無奈伤痛
無奈伤痛 2020-12-09 07:29

I want to:

  • open a file in read-write mode if it exists;
  • create it if it doesn\'t exist;
  • be able to truncate it anytime-anywhere.
相关标签:
4条回答
  • 2020-12-09 08:07

    You can read, write and truncate with "a+" (Ruby):

    File.open("test.txt", "a+") do |f|
      f.print "abc\ndefgh" 
      f.rewind
      p f.read 
      f.truncate(5) 
    end
    puts File.size("test.txt") #=> 5
    
    0 讨论(0)
  • 2020-12-09 08:14

    I don't know of any elegant way to do exactly this in Ruby. My solution would probably be to create a temporary file, write contents to it, and then rename it to the filename I really wanted. This would overwrite the previous file if it exists, or create the file if it did not. Something like this:

    orig_filename = './whatever_file.log'
    temp_filename = './.tempfile'
    temp_file = File.new(temp_filename, 'w')
    
    // Write contents to file
    
    temp_file.close
    File.rename(temp_filename, orig_filename)
    

    The rename will raise SystemCallError if it fails for whatever reason.

    0 讨论(0)
  • 2020-12-09 08:18

    According to OpenGroup:

    O_TRUNC

    If the file exists and is a regular file, and the file is successfully opened O_RDWR or O_WRONLY, its length is truncated to 0 and the mode and owner are unchanged. It will have no effect on FIFO special files or terminal device files. Its effect on other file types is implementation-dependent. The result of using O_TRUNC with O_RDONLY is undefined.

    So, O_TRUNC is probably passed when opening a file with "w" or "w+". This gives "truncation" a different meaning, not what I want.

    With python the solution seems to open file at low-level I/O with os.open() function.

    The following python function:

    def touchopen(filename, *args, **kwargs):
        # Open the file in R/W and create if it doesn't exist. *Don't* pass O_TRUNC
        fd = os.open(filename, os.O_RDWR | os.O_CREAT)
    
        # Encapsulate the low-level file descriptor in a python file object
        return os.fdopen(fd, *args, **kwargs)
    

    has the behavior I wanted. You can use it like this (it's in fact my use case):

    # Open an existing file or create if it doesn't exist
    with touchopen("./tool.run", "r+") as doing_fd:
    
        # Acquire a non-blocking exclusive lock
        fcntl.lockf(doing_fd, fcntl.LOCK_EX)
    
        # Read a previous value if present
        previous_value = doing_fd.read()
        print previous_value 
    
        # Write the new value and truncate
        doing_fd.seek(0)
        doing_fd.write("new value")
        doing_fd.truncate()
    
    0 讨论(0)
  • 2020-12-09 08:28

    Well, there are only these modes, and all of them have the "defects" you listed.

    Your only option is to wrap open(). Why not something like this? (Python)

    def touchopen(filename, *args, **kwargs):
        open(filename, "a").close() # "touch" file
        return open(filename, *args, **kwargs)
    

    it behaves just like open, you could even rebind it to open() if you really wish.

    all of open's features are preserved, you can even do:

    with touchopen("testfile", "r+") as testfile:
        do_stuff()
    

    You could of course create a contextmanager which opens the file in a+ mode, reads it into memory, and intercepts writes so you handle truncation by magically creating a temporary file in w mode, and renames that tempfile to your original file when you close it, but that would be overkill I guess.

    0 讨论(0)
提交回复
热议问题