Как создать инкрементное имя файла в Python?

Я создаю программу, которая создаст файл и сохранит его в каталоге с именем файла sample.xml. Как только файл сохраняется, когда я пытаюсь запустить программу, он перезаписывает старый файл в новый, поскольку у них есть одно и то же имя файла. Как я увеличиваю имена файлов, чтобы всякий раз, когда я пытаюсь запустить код, он будет увеличивать имя файла. и не будет перезаписывать существующий. Я собираюсь сначала проверить имя файла в каталоге, и если они будут одинаковыми, код будет генерировать новое имя файла:

fh = open("sample.xml", "w")
rs = [blockresult]
fh.writelines(rs)
fh.close()

Ответ 1

Я бы перебрал через sample[int].xml, например, и захватил следующее доступное имя, которое не используется файлом или каталогом.

import os

i = 0
while os.path.exists("sample%s.xml" % i):
    i += 1

fh = open("sample%s.xml" % i, "w")
....

Это должно дать вам sample0.xml изначально, затем sample1.xml и т.д.

Обратите внимание, что относительная запись файла по умолчанию относится к файловому каталогу/папке, из которой вы запускаете код. При необходимости используйте абсолютные пути. Используйте os.getcwd(), чтобы прочитать текущий каталог и os.chdir(path_to_dir), чтобы установить новый текущий каталог.

Ответ 2

def get_nonexistant_path(fname_path):
    """
    Get the path to a filename which does not exist by incrementing path.

    Examples
    --------
    >>> get_nonexistant_path('/etc/issue')
    '/etc/issue-1'
    >>> get_nonexistant_path('whatever/1337bla.py')
    'whatever/1337bla.py'
    """
    if not os.path.exists(fname_path):
        return fname_path
    filename, file_extension = os.path.splitext(fname_path)
    i = 1
    new_fname = "{}-{}{}".format(filename, i, file_extension)
    while os.path.exists(new_fname):
        i += 1
        new_fname = "{}-{}{}".format(filename, i, file_extension)
    return new_fname

Прежде чем открыть файл, вызовите

fname = get_nonexistant_path("sample.xml")

Это либо даст вам 'sample.xml', либо - если этот alreay существует - 'sample-i.xml', где я - наименьшее положительное целое число, так что файл еще не существует.

Я рекомендую использовать os.path.abspath("sample.xml"). Если у вас ~ как домашний каталог, вам может потребоваться развернуть его в первую очередь.

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

Ответ 3

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

Вот версия, которая находит следующее доступное имя файла во время log (n):

import os

