【问题标题】:Non-alphanumeric list order from os.listdir()来自 os.listdir() 的非字母数字列表顺序
【发布时间】:2011-01-27 05:30:29
【问题描述】:

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

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

那我通常会按这个顺序得到一个列表:

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

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

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

【问题讨论】:

    标签: python list directory-listing listdir


    【解决方案1】:

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

    sorted(os.listdir(whatever_directory))
    

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

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

    我认为应该做的伎俩。

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

    【讨论】:

    • 在处理数字优先的文件名时不改变顺序(即 59.9780radps-0096 仍然在 9.9746radps-0082 之前)。我想是因为一切都是字符串,所以小数没有被正确处理。
    • 或者使用我刚刚找到的natsort库。
    • 只有sorted(listdir) 为我工作。 listdir.sort() 给了我:TypeError: 'NoneType' object is not iterable
    • @paul_h -- listdir.sort() 不适用于for i in listdir.sort() 之类的语句,因为list.sort() 方法会更改列表IN PLACE 中的项目顺序,这意味着处理列表本身,但不会返回除None 之外的任何内容。所以你需要使用a_list = listdir('some_path'); a_list.sort() 然后使用for i in a_list
    • 您知道如何使用.sort 将顺序更改为升序或降序吗?
    【解决方案2】:

    我认为顺序与文件在文件系统上的索引方式有关。 如果你真的想让它遵守某种顺序,你总是可以在获取文件后对列表进行排序。

    【讨论】:

      【解决方案3】:

      根据documentation

      os.listdir(路径)

      返回一个列表 包含条目的名称 path 给出的目录。 列表 是任意顺序。它不是 包括特殊条目“。”和 '..' 即使它们出现在 目录。

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

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

      【讨论】:

        【解决方案4】:

        无论出于何种原因,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 Explorer 将排序 !1, 1, !a, a

        因此,如果您想像 Windows Explorer 在 Python 中一样进行排序,您必须通过 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)
        

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

        【讨论】:

        • 工作得很好。 print( sorted_aphanumeric(["1", "10", "2", "foo_10", "foo_8"]) ) -> ['1', '2', '10', 'foo_8', 'foo_10']。完全符合预期。
        • natsorted 上存在一个长期未解决的问题,以实现 Windows 资源管理器匹配功能。也许您应该提供解决方案? github.com/SethMMorton/natsort/issues/41
        • winsort 函数正是我所需要的 :)
        【解决方案5】:

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

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

        【讨论】:

        • 以上方法都不适合我,“key-len”似乎是最后一个技巧,非常感谢。
        【解决方案6】:

        使用natsort库:

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

        Python 2

        sudo pip install natsort
        

        Python 3

        sudo pip3 install natsort
        

        如何使用这个库的详细信息可以找到here

        from natsort import natsorted
        
        files = ['run01', 'run18', 'run14', 'run13', 'run12', 'run11', 'run08']
        natsorted(files)
        
        [out]:
        ['run01', 'run08', 'run11', 'run12', 'run13', 'run14', 'run18']
        
        • 这不是answer 的副本。 natsort 于 2020 年 1 月 27 日添加为 edit

        【讨论】:

        • sorted()更准确!谢谢
        【解决方案7】:
        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]))
        

        【讨论】:

        • 这里无疑是最佳答案。
        【解决方案8】:

        这可能只是 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

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

        【讨论】:

        • 您看到 soted 输出的原因可能取决于很多因素,例如操作系统、文件系统、文件创建时间、上次碎片整理期间的操作……
        【解决方案9】:

        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 ) ) 工作得很好。

        【讨论】:

          【解决方案10】:

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

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

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

          【讨论】:

          • 这是预期行为。 ('5' &gt; '403') is True.
          • @AXO 是正确的,因为此时您正在比较字母数字排序,而不是数字的定量值。为了获得与您的期望相似的排序,您可能希望在文件夹上使用数字填充... ['002', '003', '004', '005', '403', '404', ' 405', '406']
          【解决方案11】:

          来自documentation

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

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

          干杯:)

          【讨论】:

          • 哦,哇,这确实解决了问题,不是吗。它只是没有得到排序。接受。多么天才的答案!太有用了!你应该因此而享有盛誉!
          • @Puddle 你真好。非常感谢。
          【解决方案12】:
          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.
          

          【讨论】:

          • 这解释了为什么他们看到了这种行为,但没有提供解决方案。
          • OP 只想知道为什么,而不是如何。
          • @Denis 感谢您指出这一点——我之前没有注意到
          • @DanielWatkins 好的,不是。)
          【解决方案13】:

          ls 默认预览按名称排序的文件。 (ls 选项可用于按日期、大小等排序)

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

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

          【讨论】:

            【解决方案14】:

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

            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”的列表项转换为整数。

            【讨论】:

              猜你喜欢
              • 2018-03-10
              • 2010-12-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-09-15
              相关资源
              最近更新 更多