来自 os.listdir ()的非字母数字列表顺序

我经常使用 python 来处理数据目录。最近,我注意到列表的默认顺序变成了一些几乎毫无意义的东西。例如,如果我在一个包含以下子目录的工作目录中: run01,run02,... run19,run20,然后我从以下命令生成一个列表:

dir = os.listdir(os.getcwd())

然后我通常会得到一个按顺序排列的列表:

dir = ['run01', 'run18', 'run14', 'run13', 'run12', 'run11', 'run08', ... ]

诸如此类。以前的顺序是字母数字。但这种新秩序已经存在了一段时间了。

是什么决定了这些列表的(显示)顺序?

224250 次浏览

我认为顺序与文件在 FileSystem 上的索引方式有关。 如果你真的想让它坚持某种顺序,你总是可以排序的列表后获得的文件。

这可能只是 C 的 readdir()返回的顺序:

#include <dirent.h>
#include <stdio.h>


int main(void){
DIR *dirp;
struct dirent* de;
dirp = opendir(".");
while(de = readdir(dirp)) // Yes, one '='.
printf("%s\n", de->d_name);
closedir(dirp);
return 0;
}

构建线应该类似于 gcc -o foo foo.c

另外,刚刚运行了这个和你的 Python 代码,他们都给了我排序后的输出,所以我无法重现你看到的。

根据 文件:

(路径)

返回一个列表 中的项的名称 路径给定的目录。 < strong > 列表 是在任意顺序 。它不 包括特别条目 即使他们出现在 目录。

秩序是不可依赖的,它是文件系统的工件。

要对结果进行排序,请使用 sorted(os.listdir(path))

您可以使用内置的 sorted函数对字符串进行任意排序,

sorted(os.listdir(whatever_directory))

或者,您可以使用列表的 .sort方法:

lst = os.listdir(whatever_directory)
lst.sort()

我觉得应该可以。

请注意,os.listdir获取文件名的顺序可能完全取决于您的文件系统。

In [6]: os.listdir?


Type:       builtin_function_or_method
String Form:<built-in function listdir>
Docstring:
listdir(path) -> list_of_strings
Return a list containing the names of the entries in the directory.
path: path of directory to list
The list is in **arbitrary order**.  It does not include the special
entries '.' and '..' even if they are present in the directory.

我发现“排序”并不总是如我所愿。例如,我有一个如下的目录,“排序”给了我一个非常奇怪的结果:

>>> os.listdir(pathon)
['2', '3', '4', '5', '403', '404', '407', '408', '410', '411', '412', '413', '414', '415', '416', '472']
>>> sorted([ f for f in os.listdir(pathon)])
['2', '3', '4', '403', '404', '407', '408', '410', '411', '412', '413', '414', '415', '416', '472', '5']

似乎是先比较第一个字符,如果那是最大的,那就是最后一个。

建议的 os.listdirsorted命令的组合生成的结果与 Linux 下的 ls -l命令相同。下面的例子证实了这一假设:

user@user-PC:/tmp/test$ touch 3a 4a 5a b c d1 d2 d3 k l p0 p1 p3 q 410a 409a 408a 407a
user@user-PC:/tmp/test$ ls -l
total 0
-rw-rw-r-- 1 user user 0 Feb  15 10:31 3a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 407a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 408a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 409a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 410a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 4a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 5a
-rw-rw-r-- 1 user user 0 Feb  15 10:31 b
-rw-rw-r-- 1 user user 0 Feb  15 10:31 c
-rw-rw-r-- 1 user user 0 Feb  15 10:31 d1
-rw-rw-r-- 1 user user 0 Feb  15 10:31 d2
-rw-rw-r-- 1 user user 0 Feb  15 10:31 d3
-rw-rw-r-- 1 user user 0 Feb  15 10:31 k
-rw-rw-r-- 1 user user 0 Feb  15 10:31 l
-rw-rw-r-- 1 user user 0 Feb  15 10:31 p0
-rw-rw-r-- 1 user user 0 Feb  15 10:31 p1
-rw-rw-r-- 1 user user 0 Feb  15 10:31 p3
-rw-rw-r-- 1 user user 0 Feb  15 10:31 q


user@user-PC:/tmp/test$ python
Python 2.7.6 (default, Jun 22 2015, 17:58:13)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.listdir( './' )
['d3', 'k', 'p1', 'b', '410a', '5a', 'l', 'p0', '407a', '409a', '408a', 'd2', '4a', 'p3', '3a', 'q', 'c', 'd1']
>>> sorted( os.listdir( './' ) )
['3a', '407a', '408a', '409a', '410a', '4a', '5a', 'b', 'c', 'd1', 'd2', 'd3', 'k', 'l', 'p0', 'p1', 'p3', 'q']
>>> exit()
user@user-PC:/tmp/test$

因此,对于希望在 Python 代码中复制众所周知的 ls -l命令的结果的人来说,sorted( os.listdir( DIR ) )工作得非常好。

