如何在 Python 中获取按创建日期排序的目录中的 csv 文件列表

How to get the list of csv files in a directory sorted by creation date in Python

我需要获取目录中“.csv”文件的列表,按创建日期排序。

我使用这个功能:

from os import listdir
from os.path import isfile, join, getctime

def get_sort_files(path, file_extension):
    list_of_files = filter(lambda x: isfile(join(path, x)),listdir(path)) 
    list_of_files = sorted(list_of_files, key=lambda x: getctime(join(path, x)))
    list_of_files = [file for file in list_of_files if file.endswith(file_extension)] # keep only csv files
    return list_of_files

当我在包含少量 csv 文件(例如 500)的目录中使用它时它工作正常,但是当我在包含 50000 个 csv 文件的目录中使用它时它非常慢:它需要大约 50 秒 return.

如何修改?或者我可以使用更好的替代功能吗?

EDIT1:

瓶颈是 sorted 函数,所以我必须找到一个替代方法来按创建日期对文件进行排序而不使用它

EDIT2:

我只需要最旧的文件(如果按创建日期排序则为第一个),所以也许我不需要对所有文件进行排序。我可以只选最老的吗?

你可以试试这个方法:

def get_sort_files(path, extention):
    # Relative path generator
    sort_paths = (join(path, i)
                  for i in listdir(path) if i.endswith(extention))
    sort_paths = sorted(sort_paths, key=getctime)

    return sort_paths
# Include the . char to be explicit
>>> get_sort_files("dir", ".csv")
['dir/new.csv', 'dir/test.csv']

但是,所有文件名都在相对路径中; folder/file.csv。稍微低效的 work-around 是再次使用 lambda 键:

def get_sort_files(path, extention):
    # File name generator
    sort_paths = (i for i in listdir(path) if i.endswith(extention))
    sort_paths = sorted(sort_paths, key=lambda x: getctime(join(path, x)))

    return sort_paths
>>> get_sort_files("dir", ".csv")
['new.csv', 'test.csv']

编辑以避免sorted():

使用min():

这是此答案中列出的所有方法中最快的方法

def get_sort_files(path, extention):
    # Relative path generator
    sort_paths = (join(path, i) for i in listdir(path) if i.endswith(extention))
    return min(sort_paths, key=getctime)

手动:

def get_sort_files(path, extention):
    # Relative path generator
    sort_paths = [join(path, i) for i in listdir(path) if i.endswith(extention)]

    oldest = (getctime(sort_paths[0]), sort_paths[0])
    for i in sort_paths[1:]:
        t = getctime(i)
        if t < oldest[0]:
            oldest = (t, i)

    return oldest[1]

您应该首先检查相关文件的创建时间。您可以使用 glob() 来 return 感兴趣的文件。

构建二元组列表 - 即(创建时间、文件名)

将对每个元组中的第一项(创建日期)隐式执行该列表的排序。

然后您可以return按要求顺序排列的文件列表。

from glob import glob
from os.path import join, getctime

def get_sort_files(path, extension):
    list_of_files = []
    for file in glob(join(path,f'*{extension}')):
        list_of_files.append((getctime(file), file))
    return [file for _, file in sorted(list_of_files)]

print(get_sort_files('some directory', 'csv'))

编辑:

我创建了一个包含 50,000 个虚拟 CSV 文件的目录,并为这个答案中显示的代码计时。耗时0.24s

编辑 2:

OP 只需要最旧的文件。在这种情况下:

def get_oldest_file(path, extension):
    ctime = float('inf')
    old_file = None
    for file in glob(join(path,f'*{extension}')):
        if (ctime_ := getctime(file)) < ctime:
            ctime = ctime_
            old_file = file
    return old_file

您可以试试下面的代码:

def get_sort_files(path, file_extension):
    list_of_files = [file for file in listdir(path) if isfile(join(path, file)) and file.endswith(file_extension)]
    list_of_files.sort(key=lambda x: getctime(join(path, x)))
    return list_of_files

这个版本可以有更好的性能,尤其是在大文件夹上。它在一开始就直接使用列表理解,从一开始就忽略不相关的文件。它使用 in-place 排序。

这样,这段代码只使用一个列表。在你的代码中,你在内存中创建了多个列表,并且每次都必须复制数据:

  1. listdir(path) returns 初始文件名列表
  2. sorted(...) returns 初始列表的筛选和排序副本
  3. return 语句之前的列表理解创建另一个新列表

您可以尝试使用 os.scandir:

from os import scandir

def get_sort_files(path, file_extension):
    """Return the oldest file in path with correct file extension"""
    list_of_files = [(d.stat().st_ctime, d.path) for d in scandir(path) if d.is_file() and d.path.endswith(file_extension)]
    return min(list_of_files)

os.scandir 似乎使用较少的统计调用。有关详细信息,请参阅此 post。 我可以在包含 5000 个 csv 文件的示例文件夹中看到更好的性能。