Как создать файл на python без перезаписи существующего файла

В настоящее время у меня есть цикл, который пытается найти неиспользуемое имя файла, добавляя суффиксы в строку имени файла. Когда он не находит файл, он использует имя, которое не может открыть новый файл с этим именем. Проблема заключается в том, что этот код используется на веб-сайте и может быть несколько попыток сделать одно и то же в одно и то же время, поэтому существует условие гонки.

Как я могу заставить python перезаписывать существующий файл, если он создается между временем проверки и временем открытия в другом потоке.

Я могу свести к минимуму вероятность рандомизации суффиксов, но шанс уже минимизирован на основе частей пути. Я хочу исключить этот шанс с помощью функции, которую можно сказать, создайте этот файл ТОЛЬКО, если он не существует.

Я могу использовать функции win32 для этого, но я хочу, чтобы это работало на кросс-платформе, потому что в конце концов оно будет размещено на linux.

Ответ 1

Используйте os.open() с помощью os.O_CREAT и os.O_EXCL, чтобы создать файл. Это произойдет, если файл уже существует:

>>> fd = os.open("x", os.O_WRONLY | os.O_CREAT | os.O_EXCL)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 17] File exists: 'x'

Как только вы создали новый файл, используйте os.fdopen(), чтобы превратить дескриптор в стандартный файл Python:

>>> fd = os.open("y", os.O_WRONLY | os.O_CREAT | os.O_EXCL)
>>> f = os.fdopen(fd, "w")  # f is now a standard Python file object

Изменить: Из Python 3.3 встроенный open() имеет режим x, который означает "open для исключительного создания, если файл уже существует".

Ответ 2

Если вас беспокоит состояние гонки, вы можете создать временный файл и затем переименовать его.

>>> import os
>>> import tempfile
>>> f = tempfile.NamedTemporaryFile(delete=False)
>>> f.name
'c:\\users\\hughdb~1\\appdata\\local\\temp\\tmpsmdl53'
>>> f.write("Hello world")
>>> f.close()
>>> os.rename(f.name, r'C:\foo.txt')
>>> if os.path.exists(r'C:\foo.txt') :
...     print 'File exists'
...
File exists

В качестве альтернативы вы можете создавать файлы с помощью uuid в имени. fooobar.com/questions/5074/....

>>> import uuid
>>> str(uuid.uuid1())
'64362370-93ef-11de-bf06-0023ae0b04b8'

Ответ 3

Если у вас есть id, связанный с каждым потоком/процессом, который пытается создать файл, вы можете поместить его в суффикс где-то, тем самым гарантируя, что ни один из двух процессов не может использовать одно и то же имя файла.

Это устраняет условие гонки между процессами.