Python: создать файл, но если имя существует, добавьте номер
Есть ли в Python встроенные функции для добавления числа к имени файла, если оно уже существует?
Моя идея заключается в том, что он будет работать так, как работает определенная ОС - если файл выводится в каталог, где файл с таким именем уже существует, он добавляет число или увеличивает его.
Т.е.: если файл "file.pdf" существует, он создаст файл "file2.pdf", а в следующий раз "file3.pdf".
Ответы
Ответ 1
В некотором смысле Python имеет эту функциональность, встроенную в модуль tempfile
. К сожалению, вам нужно подключиться к частной глобальной переменной tempfile._name_sequence
. Это означает, что официально tempfile
не гарантирует, что в будущих версиях _name_sequence
даже существует - это деталь реализации.
Но если вы все равно используете его, это покажет, как вы можете создавать уникально названные файлы формы file#.pdf
в указанном каталоге, например /tmp
:
import tempfile
import itertools as IT
import os
def uniquify(path, sep = ''):
def name_sequence():
count = IT.count()
yield ''
while True:
yield '{s}{n:d}'.format(s = sep, n = next(count))
orig = tempfile._name_sequence
with tempfile._once_lock:
tempfile._name_sequence = name_sequence()
path = os.path.normpath(path)
dirname, basename = os.path.split(path)
filename, ext = os.path.splitext(basename)
fd, filename = tempfile.mkstemp(dir = dirname, prefix = filename, suffix = ext)
tempfile._name_sequence = orig
return filename
print(uniquify('/tmp/file.pdf'))
Ответ 2
Я пытался реализовать то же самое в своем проекте, но ответ @unutbu казался слишком "тяжелым" для моих нужд, поэтому я наконец придумал следующий код:
import os
index = ''
while True:
try:
os.makedirs('../hi'+index)
break
except WindowsError:
if index:
index = '('+str(int(index[1:-1])+1)+')' # Append 1 to number in brackets
else:
index = '(1)'
pass # Go and try create file again
На всякий случай кто-то наткнулся на это и требует чего-то более простого.
Ответ 3
Поскольку tempfile hack A) является взломом и B) по-прежнему требует достойного количества кода, я пошел с ручной реализацией. Вам в основном нужно:
Я определил safe_open, который можно использовать так же, как open:
def iter_incrementing_file_names(path):
"""
Iterate incrementing file names. Start with path and add " (n)" before the
extension, where n starts at 1 and increases.
:param path: Some path
:return: An iterator.
"""
yield path
prefix, ext = os.path.splitext(path)
for i in itertools.count(start=1, step=1):
yield prefix + ' ({0})'.format(i) + ext
def safe_open(path, mode):
"""
Open path, but if it already exists, add " (n)" before the extension,
where n is the first number found such that the file does not already
exist.
Returns an open file handle. Make sure to close!
:param path: Some file name.
:return: Open file handle... be sure to close!
"""
flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
if 'b' in mode and platform.system() == 'Windows':
flags |= os.O_BINARY
for filename in iter_incrementing_file_names(path):
try:
file_handle = os.open(filename, flags)
except OSError as e:
if e.errno == errno.EEXIST:
pass
else:
raise
else:
return os.fdopen(file_handle, mode)
# Example
with safe_open("some_file.txt", "w") as fh:
print("Hello", file=fh)
Ответ 4
Если нумерация всех файлов не является проблемой, и вы заранее знаете имя файла, который нужно записать, вы можете просто сделать:
import os
counter = 0
filename = "file{}.pdf"
while os.path.isfile(filename.format(counter)):
counter += 1
filename = filename.format(counter)
Ответ 5
Недавно я столкнулся с тем же, и вот мой подход:
import os
file_name = "file_name.txt"
if os.path.isfile(file_name):
expand = 1
while True:
expand += 1
new_file_name = file_name.split(".txt")[0] + str(expand) + ".txt"
if os.path.isfile(new_file_name):
continue
else:
file_name = new_file_name
break
Ответ 6
Я еще не тестировал это, но он должен работать, итерации по возможным именам файлов, пока этот файл не будет существовать, в какой момент он сломается.
def increment_filename(fn):
fn, extension = os.path.splitext(path)
n = 1
yield fn + extension
for n in itertools.count(start=1, step=1)
yield '%s%d.%s' % (fn, n, extension)
for filename in increment_filename(original_filename):
if not os.isfile(filename):
break
Ответ 7
Это работает для меня.
Исходное имя файла 0.yml
, если оно существует, оно добавит его до соответствия требованиям
import os
import itertools
def increment_filename(file_name):
fid, extension = os.path.splitext(file_name)
yield fid + extension
for n in itertools.count(start=1, step=1):
new_id = int(fid) + n
yield "%s%s" % (new_id, extension)
def get_file_path():
target_file_path = None
for file_name in increment_filename("0.yml"):
file_path = os.path.join('/tmp', file_name)
if not os.path.isfile(file_path):
target_file_path = file_path
break
return target_file_path
Ответ 8
import os
class Renamer():
def __init__(self, name):
self.extension = name.split('.')[-1]
self.name = name[:-len(self.extension)-1]
self.filename = self.name
def rename(self):
i = 1
if os.path.exists(self.filename+'.'+self.extension):
while os.path.exists(self.filename+'.'+self.extension):
self.filename = '{} ({})'.format(self.name,i)
i += 1
return self.filename+'.'+self.extension
Ответ 9
Я реализовал аналогичное решение с pathlib
:
Создайте имена файлов, которые соответствуют шаблону path/<file-name>-\d\d.ext
. Возможно, это решение поможет...
import pathlib
from toolz import itertoolz as itz
def file_exists_add_number(path_file_name, digits=2):
pfn = pathlib.Path(path_file_name)
parent = pfn.parent # parent-dir of file
stem = pfn.stem # file-name w/o extension
suffix = pfn.suffix # NOTE: extension starts with '.' (dot)!
try:
# search for files ending with '-\d\d.ext'
last_file = itz.last(parent.glob(f"{stem}-{digits * '?'}{suffix}"))
except:
curr_no = 1
else:
curr_no = int(last_file.stem[-digits:]) + 1
# int to string and add leading zeros
curr_no = str(last_no).zfill(digits)
path_file_name = parent / f"{stem}-{curr_no}{suffix}"
return str(path_file_name)
Примечание: это решение начинается с 01
и будет искать только файл-образец, содержащий -\d\d
!
Ответ 10
Я написал свою простую функцию для этого. Примитивно, но выполняет свою работу:
def uniquify(path):
filename, extension = os.path.splitext(path)
counter = 1
while os.path.exists(path):
path = filename + " (" + str(counter) + ")" + extension
counter += 1
return path
Ответ 11
Я обнаружил, что условная функция os.path.exists() делает то, что мне нужно. В качестве примера я использую сохранение из словаря в csv, но та же логика может работать для файлов любого типа:
import os
def smart_save(filename, dict):
od = filename + '_' # added underscore before number for clarity
for i in np.arange(0,500,1): # I set an arbitrary upper limit of 500
d = od + str(i)
if os.path.exists(d + '.csv'):
pass
else:
with open(d + '.csv', 'w') as f: #or any saving operation you need
for key in dict.keys():
f.write("%s,%s\n"%(key, dictionary[key]))
break
Примечание: по умолчанию к имени файла добавляется число (начиная с 0), но его легко сместить.
Ответ 12
Чуть позже, но все равно что-то подобное должно работать должным образом, м.б. будет кому-то полезно.
Вы можете использовать встроенный итератор, чтобы сделать это (загрузчик изображений в качестве примера для вас):
def image_downloader():
image_url = 'some_image_url'
for count in range(10):
image_data = requests.get(image_url).content
with open(f'image_{count}.jpg', 'wb') as handler:
handler.write(image_data)
Файлы будут увеличиваться правильно. Результат:
image.jpg
image_0.jpg
image_1.jpg
image_2.jpg
image_3.jpg
image_4.jpg
image_5.jpg
image_6.jpg
image_7.jpg
image_8.jpg
image_9.jpg