python:创建文件,但如果名称存在,则添加数字

python: Create file but if name exists add number

python对此有任何内置功能吗?我的想法是,如果一个文件被输出到一个已经存在同名文件的目录中,它将像某些操作系统的工作方式一样工作。例如:如果"file.pdf"存在,它将创建"file2.pdf",下次创建"file3.pdf"。


在某种程度上,python在tempfile模块中内置了这种功能。不幸的是,您必须利用一个私有的全局变量tempfile._name_sequence。这意味着官方上,tempfile并不保证将来的版本_name_sequence甚至存在——它是一个实现细节。但是,如果您仍然可以使用它,这将说明如何在指定目录(如/tmp)中创建file#.pdf格式的唯一命名文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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'))


我试图在我的项目中实现相同的东西,但是@unutbu的答案对于我的需求来说太"沉重",所以我最终想出了以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
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

以防有人偶然发现这一点,需要更简单的东西。


最近我遇到了同样的事情,下面是我的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
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

因为tempfile hack a)是一个hack,b)仍然需要相当数量的代码,所以我使用了手动实现。你基本上需要:

  • 一种安全地创建文件的方法,如果它不存在(这是tempfile hack提供给我们的)。
  • 文件名的生成器。
  • 用于隐藏混乱的包装功能。
  • 我定义了一个可以像打开一样使用的安全打开:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    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)

    这对我有用。初始文件名为0.yml,如果存在,将添加一个文件名,直到满足要求为止。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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

    我还没有测试过这个,但是它应该可以工作,迭代可能的文件名,直到问题文件不存在,它在哪一点中断。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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


    稍晚一点,但仍然有这样的东西应该正常工作,MB它将对某人有用。

    您可以使用内置迭代器进行此操作(以图像下载器为例):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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)

    文件将正确递增。结果是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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