Все еще «ныряю» в Python и хочу убедиться, что что-то не упускаю из виду. Я написал сценарий, который извлекает файлы из нескольких zip-файлов и сохраняет извлеченные файлы вместе в одном каталоге. Чтобы предотвратить перезапись повторяющихся имен файлов, я написал эту небольшую функцию - и мне просто интересно, есть ли лучший способ сделать это? Спасибо!
def unique_filename(file_name):
counter = 1
file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext')
while os.path.isfile(file_name):
file_name = file_name_parts[0] + '_' + str(counter) + file_name_parts[1]
counter += 1
return file_name
Мне действительно делать требуется, чтобы файлы находились в одном каталоге, и нумерация дубликатов определенно приемлема в моем случае, поэтому я не ищу более надежный метод (хотя, я полагаю, любые указатели приветствуются), но просто чтобы убедиться что то, что этим достигается, делается правильно.






Если вам нужны удобочитаемые имена, это хорошее решение. Существуют процедуры для возврата уникальных имен файлов, например. временные файлы, но они производят длинные случайные имена.
Два небольших изменения ...
base_name, ext = os.path.splitext(file_name)
Вы получите два результата с разными значениями, дайте им разные имена.
file_name = "%s_%d%s" % (base_name, str(counter), ext)
Это не быстрее и не короче. Но когда вы хотите изменить шаблон имени файла, шаблон находится в одном месте, и с ним немного легче работать.
Да, это хорошая стратегия для удобочитаемых, но уникальных имен файлов.
Одно важное изменение: Вам следует заменить os.path.isfile на os.path.lexists! Как написано прямо сейчас, если есть каталог с именем /foo/bar.baz, ваша программа попытается перезаписать его новым файлом (который не будет работать) ... поскольку isfile проверяет только файлы, а не каталоги . lexists проверяет каталоги, символические ссылки и т.д ... в основном, если есть какая-либо причина, по которой имя файла не может быть создано.
Обновлено: @Brian дал лучший ответ, который более безопасен и надежен с точки зрения условий гонки.
Одна из проблем заключается в том, что в приведенном выше коде присутствует состояние гонки, поскольку существует разрыв между проверкой существования и созданием файла. Это может иметь последствия для безопасности (подумайте о том, что кто-то злонамеренно вставляет символическую ссылку на конфиденциальный файл, который он не сможет перезаписать, но ваша программа, работающая с более высокими привилегиями, может). Такие атаки, почему такие вещи, как os.tempnam ( ) устарели.
Чтобы обойти это, лучше всего попытаться создать файл таким образом, чтобы в случае неудачи вы получали исключение, а в случае успеха возвращали фактически открытый объект файла. Это можно сделать с помощью функций os.open нижнего уровня, передав оба флага os.O_CREAT и os.O_EXCL. После открытия верните фактический файл (и, возможно, имя файла), который вы создаете. Например, вот ваш код, измененный для использования этого подхода (возврат кортежа (файл, имя файла)):
def unique_file(file_name):
counter = 1
file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext')
while 1:
try:
fd = os.open(file_name, os.O_CREAT | os.O_EXCL | os.O_RDRW)
return os.fdopen(fd), file_name
except OSError:
pass
file_name = file_name_parts[0] + '_' + str(counter) + file_name_parts[1]
counter += 1
[Редактировать] На самом деле, лучший способ решить описанные выше проблемы - это, вероятно, использовать модуль tempfile, хотя вы можете потерять некоторый контроль над именованием. Вот пример его использования (с сохранением аналогичного интерфейса):
def unique_file(file_name):
dirname, filename = os.path.split(file_name)
prefix, suffix = os.path.splitext(filename)
fd, filename = tempfile.mkstemp(suffix, prefix+"_", dirname)
return os.fdopen(fd), filename
>>> f, filename=unique_file('/home/some_dir/foo.txt')
>>> print filename
/home/some_dir/foo_z8f_2Z.txt
Единственным недостатком этого подхода является то, что вы всегда будете получать имя файла с некоторыми случайными символами в нем, поскольку нет попытки сначала создать немодифицированный файл (/home/some_dir/foo.txt). Вы также можете посмотреть файлы tempfile.TemporaryFile и NamedTemporaryFile, которые сделают то же самое, а также автоматически удалят с диска при закрытии.
Небольшая опечатка: это должен быть os.O_RDWR, а не os.O_RDRW
если вас не волнует удобочитаемость, uuid.uuid4 () - ваш друг.
import uuid
def unique_filename(prefix=None, suffix=None):
fn = []
if prefix: fn.extend([prefix, '-'])
fn.append(str(uuid.uuid4()))
if suffix: fn.extend(['.', suffix.lstrip('.')])
return ''.join(fn)
Как насчет
def ensure_unique_filename(orig_file_path):
from time import time
import os
if os.path.lexists(orig_file_path):
name, ext = os.path.splitext(orig_file_path)
orig_file_path = name + str(time()).replace('.', '') + ext
return orig_file_path
time () возвращает текущее время в миллисекундах. в сочетании с исходным именем файла он довольно уникален даже в сложных многопоточных случаях.
Да, это Правильный_Путь для этого. Хотел бы я изменить себя и поставить ваш ответ вверху!