Подтвердить что ты не робот

Как открыть (чтение-запись) или создать файл с усечением?

Я хочу:

  • открыть файл в режиме чтения-записи, если он существует;
  • создать его, если он не существует;
  • Уметь усекать его в любое время и в любом месте.

EDIT: с усечением я имею в виду писать до позиции и отбрасывать оставшуюся часть файла, если присутствует

Все это атомарно (с одним вызовом open() или имитацией одного вызова open())

Ни одна открытая модальность не применяется:

  • r: явно не работает;
  • r +: сбой, если файл не существует;
  • w: воссоздайте файл, если он существует;
  • w +: воссоздайте файл, если он существует;
  • a: не может читать;
  • a +: не может усекать.

Некоторые комбинации, которые я пытался (rw, rw +, r + w и т.д.), похоже, тоже не работают. Возможно ли это?

Некоторые doc из Ruby (также относится к питону):

r
Read-only mode. The file pointer is placed at the beginning of the file.
This is the default mode.

r+
Read-write mode. The file pointer will be at the beginning of the file.

w
Write-only mode. Overwrites the file if the file exists. If the file
does not exist, creates a new file for writing.

w+
Read-write mode. Overwrites the existing file if the file exists. If the
file does not exist, creates a new file for reading and writing.

a
Write-only mode. The file pointer is at the end of the file if the file
exists. That is, the file is in the append mode. If the file does not exist,
it creates a new file for writing.

a+
Read and write mode. The file pointer is at the end of the file if the file
exists. The file opens in the append mode. If the file does not exist, it
creates a new file for reading and writing.
4b9b3361

Ответ 1

Согласно OpenGroup:

O_TRUNC

Если файл существует и является обычным файлом, и файл успешно     открыт O_RDWR или O_WRONLY, его длина усечена до 0 и режим     и владелец не изменился. Это не повлияет на специальные файлы FIFO     или файлы терминалов. Его влияние на другие типы файлов     зависит от реализации. Результатом использования O_TRUNC с O_RDONLY является      undefined.

Итак, O_TRUNC, вероятно, передается при открытии файла с "w" или "w +". Это дает "усечение" другое значение, а не то, что я хочу.

С помощью python решение, похоже, открывает файл на низкоуровневом вводе-выводе с функцией os.open().

Следующая функция python:

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)

имеет поведение, которое я хотел. Вы можете использовать его так (это на самом деле мой прецедент):

# 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()

Ответ 2

Ну, есть только эти режимы, и все они имеют "дефекты", которые вы указали.

Ваш единственный вариант - обернуть open(). Почему не так? (Python)

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

он ведет себя так же, как open, вы можете даже переустановить его, чтобы открыть(), если вы действительно этого пожелаете.

все открытые функции сохраняются, вы даже можете сделать:

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

Конечно, вы можете создать контекстный менеджер, который открывает файл в режиме +, считывает его в память и перехватывает записи, поэтому вы обрабатываете усечение магическим созданием временного файла в режиме w и переименовываете этот tempfile в ваш исходный файл, когда вы закройте его, но это было бы излишним, я думаю.

Ответ 3

Вы можете читать, писать и усекать с помощью "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

Ответ 4

Я не знаю какого-либо изящного способа сделать именно это в Ruby. Моим решением, вероятно, было бы создать временный файл, записать в него содержимое и затем переименовать его в имя файла, который я действительно хотел. Это заменит предыдущий файл, если он существует, или создайте файл, если он этого не сделал. Что-то вроде этого:

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)

Переименование поднимет SystemCallError, если по какой-либо причине оно не получится.