aaa = ['row_163.pkl', 'row_394.pkl', 'row_679.pkl', 'row_202.pkl', 'row_1449.pkl', 'row_247.pkl', 'row_1353.pkl', 'row_749.pkl', 'row_1293.pkl', 'row_1304.pkl', 'row_78.pkl', 'row_532.pkl', 'row_9.pkl', 'row_1435.pkl']
sorted(aaa, key=lambda x: int(os.path.splitext(x.split('_')[1])[0]))

因为在我的情况下需要我有像 row_163.pkl这样的情况下,这里 os.path.splitext('row_163.pkl')会把它分成 ('row_163', '.pkl'),所以需要拆分它的基础上’_’也。

但是如果你需要的话,你可以这样做

sorted(aa, key = lambda x: (int(re.sub('\D','',x)),x))

哪里

aa = ['run01', 'run08', 'run11', 'run12', 'run13', 'run14', 'run18']

也可以进行目录检索 sorted(os.listdir(path))

对于像 'run01.txt''run01.csv'这样的情况,你可以这样做

sorted(files, key=lambda x : int(os.path.splitext(x)[0]))

不管出于什么原因,Python 都没有内置 自然分类(意思是1,2,10而不是1,10,2) ,所以你必须自己编写:

import re
def sorted_alphanumeric(data):
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
return sorted(data, key=alphanum_key)

现在可以使用这个函数对列表进行排序:

dirlist = sorted_alphanumeric(os.listdir(...))

问题: 如果你使用上面的函数对字符串(例如文件夹名称)进行排序,并希望它们像文件资源管理器那样排序,那么在某些边缘情况下,它将不能正常工作。
如果你的文件夹名中包含特定的“特殊”字符,这个排序函数将返回不正确的结果。例如,这个函数将对 1, !1, !a, a进行排序,而文件资源管理器函数将对 !1, 1, !a, a进行排序。

因此,如果你想使用 就像 Python 中的文件资源管理器那样排序,你必须使用 Windows 内置函数 StrCmpLogicalW通过 ctype (这当然不会在 Unix 上工作) :

from ctypes import wintypes, windll
from functools import cmp_to_key


def winsort(data):
_StrCmpLogicalW = windll.Shlwapi.StrCmpLogicalW
_StrCmpLogicalW.argtypes = [wintypes.LPWSTR, wintypes.LPWSTR]
_StrCmpLogicalW.restype  = wintypes.INT


cmp_fnc = lambda psz1, psz2: _StrCmpLogicalW(psz1, psz2)
return sorted(data, key=cmp_to_key(cmp_fnc))

这个函数比 sorted_alphanumeric()稍慢。

奖励: winsort也可以 对 Windows 上的完整路径进行排序

或者,特别是如果您使用 Unix,您可以使用 natsort库(pip install natsort)以正确的方式按完整路径进行排序(意味着子文件夹位于正确的位置)。

您可以像这样使用它来对完整路径进行排序:

from natsort import natsorted, ns
dirlist = natsorted(dirlist, alg=ns.PATH | ns.IGNORECASE)

从7.1.0版本开始,natsort 支持 os_sorted,它在内部使用前面提到的 Windows API 或 Linux 排序,应该使用 os_sorted而不是 natsorted()

使用 natsort库:

使用以下命令安装 Ubuntu 和其他 Debian 版本的库

巨蟒2

sudo pip install natsort

巨蟒3

sudo pip3 install natsort

有关如何使用此库的详细信息,请参阅 给你

from natsort import natsorted


files = ['run01', 'run18', 'run14', 'run13', 'run12', 'run11', 'run08']
natsorted(files)


[out]:
['run01', 'run08', 'run11', 'run12', 'run13', 'run14', 'run18']
  • 这不是 回答的副本。 natsort是在2020-01-27作为 编辑添加的。

我认为默认情况下,订单是用 ASCII 值确定的

dir = sorted(os.listdir(os.getcwd()), key=len)

来自 文件:

列表按任意顺序 排列,不包括特殊条目’还有。.即使它们出现在目录中。

这意味着顺序可能与操作系统/文件系统相关,没有特别有意义的顺序,因此不能保证是特定的顺序。正如许多答案所提到的: 如果首选,可以对检索到的列表进行排序。

干杯:)

默认情况下,ls预览按名称排序的文件(ls选项可用于按日期、大小、 ... ... 进行排序)

files = list(os.popen("ls"))
files = [file.strip("\n") for file in files]

当目录包含如此多的文件时,使用 ls会有更好的性能。

要直接回答这个问题,可以使用以下代码。

dir = ['run01', 'run18', 'run14', 'run13', 'run12', 'run11', 'run08']
for file in sorted(dir, key=lambda x:int(x.replace('run', ''))):
print(file)

它将印刷:

run01
run08
run11
run12
run13
run14
run18

这种方法使用 Python 内置方法 sorted,并且通过 key参数指定排序条件,即没有将‘ run’转换为整数的列表项。