os.listdir() 返回的文件和目录列表的顺序是不确定的,并且在不同的操作系统、不同的文件系统,甚至是同一系统的不同时间点都可能不同。

绝对不要依赖 listdir() 返回列表的默认顺序来进行任何逻辑判断或排序操作。
为什么顺序不确定?
listdir() 的底层实现直接调用了操作系统提供的文件系统接口,操作系统在遍历目录时,其顺序取决于:
- 文件系统类型:不同的文件系统(如 ext4 on Linux, NTFS on Windows, APFS on macOS)存储和管理文件元数据的方式不同,导致遍历顺序各异。
- 文件创建/修改时间:某些文件系统可能会按照时间戳来排序,但这并非标准行为。
- 文件名哈希:为了提高性能,文件系统内部可能会使用哈希表来存储文件名,遍历哈希表的顺序通常是随机的。
- 操作系统版本和实现细节:不同版本的 Windows 或 Linux,其
listdir的具体实现可能略有不同。
如何获得有序的列表?
如果你需要按特定顺序(例如字母顺序)处理文件,最可靠的方法是先获取列表,然后进行排序。
按字母顺序排序(最常见)
这是最常用的排序方式,通常使用 sorted() 函数。

import os
# 假设你的目录路径是 'my_folder'
folder_path = 'my_folder'
# 1. 使用 listdir 获取原始列表(顺序不确定)
entries = os.listdir(folder_path)
print("原始列表 (顺序不确定):")
print(entries)
# 2. 使用 sorted() 进行排序
sorted_entries = sorted(entries)
print("\n按字母顺序排序后的列表:")
print(sorted_entries)
# 3. 如果你希望不区分大小写的排序
case_insensitive_sorted_entries = sorted(entries, key=str.lower)
print("\n不区分大小写的排序列表:")
print(case_insensitive_sorted_entries)
sorted() vs list.sort()
sorted(entries):返回一个新的已排序列表,原始列表entries保持不变。entries.sort():对原始列表entries进行原地排序,会改变entries本身,不返回新列表。
推荐使用 sorted(),因为它更安全,不会意外修改原始数据。
按修改时间/创建时间排序
如果你想根据文件的最后修改时间或创建时间来排序,可以使用 os.path.getmtime() 和 os.path.getctime()。
import os
from operator import itemgetter
folder_path = 'my_folder'
# 获取包含文件名和修改时间的元组列表
entries_with_mtime = []
for filename in os.listdir(folder_path):
filepath = os.path.join(folder_path, filename)
# 获取文件的最后修改时间(时间戳)
mtime = os.path.getmtime(filepath)
entries_with_mtime.append((filename, mtime))
# 按修改时间(元组的第二个元素,索引为1)排序
# reverse=True 表示从新到旧排序
sorted_by_mtime = sorted(entries_with_mtime, key=itemgetter(1), reverse=True)
print("按修改时间排序(从新到旧):")
for filename, mtime in sorted_by_mtime:
# 将时间戳转换为可读的格式
print(f"{filename}: {os.path.getmtime(filepath)} -> {mtime}")
按文件大小排序
使用 os.path.getsize() 来获取文件大小。

import os
from operator import itemgetter
folder_path = 'my_folder'
# 获取包含文件名和大小的元组列表
entries_with_size = []
for filename in os.listdir(folder_path):
filepath = os.path.join(folder_path, filename)
# 获取文件大小(字节)
size = os.path.getsize(filepath)
entries_with_size.append((filename, size))
# 按大小(元组的第二个元素,索引为1)排序
# reverse=True 表示从大到小排序
sorted_by_size = sorted(entries_with_size, key=itemgetter(1), reverse=True)
print("按文件大小排序(从大到小):")
for filename, size in sorted_by_size:
print(f"{filename}: {size} bytes")
现代替代方案:os.scandir()
从 Python 3.5 开始,官方推荐使用 os.scandir() 来替代 os.listdir()。os.scandir() 不仅功能更强大,而且在性能上通常更优。
主要优势:
- 性能更高:
os.scandir()在返回DirEntry对象时,已经预加载了文件的一些元数据(如文件类型、大小、修改时间等),如果你之后需要这些信息,就不需要再次调用os.stat(),大大减少了系统调用的次数。 - 信息更丰富:返回的是
DirEntry对象,而不是简单的字符串,你可以直接通过属性(如.is_file(),.is_dir(),.name,.stat())获取信息,非常方便。
示例:使用 os.scandir() 进行排序
import os
from operator import itemgetter
folder_path = 'my_folder'
# 使用 os.scandir()
with os.scandir(folder_path) as entries:
# 直接获取文件名和大小,无需再次调用 os.path.getsize
entries_with_size = [
(entry.name, entry.stat().st_size) for entry in entries if entry.is_file()
]
# 按大小排序
sorted_by_size = sorted(entries_with_size, key=itemgetter(1), reverse=True)
print("使用 os.scandir() 按文件大小排序(从大到小):")
for filename, size in sorted_by_size:
print(f"{filename}: {size} bytes")
总结与最佳实践
| 方法 | 返回值 | 顺序 | 推荐场景 |
|---|---|---|---|
os.listdir() |
字符串列表 | 不确定 | 仅需获取所有文件名,且不关心顺序时(较少见)。 |
os.scandir() |
DirEntry 对象列表 |
不确定 | 强烈推荐,需要文件属性或关心性能时。 |
sorted(listdir()) |
新的已排序列表 | 按字母顺序 | 当需要按字母顺序处理文件时,这是最简单直接的方法。 |
sorted(scandir()) |
新的已排序列表 | 按自定义顺序 | 当需要按时间、大小等自定义逻辑排序时,性能最佳。 |
黄金法则:
永远不要相信
listdir()或scandir()的默认顺序,如果你需要有序的列表,请务必在获取后使用sorted()或其他排序方法进行排序,对于现代 Python 代码,优先使用os.scandir()。
