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

Oli*_*net 22 python file-io

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

fh = open("sample.xml", "w")
rs = [blockresult]
fh.writelines(rs)
fh.close()
Run Code Online (Sandbox Code Playgroud)

bos*_*ssi 42

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

import os

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

fh = open("sample%s.xml" % i, "w")
....
Run Code Online (Sandbox Code Playgroud)

那应该先给你sample0.xml,然后是sample1.xml等.

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

  • 请问这里什么是无用或无建设性的?在不留下(建设性)评论的情况下投票对我来说似乎更没有建设性。 (3认同)
  • 你说得对,我应该更清楚。我的意思是,当名称与目录匹配时,`isfile()` 将使您的循环退出,然后您的代码尝试以写入模式打开该目录,该操作因 `IOError` 而失败。这就是为什么 `isfile()` 不是正确的测试,应该被 @Eiyrioü von Kauyf 的 `exists()` 替换。至于相对路径,我真的认为当前的“相对文件符号始终与您运行代码的文件目录/文件夹相关”具有误导性(因为“总是”)。 (2认同)

Mar*_*oma 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
Run Code Online (Sandbox Code Playgroud)

在打开文件之前,请致电

fname = get_nonexistant_path("sample.xml")
Run Code Online (Sandbox Code Playgroud)

这将给你'sample.xml'或 - 如果这个alreay存在 - 'sample-i.xml'其中i是最低的正整数,使得该文件尚不存在.

我推荐使用os.path.abspath("sample.xml").如果您有~主目录,则可能需要先将其展开.

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


Jam*_*mes 7

依次检查每个文件名以找到下一个可用的文件名,对于少量文件,效果很好,但是随着文件数量的增加,速度很快变慢。

这是一个在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
Run Code Online (Sandbox Code Playgroud)

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

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

并实现了幼稚的方法:

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
Run Code Online (Sandbox Code Playgroud)

结果如下:

快速版本:

real    0m2.132s
user    0m0.773s
sys 0m1.312s
Run Code Online (Sandbox Code Playgroud)

天真的版本:

real    2m36.480s
user    1m12.671s
sys 1m22.425s
Run Code Online (Sandbox Code Playgroud)

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

  • 感谢@GiselleSerate,看起来Python 3处理整数除法的方式与Python 2不同。我已经更新了代码以使用“//”运算符而不是“/”,这似乎解决了问题。 (4认同)

for*_*ord 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())))
Run Code Online (Sandbox Code Playgroud)

以供参考:

from time import mktime, gmtime

def now(): 
   return mktime(gmtime()) 
Run Code Online (Sandbox Code Playgroud)

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

  • Python为此提供了for循环,它们的读取和理解速度比模拟它们的while循环要快得多。此外,不建议使用%操作符。但是,请不要投反对票,因为它可以完成任务-只是不能以首选的Python方式完成任务。 (2认同)

Eiy*_*uyf 3

有两种方法可以做到这一点:

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

一种简单的方法是:

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')
Run Code Online (Sandbox Code Playgroud)

作为设计的事情,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')
Run Code Online (Sandbox Code Playgroud)

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

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