如何在 Python 中创建递增的文件名?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17984809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 09:40:13  来源:igfitidea点击:

How do I create a incrementing filename in Python?

pythonfile-io

提问by Oliver Ven Quilnet

I'm creating a program that will create a file and save it to the directory with the filename sample.xml. Once the file is saved when i try to run the program again it overwrites the old file into the new one because they do have the same file name. How do I increment the file names so that whenever I try to run the code again it will going to increment the file name. and will not overwrite the existing one. I am thinking of checking the filename first on the directory and if they are the same the code will generate a new filename:

我正在创建一个程序,该程序将创建一个文件并将其保存到文件名为 sample.xml 的目录中。一旦文件被保存,当我尝试再次运行该程序时,它会将旧文件覆盖到新文件中,因为它们确实具有相同的文件名。如何增加文件名,以便每当我尝试再次运行代码时,它都会增加文件名。并且不会覆盖现有的。我想首先检查目录上的文件名,如果它们相同,代码将生成一个新的文件名:

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

回答by Eiyrioü von Kauyf

The two ways to do it are:

有两种方法可以做到:

  1. Check for the existence of the old file and if it exists try the next file name +1
  2. save state data somewhere
  1. 检查旧文件是否存在,如果存在则尝试下一个文件名+1
  2. 将状态数据保存在某处


an easy way to do it off the bat would be:

一个简单的方法是:

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')

as a design thing, while Trueslows things down and isn't a great thing for code readability

作为设计的东西,while True减慢了速度,对于代码可读性来说并不是一件好事



edited: @EOL contributions/ thoughts

编辑:@EOL 贡献/想法

so I think not having .format is more readable at first glance - but using .format is better for generality and convention so.

所以我认为没有 .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')

and you don't have to use abspath - you can use relative paths if you prefer, I prefer abs path sometimes because it helps to normalize the paths passed :).

并且您不必使用 abspath - 如果您愿意,可以使用相对路径,有时我更喜欢 abs 路径,因为它有助于规范传递的路径:)。

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

回答by bossi

I would iterate through sample[int].xmlfor example and grab the next available name that is not used by a file or directory.

sample[int].xml例如,我会遍历并获取文件或目录未使用的下一个可用名称。

import os

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

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

That should give you sample0.xmlinitially, then sample1.xml, etc.

最初应该给你sample0.xml,然后是sample1.xml等。

Note that the relative file notation by default relates to the file directory/folder you run the code from. Use absolute paths if necessary. Use os.getcwd()to read your current dirand os.chdir(path_to_dir)to set a new current dir.

请注意,默认情况下,相对文件表示法与您运行代码的文件目录/文件夹相关。如有必要,请使用绝对路径。使用os.getcwd()读你的当前目录下,并os.chdir(path_to_dir)设置新的当前目录

回答by ford

Try setting a count variable, and then incrementing that variable nested inside the same loop you write your file in. Include the count loop inside the name of the file with an escape character, so every loop ticks +1 and so does the number in the file.

尝试设置一个计数变量,然后递增嵌套在您写入文件的同一循环中的该变量。在文件名中包含带有转义字符的计数循环,因此每个循环都会打勾 +1,并且数字中的数字也是如此文件。

Some code from a project I just finished:

我刚刚完成的项目中的一些代码:

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

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

For reference:

以供参考:

from time import mktime, gmtime

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

which is probably irrelevant in your case but i was running multiple instances of this program and making tons of files. Hope this helps!

这可能与您的情况无关,但我正在运行该程序的多个实例并制作大量文件。希望这可以帮助!

回答by Vorticity

Without storing state data in an extra file, a quicker solution to the ones presented here would be to do the following:

如果不将状态数据存储在额外的文件中,则此处提供的更快解决方案是执行以下操作:

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

This will also keep incrementing, even if some of the lower numbered files disappear.

即使某些编号较低的文件消失,这也会不断增加。

The other solution here that I like (pointed out by Eiyrioü) is the idea of keeping a temporary file that contains your most recent number:

我喜欢的另一个解决方案(由 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()

回答by chumbaloo

Another example using recursion

另一个使用递归的例子

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

Use:

用:

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

回答by Martin Thoma

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

Before you open the file, call

在打开文件之前,调用

fname = get_nonexistant_path("sample.xml")

This will either give you 'sample.xml'or - if this alreay exists - 'sample-i.xml'where i is the lowest positive integer such that the file does not already exist.

这将给你'sample.xml'或者 - 如果这已经存在 -'sample-i.xml'其中 i 是最小的正整数,这样文件还不存在。

I recommend using os.path.abspath("sample.xml"). If you have ~as home directory, you might need to expand itfirst.

我建议使用os.path.abspath("sample.xml"). 如果您有~主目录,则可能需要先扩展它

Please note that race conditions might occur with this simple code if you have multiple instances running at the same time. If this might be a problem, please check this question.

请注意,如果您有多个实例同时运行,这个简单的代码可能会出现竞争条件。如果这可能是一个问题,请检查此问题

回答by James

Sequentially checking each file name to find the next available one works fine with small numbers of files, but quickly becomes slower as the number of files increases.

顺序检查每个文件名以找到下一个可用的文件名对于少量文件很有效,但随着文件数的增加,速度会很快变慢。

Here is a version that finds the next available file name in log(n) time:

这是一个在 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

To measure the speed improvement I wrote a small test function that creates 10,000 files:

为了测量速度提升,我编写了一个创建 10,000 个文件的小测试函数:

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

And implemented the naive approach:

并实施了幼稚的方法:

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

And here are the results:

结果如下:

Fast version:

快速版:

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

Naive version:

天真版本:

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

Finally, note that either approach is susceptible to race conditions if multiple actors are trying to create files in the sequence at the same time.

最后,请注意,如果多个参与者试图同时在序列中创建文件,则任一方法都容易受到竞争条件的影响。

回答by Malo Pocheau

Another solution that avoids the use of while loop is to use os.listdir()function which returns a list of all the files and directories contained in a directory whose path is taken as an argument.

避免使用 while 循环的另一个解决方案是使用os.listdir()函数,该函数返回目录中包含的所有文件和目录的列表,该目录的路径作为参数。

To answer the example in the question, supposing that the directory you are working in only contains "sample_i.xlm" files indexed starting at 0, you can easily obtain the next index for the new file with the following code.

为了回答问题中的示例,假设您正在工作的目录仅包含索引从 0 开始的“sample_i.xlm”文件,您可以使用以下代码轻松获取新文件的下一个索引。

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')

回答by Matrix Programmer

You can use a while loop with a counter which checks if a file with a name and the counter's value exists if it does then move on else break and make a file.

您可以使用带有计数器的 while 循环,该计数器检查具有名称和计数器值的文件是否存在,如果存在,则继续执行 else 中断并创建文件。

I have done it in this way for one of my projects:`

我以这种方式为我的一个项目做到了:`

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.

`

`

Here the counter i starts from 0 and a while loop checks everytime if the file exists, if it does it moves on else it breaks out and creates a file from then you can customize. Also make sure to close it else it will result in the file being open which can cause problems while deleting it. I used path.exists() to check if a file exists. Don't do from os import *it can cause problem when we use open() method as there is another os.open() method too and it can give the error. TypeError: Integer expected. (got str)Else wish u a Happy New Year and to all.

在这里,计数器 i 从 0 开始,while 循环每次检查文件是否存在,如果存在则继续移动,否则它会中断并创建一个文件,然后您可以自定义。还要确保关闭它,否则它会导致文件被打开,这可能会在删除它时导致问题。我使用 path.exists() 来检查文件是否存在。from os import *当我们使用 open() 方法时,不要这样做会导致问题,因为还有另一个 os.open() 方法,它会给出错误。TypeError: Integer expected. (got str)否则祝大家新年快乐。

回答by Woods26

I needed to do something similar, but for output directories in a data processing pipeline. I was inspired by Vorticity's answer, but added use of regex to grab the trailing number. This method continues to increment the last directory, even if intermediate numbered output directories are deleted. It also adds leading zeros so the names will sort alphabetically (i.e. width 3 gives 001 etc.)

我需要做一些类似的事情,但是对于数据处理管道中的输出目录。我受到 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's 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")