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


108

我经常使用python处理数据目录。最近,我注意到列表的默认顺序已更改为几乎毫无意义的内容。例如,如果我位于包含以下子目录的当前目录中:run01,run02,...,run19,run20,然后从以下命令生成列表:

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

然后我通常会按以下顺序获得列表:

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

等等。该顺序曾经是字母数字。但是这个新订单已经存在了一段时间。

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


python列表中的顺序实际上是相关的(即列表是有序的)。我同意Nowayz的观点:您看到的奇怪命令可能是文件系统的功能。几年前,我看到这种情况发生在Mac上连接了第三方网络文件系统。
David P Simons

感谢您提供信息,我已删除列表订单注释。
marshall.ward 2012年

@ shog9好,现在我可以看到问题被问了,并且已经回答了(链接答案中从未提供数据排序的方式),但是问题的主题不是很清楚(搜索未出现答案)并且标签不是很有帮助
Dimitris

@Dimitris:这是一个公平的批评-我已将其改名并合并了两个问题,因此现在可以在此处找到这两组答案,而您的答案仍然指向该问题。
Shog9年

顺便说一句,如果还有其他人像我一样对这里的答案感到困惑,那是因为我的问题与另一个请求排序listdir输出的问题合并了。我不确定为什么将这些问题合并。
marshall.ward

Answers:


63

我认为顺序与文件在FileSystem上建立索引的方式有关。如果您确实要使其遵循某些顺序,则可以在获取文件后始终对列表进行排序。


128

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

sorted(os.listdir(whatever_directory))

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

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

我认为应该可以解决问题。

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


1
如果处理的是数字优先的文件名,则不更改顺序(即59.9780radps-0096仍在9.9746radps-0082之前)。我认为这是因为所有内容都是字符串,因此十进制未正确处理。
Elliot

2
或使用我刚刚发现的natsort库。
Elliot 2014年

5
sorted(listdir)为我工作。listdir.sort()给了我:TypeError:'NoneType'对象不是可迭代的
paul_h

1
@AlexB-当然...只是通过reverse=True使其降序排列。
mgilson

1
@ user3895596-我认为sorted首先编写的东西可以单行吗?
米尔森

43

根据文档

os.listdir(路径)

返回一个列表,其中包含由path给出的目录中条目的名称。该列表按任意顺序排列。它不包括特殊条目“。” 和“ ..”,即使它们存在于目录中。

不能依赖顺序,它是文件系统的产物。

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


26

不管出于什么原因,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(...))

问题: 如果您使用上述函数对字符串(例如文件夹名称)进行排序,并希望像Windows资源管理器一样对它们进行排序,则在某些情况下无法正常使用。
如果您的文件夹名称中带有某些“特殊”字符,则此排序功能将在Windows上返回不正确的结果。例如,此函数将排序1, !1, !a, a,而Windows资源管理器将排序!1, 1, !a, a

因此,如果您想像Python中的Windows资源管理器那样进行排序,则必须通过ctypes 使用Windows内置函数StrCmpLogicalW(这当然在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)

不要将其用于仅对文件夹名称(或通常为字符串)进行常规排序,因为它比sorted_alphanumeric()上面的函数要慢很多。如果您期望Windows资源管理器排序,该
natsorted库将给您不正确的结果,因此可以使用winsort()它。


工作完美。print( sorted_aphanumeric(["1", "10", "2", "foo_10", "foo_8"]) )-> ['1', '2', '10', 'foo_8', 'foo_10']。完全符合预期。
user136036

natsorted要实现Windows资源管理器匹配功能,存在一个长期存在的未解决问题。也许您应该提供解决方案?github.com/SethMMorton/natsort/issues/41
SethMMorton

8

我认为默认情况下,顺序由ASCII值确定。这个问题的解决方案是这样

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

5

这可能只是C的readdir()返回顺序。尝试运行此C程序:

#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

PS只需运行此代码和您的Python代码,它们都给了我排序的输出,所以我无法重现您看到的内容。


1
您看到过时的输出的原因可能取决于许多因素,例如操作系统,文件系统,文件创建时间,上一次碎片整理期间的操作……
Joachim Sauer

3
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))

对于like 'run01.txt''run01.csv'您可以这样做

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

2

我发现“排序”并不总是按预期进行。例如,我有一个如下目录,“ sort”给我一个非常奇怪的结果:

>>> 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']

看起来它首先比较第一个字符,如果最大,那就是最后一个。


2
这是预期的行为。('5' > '403') is True
AXO

2
@AXO是正确的,因为此时您正在比较字母数字排序,而不是数字的定量值。为了获得与您的期望类似的排序,您可能需要在文件夹上使用数字填充... ['002','003','004','005','403','404',' 405','406']
安德鲁(Andrew)

2

文档中

该列表以任意顺序排列,并且不包括特殊条目“。”。和“ ..”,即使它们存在于目录中。

这意味着该顺序可能与OS /文件系统相关,没有特别有意义的顺序,因此不能保证特定顺序。提到了很多答案:如果需要,可以对检索到的列表进行排序。

干杯:)


2

艾略特的答案可以很好地解决它,但是由于它是评论,因此没有引起注意,因此为了帮助某人,我在此重申它为解决方案。

使用natsort库:

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

Python 2

sudo pip install natsort

Python 3

sudo pip3 install natsort

有关如何使用此库的详细信息,请参见此处


1
比那更准确sorted()!谢谢
法里德Alijani

1
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.

这就解释了为什么他们没有提供解决方案就看到了行为。
丹尼尔·沃特金斯

1
OP只想知道为什么,而不是如何。
丹尼斯

@Denis感谢您指出这一点-我之前没有注意到
Dimitris

@DanielWatkins OK,不是吗?)
Denis

0

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))效果很好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.