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

时间:2013-08-01 03:31:01

标签: python file-io

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

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

12 个答案:

答案 0 :(得分:41)

例如,我会遍历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)以设置新的当前目录

答案 1 :(得分:10)

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'或 - 如果此等值存在 - 'sample-i.xml'其中i是最低正整数,使得该文件尚不存在。

我建议使用os.path.abspath("sample.xml")。如果您有~作为主目录,则可能需要先expand it

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

答案 2 :(得分:6)

按顺序检查每个文件名以查找下一个可用的文件名,只需少量文件即可正常工作,但随着文件数量的增加,文件名会很快变慢。

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

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

答案 3 :(得分:5)

尝试设置一个count变量,然后将该变量嵌套在你编写文件的同一个循环中。在包含转义字符的文件名中加入count循环,这样每个循环都会+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()) 

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

答案 4 :(得分:1)

如果不将状态数据存储在额外的文件中,可以使用以下方法更快地解决此问题:

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

答案 5 :(得分:1)

使用递归的另一个例子

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)

答案 6 :(得分:1)

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

要回答问题的示例,假设您正在使用的目录仅包含索引为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')

答案 7 :(得分:1)

您可以使用带有计数器的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.

`

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

答案 8 :(得分:0)

这两种方法是:

  1. 检查是否存在旧文件,如果存在,请尝试下一个文件名+1
  2. 在某处保存状态数据

  3. 一个简单的方法就是:

    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
    

答案 9 :(得分:0)

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

答案 10 :(得分:0)

我的2美分:一个不断增加的macOS风格的增量命名过程

  • get_increased_path("./some_new_dir").mkdir()创建./some_new_dir;然后
  • get_increased_path("./some_new_dir").mkdir()创建./some_new_dir (1);然后
  • get_increased_path("./some_new_dir").mkdir()创建./some_new_dir (2);等

如果./some_new_dir (2)存在,但不是{strong> {strong} ./some_new_dir (1),那么get_increased_path("./some_new_dir").mkdir()仍会创建./some_new_dir (3),因此索引总是增加,并且您始终知道哪个是最新


from pathlib import Path
import re

def get_increased_path(file_path):
    fp = Path(file_path).resolve()
    f = str(fp)

    vals = []
    for n in fp.parent.glob("{}*".format(fp.name)):
        ms = list(re.finditer(r"^{} \(\d+\)$".format(f), str(n)))
        if ms:
            m = list(re.finditer(r"\(\d+\)$", str(n)))[0].group()
            vals.append(int(m.replace("(", "").replace(")", "")))
    if vals:
        ext = " ({})".format(max(vals) + 1)
    elif fp.exists():
        ext = " (1)"
    else:
        ext = ""

    return fp.parent / (fp.name + ext + fp.suffix)

答案 11 :(得分:0)

这里是另一个示例。代码测试文件是否存在于目录中(如果存在),它会在文件名的最后一个索引中递增并保存 典型的文件名是:month_date_lastindex.txt的三个字母,例如May10_1.txt

import time
import datetime
import shutil
import os
import os.path


da=datetime.datetime.now()

data_id =1
ts = time.time()
st = datetime.datetime.fromtimestamp(ts).strftime("%b%d")
data_id=str(data_id)
filename = st+'_'+data_id+'.dat'
while (os.path.isfile(str(filename))):
    data_id=int(data_id)
    data_id=data_id+1
    print(data_id)
    filename = st+'_'+str(data_id)+'.dat'
    print(filename)


shutil.copyfile('Autonamingscript1.py',filename)

f = open(filename,'a+')
f.write("\n\n\n")
f.write("Data comments: \n")


f.close()