def next_path(path_pattern):
    """
    Finds the next free path in an sequentially named list of files

    e.g. path_pattern = 'file-%s.txt':

    file-1.txt
    file-2.txt
    file-3.txt

    Runs in log(n) time where n is the number of existing files in sequence
    """
    i = 1

    # First do an exponential search
    while os.path.exists(path_pattern % i):
        i = i * 2

    # Result lies somewhere in the interval (i/2..i]
    # We call this interval (a..b] and narrow it down until a + 1 = b
    a, b = (i // 2, i)
    while a + 1 < b:
        c = (a + b) // 2 # interval midpoint
        a, b = (c, b) if os.path.exists(path_pattern % c) else (a, c)

    return path_pattern % b

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

for i in range(1,10000):
    with open(next_path('file-%s.foo'), 'w'):
        pass

И реализовал наивный подход:

def next_path_naive(path_pattern):
    """
    Naive (slow) version of next_path
    """
    i = 1
    while os.path.exists(path_pattern % i):
        i += 1
    return path_pattern % i

И вот результаты:

Быстрая версия:

real    0m2.132s
user    0m0.773s
sys 0m1.312s

Наивная версия:

real    2m36.480s
user    1m12.671s
sys 1m22.425s

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

Ответ 4

Попробуйте установить переменную count, а затем добавьте эту переменную, вложенную в тот же цикл, в который вы записываете свой файл. Включите цикл count внутри имени файла с escape-символом, так что каждый цикл имеет отметку +1, номер в файле.

Некоторый код из проекта, который я только что закончил:

numberLoops = #some limit determined by the user
currentLoop = 1
while currentLoop < numberLoops:
    currentLoop = currentLoop + 1

    fileName = ("log%d_%d.txt" % (currentLoop, str(now())))

Для справки:

from time import mktime, gmtime

def now(): 
   return mktime(gmtime()) 

который, вероятно, не имеет отношения к вашему делу, но я запускал несколько экземпляров этой программы и делал множество файлов. Надеюсь, это поможет!

Ответ 5

Без сохранения данных состояния в дополнительном файле более быстрое решение для представленных здесь будет заключаться в следующем:

from glob import glob
import os

files = glob("somedir/sample*.xml")
files = files.sorted()
cur_num = int(os.path.basename(files[-1])[6:-4])
cur_num += 1
fh = open("somedir/sample%s.xml" % cur_num, 'w')
rs = [blockresult]
fh.writelines(rs)
fh.close()

Это также будет увеличиваться, даже если некоторые из файлов с более низким номером исчезнут.

Другим решением, которое мне нравится (указывается Eiyrioü), является идея сохранения временного файла, который содержит ваш последний номер:

temp_fh = open('somedir/curr_num.txt', 'r')
curr_num = int(temp_fh.readline().strip())
curr_num += 1
fh = open("somedir/sample%s.xml" % cur_num, 'w')
rs = [blockresult]
fh.writelines(rs)
fh.close()

Ответ 6

Другое решение, которое избегает использования цикла while, - это использование функции os.listdir() которая возвращает список всех файлов и каталогов, содержащихся в каталоге, путь которого берется в качестве аргумента.

Чтобы ответить на пример в вопросе, предположив, что каталог, в котором вы работаете, содержит только файлы "sample_i.xlm", проиндексированные начиная с 0, вы можете легко получить следующий индекс для нового файла с помощью следующего кода.

import os

new_index = len(os.listdir('path_to_file_containing_only_sample_i_files'))
new_file = open('path_to_file_containing_only_sample_i_files/sample_%s.xml' % new_index, 'w')

Ответ 7

Другой пример, использующий рекурсию

import os
def checkFilePath(testString, extension, currentCount):
    if os.path.exists(testString + str(currentCount) +extension):
        return checkFilePath(testString, extension, currentCount+1)
    else:
        return testString + str(currentCount) +extension

Использование:

checkFilePath("myfile", ".txt" , 0)

Ответ 8

Вы можете использовать цикл while со счетчиком, который проверяет, существует ли файл с именем и значением счетчика, если он все же затем перемещается, иначе разбивается и создает файл.

Я сделал это таким образом для одного из моих проектов:

from os import path
import os

i = 0
flnm = "Directory\\Filename" + str(i) + ".txt"
while path.exists(flnm) :
    flnm = "Directory\\Filename" + str(i) + ".txt"
    i += 1
f = open(flnm, "w") #do what you want to with that file...
f.write(str(var))
f.close() # make sure to close it.

'

Здесь счетчик я начинается с 0, и цикл while проверяет каждый раз, существует ли файл, перемещается ли он, иначе вырывается и создает файл, из которого вы можете настроить. Также обязательно закройте его, иначе это приведет к открытию файла, что может вызвать проблемы при его удалении. Я использовал path.exists(), чтобы проверить, существует ли файл. Не делайте from os import * это может вызвать проблемы, когда мы используем метод open(), так как есть и другой метод os.open(), и он может выдавать ошибку. TypeError: Integer expected. (got str) TypeError: Integer expected. (got str) Остальное желаю всем счастливого Нового года.

Ответ 9

Два способа сделать это:

  • Проверьте наличие старого файла и, если он существует, попробуйте следующее имя файла +1
  • сохранить данные состояния где-нибудь

простой способ сделать это с места в карьер будет:

import os.path as pth
filename = "myfile"
filenum = 1
while (pth.exists(pth.abspath(filename+str(filenum)+".py")):
    filenum+=1
my_next_file = open(filename+str(filenum)+".py",'w')

как вещь дизайна, while True замедляет работу и не является отличным показателем для чтения кода.


отредактировано: @EOL вклады/мысли

поэтому я думаю, что с .format более читабельным на первый взгляд - но использование .format лучше для общности и соглашения так.

import os.path as pth
filename = "myfile"
filenum = 1
while (pth.exists(pth.abspath(filename+str(filenum)+".py")):
    filenum+=1
my_next_file = open("{}{}.py".format(filename, filenum),'w')
# or 
my_next_file = open(filename + "{}.py".format(filenum),'w')

и вам не нужно использовать abspath - вы можете использовать относительные пути, если хотите, я предпочитаю путь abs иногда, потому что он помогает нормализовать пройденные пути:).

import os.path as pth
filename = "myfile"
filenum = 1
while (pth.exists(filename+str(filenum)+".py"):
    filenum+=1
##removed for conciseness

Ответ 10

Мне нужно было сделать нечто подобное, но для выходных каталогов в конвейере обработки данных. Я был вдохновлен ответом Vorticity, но добавил использование регулярных выражений, чтобы получить конечный номер. Этот метод продолжает увеличивать последний каталог, даже если промежуточные пронумерованные выходные каталоги удалены. Также добавляются начальные нули, чтобы имена сортировались в алфавитном порядке (то есть ширина 3 дает 001 и т.д.)

def get_unique_dir(path, width=3):
    # if it doesn't exist, create
    if not os.path.isdir(path):
        log.debug("Creating new directory - {}".format(path))
        os.makedirs(path)
        return path

    # if it empty, use
    if not os.listdir(path):
        log.debug("Using empty directory - {}".format(path))
        return path

    # otherwise, increment the highest number folder in the series

    def get_trailing_number(search_text):
        serch_obj = re.search(r"([0-9]+)$", search_text)
        if not serch_obj:
            return 0
        else:
            return int(serch_obj.group(1))

    dirs = glob(path + "*")
    num_list = sorted([get_trailing_number(d) for d in dirs])
    highest_num = num_list[-1]
    next_num = highest_num + 1
    new_path = "{0}_{1:0>{2}}".format(path, next_num, width)

    log.debug("Creating new incremented directory - {}".format(new_path))
    os.makedirs(new_path)
    return new_path

get_unique_dir("output")