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

Python 来自os.listdir()的非字母数字列表顺序,python,list,directory-listing,listdir,Python,List,Directory Listing,Listdir,我经常使用python来处理数据目录。最近,我注意到列表的默认顺序已经变成了一些几乎毫无意义的东西。例如,如果我在包含以下子目录的当前目录中:run01、run02。。。run19、run20,然后我通过以下命令生成一个列表: dir = os.listdir(os.getcwd()) 然后我通常会按以下顺序得到一个列表: dir = ['run01', 'run18', 'run14', 'run13', 'run12', 'run11', 'run08', ... ] 等等。以前的顺序是

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

dir = os.listdir(os.getcwd())
然后我通常会按以下顺序得到一个列表:

dir = ['run01', 'run18', 'run14', 'run13', 'run12', 'run11', 'run08', ... ]
等等。以前的顺序是字母数字。但这一新秩序已经伴随我一段时间了


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

我认为顺序与文件系统中文件的索引方式有关。
如果你真的想让它遵循某种顺序,你可以在得到文件后对列表进行排序。

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

包括 包括 int mainvoid{ DIR*dirp; 结构方向*de; dirp=opendir。; whilede=readdirp//是,一个“=”。 printf%s\n,de->d\u名称; closedirp; 返回0; } 构建行应该类似于gcc-ofoo.c

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

os.listdirpath

返回列表 包含中项目的名称 路径指定的目录。名单 是任意排列的。事实并非如此 包括特殊条目“.”和 “..”即使他们出现在 目录

顺序不可依赖,是文件系统的产物

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.
要对结果进行排序,请使用sortedos.listdirpath。

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

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.listdir和sorted命令的组合生成的结果与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工作得非常好

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',因此也需要基于'163'对其进行拆分

但是如果你有要求,你可以这样做

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

aa = ['run01', 'run08', 'run11', 'run12', 'run13', 'run14', 'run18']
对于目录检索,您也可以执行sortedos.listdirpath

对于类似“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(...))
问题: 如果您使用上述函数对字符串(例如文件夹名称)进行排序,并希望它们像Windows资源管理器那样进行排序,则在某些边缘情况下,该函数将无法正常工作。 如果文件夹名称中包含某些“特殊”字符,则此排序函数将在Windows上返回不正确的结果。例如,此函数将排序为1!1.a、 a,而Windows资源管理器会排序!1, 1, !a、 a

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

好处:winsort还可以在Windows上对完整路径进行排序

或者,特别是使用Unix时,可以使用natsort库pip安装natsort以正确的方式按完整路径进行排序,这意味着子文件夹位于正确的位置

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

from natsort import natsorted, ns
dirlist = natsorted(dirlist, alg=ns.PATH | ns.IGNORECASE)
从7.1.0版开始,natsort支持在内部使用前面提到的Windows API或Linux排序,应该使用它来代替natsort。

使用natsort库:

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

Python 2

Python 3

可以找到有关如何使用此库的详细信息

从natsort导入natsorted 文件=['run01'、'run18'、'run14'、'run13'、'run12'、'run11'、'run08'] 纳特弗里斯酒店 [out]: ['run01'、'run08'、'run11'、'run12'、'run13'、'run14'、'run18'] 这不是一个复制品。natsort于2020年1月27日被添加为一个。
我认为默认情况下,顺序是由ASCII值决定的。这个问题的解决办法是

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

列表按任意顺序排列,不包括特殊条目的名称 “.”和“..”即使它们存在于目录中

这意味着顺序可能依赖于OS/文件系统,没有特别有意义的顺序,因此不能保证是任何特别的顺序。正如许多答案所提到的:如果愿意,可以对检索到的列表进行排序

干杯:

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

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

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

您看到soted输出的原因可能取决于很多因素,如操作系统、文件系统、文件创建时间、上次碎片整理期间的操作等。这解释了为什么他们会看到这种行为,没有提供解决方案。OP只是想知道为什么,而不是如何。@Denis谢谢你指出这一点-我没有注意到before@DanielWatkins好的,不是。如果处理第一个文件名,即59.9780radps-0096仍然在9.9746radps-0082之前,则不更改顺序。我想这是因为所有东西都是字符串,所以十进制没有得到正确处理。或者使用我刚找到的natsort库。只有sortedlistdir对我有效。listdir.sort给了我:TypeError:“NoneType”对象不是iterable@paul_h-listdir.sort不适用于listdir.sort中的类似于i的语句,因为list.sort方法会更改列表中项目的顺序,这意味着处理列表本身,但只返回一个。所以您需要使用一个_list=listdir'some_path';a_list.sort然后为a_list中的i执行操作您知道如何使用.sort将顺序更改为升序或降序吗?这是预期的行为。”5'>'403'是真的。@AXO是正确的,因为此时您比较的是字母数字排序,而不是数字的数量值。为了获得与预期类似的排序,您可能希望在文件夹中使用数字填充。。。['002',003',004',005',403',404',405',406']这比排序更准确!谢谢,非常好。按字母顺序打印[1,10,2,foo_10,foo_8]->[1,2,10,foo_8,foo_10']。正如预期的那样。在实现Windows资源管理器匹配功能方面,存在一个长期存在的问题。也许你应该提出一个解决方案?winsort函数正是我所需要的:毫无疑问,这里的答案是最好的。以上这些对我都不起作用,那个键len似乎是剩下的最后一个技巧,非常感谢。哦,哇,这确实解决了问题,不是吗。只是没有分类。接受它。多么天才的回答!太有用了!你应该为此享有盛名@你真是太好了。非常感谢你。
files = list(os.popen("ls"))
files = [file.strip("\n") for file in files]