os.listdir() 使用什么方法获取目录中的文件列表?

What method does os.listdir() use to obtain a list of files in a directory?

我正在处理一个项目,我必须在大约 400 个不同的文件中编辑几行内容。它们都在同一个文件夹中,并且每个都有唯一的名称。为了这个问题,我将它们称为fileName001.conffileName420.conf

我正在使用 python 脚本来获取每个文件的内容,然后再以编程方式进行编辑。目前,我正在使用此代码段获取包含一些 print() 行的文件以进行调试:

folderPath = '/file/path/to/list/of/conf/files'

for filename in os.listdir(folderPath):
  print('filename = ' + filename)
  print('filepath = ' + folderPath + '/' + filename)

  with open(folderPath + '/' + filename, 'r') as currFile:
    #... code goes on...

第 4 行和第 5 行仅用于调试。 运行 这个,我注意到脚本表现出一些奇怪的行为 - 文件名的打印顺序似乎在每个 运行 上都发生了变化。我更进一步并添加了以下行:

print(os.listdir(folderPath))

在我的第一个代码片段中的 for 循环之前。现在,当我 运行 来自终端的脚本时,我可以确认我得到的输出虽然包含所有文件名,但 每次都有不同的顺序:

RafaGuillermo@virtualMachine:~$ python renamefiles.py
['fileName052.txt', 'fileName216.txt', 'fileName084.txt', 'fileName212.txt', 'fileName380.txt', 'fileName026.txt', 'fileName119.txt', etc...]

RafaGuillermo@virtualMachine:~$ python renamefiles.py
['fileName024.txt', 'fileName004.txt', 'fileName209.txt', 'fileName049.txt', 'fileName166.txt', 'fileName198.txt', 'fileName411.txt', etc...]

RafaGuillermo@virtualMachine:~$

就克服这一点而言 - 因为我想确保每次都以相同的顺序浏览文件,所以我可以使用

list = sorted(os.listdir(folderPath))

对列表进行字母排序,尽管每次我 运行 脚本时 os.listdir() return 文件名列表的顺序不同似乎违反直觉。

因此,我的问题是不是如何使用os.listdir()获取目录中文件的排序列表,但是:

os.listdir() 使用什么方法来检索文件列表,为什么它似乎在每次调用时以不同的方式填充其 return 值?

答案:

这是 os.listdir() 方法的预期行为。

更多信息:

根据 Python Software Foundation Documentation:

os.listdir(path='.')

Return a list containing the names of the entries in the directory given by path. The list is in arbitrary order, and does not include the special entries '.' and '..' even if they are present in the directory.

os.listdir() 是位于 posixmodule.c of the Python source 中的 C 模块的实现。 return 基于存储文件的文件系统的结构,并且根据确定本地操作系统的条件语句的评估具有不同的实现。您在 os.listdir() 中调用的目录使用以下 C 代码打开:

static PyObject *
_posix_listdir(path_t *path, PyObject *list) {
    /* stuff */
    dirp = opendir(name);

它为存储在 name 中的目录名称打开一个流,并且 return 是一个指向目录流的指针,其位置为第一个目录条目。

继续:

for (;;) {
    errno = 0;
    Py_BEGIN_ALLOW_THREADS
    ep = readdir(dirp);
    Py_END_ALLOW_THREADS
    if (ep == NULL) {
        if (errno == 0) {
            break;
        } else {
            Py_DECREF(list);
            list = path_error(path);
            goto exit;
        }
    }
    if (ep->d_name[0] == '.' &&
        (NAMLEN(ep) == 1 ||
         (ep->d_name[1] == '.' && NAMLEN(ep) == 2)))
        continue;
    if (return_str)
        v = PyUnicode_DecodeFSDefaultAndSize(ep->d_name, NAMLEN(ep));
    else
        v = PyBytes_FromStringAndSize(ep->d_name, NAMLEN(ep));
    if (v == NULL) {
        Py_CLEAR(list);
        break;
    }
    if (PyList_Append(list, v) != 0) {
        Py_DECREF(v);
        Py_CLEAR(list);
        break;
    }
    Py_DECREF(v);
}

readdir() 被调用,先前分配的指向目录文件流的指针作为函数参数传递。 readdir() on Linux returns a dirent structure 表示 dirp 指向的目录流中的下一个点。

readdir() Linux 手册页所述:

A directory stream is opened using opendir(3). The order in which filenames are read by successive calls to readdir() depends on the filesystem implementation; it is unlikely that the names will be sorted in any fashion.

所以这种行为是预期的,也是文件系统实施的结果。

参考文献: