Python中包含路径列表的自然排序

7

我有一个列表paths_list,其中包含特定文件夹中文件(图像)的路径。例如:

['/home/username/images/s1/4.jpg', '/home/username/images/s1/7.jpg', 
'/home/username/images/s1/6.jpg', '/home/username/images/s1/3.jpg', 
'/home/username/images/s1/5.jpg', '/home/username/images/s1/10.jpg', 
'/home/username/images/s1/9.jpg', '/home/username/images/s1/1.jpg', 
'/home/username/images/s1/2.jpg', '/home/username/images/s1/12.jpg', 
'/home/username/images/s1/11.jpg', '/home/username/images/s1/8.jpg']

我希望将它们按顺序排列:[/1.jpg ,2.jpg .....,/12.jpg]。不论是按长度还是按字母表顺序排序都没有帮助。这里应该怎么做?

2
那么,您想如何排序?有什么规则吗?如果有规则,就可以完成。 - Ma0
7个回答

19

您可以使用带有lambdasorted函数。对于排序标准,您可以使用os首先仅提取文件名(使用basename),然后分离出不带扩展名的文件名(使用splitext)。

最后将其转换为int,以便按数字而不是字典顺序进行排序。

>>> import os
>>> l = ['/home/username/images/s1/4.jpg', '/home/username/images/s1/7.jpg', '/home/username/images/s1/6.jpg', '/home/username/images/s1/3.jpg', '/home/username/images/s1/5.jpg', '/home/username/images/s1/10.jpg', '/home/username/images/s1/9.jpg', '/home/username/images/s1/1.jpg', '/home/username/images/s1/2.jpg', '/home/username/images/s1/12.jpg', '/home/username/images/s1/11.jpg', '/home/username/images/s1/8.jpg']
>>> sorted(l, key=lambda i: int(os.path.splitext(os.path.basename(i))[0]))
['/home/username/images/s1/1.jpg',
 '/home/username/images/s1/2.jpg',
 '/home/username/images/s1/3.jpg',
 '/home/username/images/s1/4.jpg',
 '/home/username/images/s1/5.jpg',
 '/home/username/images/s1/6.jpg',
 '/home/username/images/s1/7.jpg',
 '/home/username/images/s1/8.jpg',
 '/home/username/images/s1/9.jpg',
 '/home/username/images/s1/10.jpg',
 '/home/username/images/s1/11.jpg',
 '/home/username/images/s1/12.jpg']

为什么要将split('.')替换为splitext?是因为基本文件名中有多个点吗? - Ma0
1
我只是想既然我已经在使用osbasename,那么我就可以使用splitext来更清楚地说明这个分割正在做什么。 split('.')也可以正常工作。是的,这也是一个很好的发现,splitext对于文件名中有多个点的情况也可以正常工作,尽管在这种特定情况下不是这样,因为他们希望该值为数字,可以按数字排序。 - Cory Kramer
1
我想你也应该补充一点,与 .sort() 不同,sorted() 不会就地对列表进行排序,因此我们需要保存列表。例如: l = sorted(l, key=lambda i: int(os.path.splitext(os.path.basename(i))[0])) - Kancha

12

使用自然排序(请参见此问题):在对字符串进行排序时,这是一种清晰的代码和良好的实践。

from natsort import natsorted
l = ['/home/username/images/s1/4.jpg', '/home/username/images/s1/7.jpg', '/home/username/images/s1/6.jpg', '/home/username/images/s1/3.jpg', '/home/username/images/s1/5.jpg', '/home/username/images/s1/10.jpg', '/home/username/images/s1/9.jpg', '/home/username/images/s1/1.jpg', '/home/username/images/s1/2.jpg', '/home/username/images/s1/12.jpg', '/home/username/images/s1/11.jpg', '/home/username/images/s1/8.jpg']
natsorted(l)
给予。
['/home/username/images/s1/1.jpg',
'/home/username/images/s1/2.jpg',
'/home/username/images/s1/3.jpg',
'/home/username/images/s1/4.jpg',
'/home/username/images/s1/5.jpg',
'/home/username/images/s1/6.jpg',
'/home/username/images/s1/7.jpg',
'/home/username/images/s1/8.jpg',
'/home/username/images/s1/9.jpg',
'/home/username/images/s1/10.jpg',
'/home/username/images/s1/11.jpg',
'/home/username/images/s1/12.jpg']

自然排序是根据计算机屏幕上的读法(按字母和数字顺序)进行排序,而不是根据计算机读取代码的方式进行排序。


我猜natsorted不会就地对列表进行排序,我们需要将排序后的列表保存到新列表中。 - Kancha
这是正确的。对于所有使用 sorted() 的其他答案也是如此。 - VinceP

3

受@Cory Kramer答案的启发,您可以使用pathlib库并获取路径的自然排序:

from pathlib import Path

a = ['/home/username/images/s1/4.jpg', 
     '/home/username/images/s1/7.jpg', 
     '/home/username/images/s1/6.jpg', 
     '/home/username/images/s1/3.jpg', 
     '/home/username/images/s1/5.jpg', 
     '/home/username/images/s1/10.jpg', 
     '/home/username/images/s1/9.jpg', 
     '/home/username/images/s1/1.jpg', 
     '/home/username/images/s1/2.jpg', 
     '/home/username/images/s1/12.jpg', 
     '/home/username/images/s1/11.jpg', 
     '/home/username/images/s1/8.jpg']

a = [Path(i) for i in a]
sorted_a = sorted(a, key=lambda i: int(i.stem))
sorted_a = [str(i) for i in a]

输出:

['/home/username/images/s1/1.jpg',
 '/home/username/images/s1/2.jpg',
 '/home/username/images/s1/3.jpg',
 '/home/username/images/s1/4.jpg',
 '/home/username/images/s1/5.jpg',
 '/home/username/images/s1/6.jpg',
 '/home/username/images/s1/7.jpg',
 '/home/username/images/s1/8.jpg',
 '/home/username/images/s1/9.jpg',
 '/home/username/images/s1/10.jpg',
 '/home/username/images/s1/11.jpg',
 '/home/username/images/s1/12.jpg']

一般而言,使用 pathlib 比直接使用 os.path 更能使代码表达更简洁。


1
您可以使用 "/" 对字符串进行分割,取最后一个元素,再使用 "." 进行分割,取第一个元素,并将其转换为整数类型:
l = ['/home/username/images/s1/4.jpg', '/home/username/images/s1/7.jpg', '/home/username/images/s1/6.jpg', '/home/username/images/s1/3.jpg', '/home/username/images/s1/5.jpg', '/home/username/images/s1/10.jpg', '/home/username/images/s1/9.jpg', '/home/username/images/s1/1.jpg', '/home/username/images/s1/2.jpg', '/home/username/images/s1/12.jpg', '/home/username/images/s1/11.jpg', '/home/username/images/s1/8.jpg']
sorted_list = sorted(l, key = lambda x: int(x.split("/")[-1].split(".")[0]))

输出
['/home/username/images/s1/1.jpg',
 '/home/username/images/s1/2.jpg',
 '/home/username/images/s1/3.jpg',
 '/home/username/images/s1/4.jpg',
 '/home/username/images/s1/5.jpg',
 '/home/username/images/s1/6.jpg',
 '/home/username/images/s1/7.jpg',
 '/home/username/images/s1/8.jpg',
 '/home/username/images/s1/9.jpg',
 '/home/username/images/s1/10.jpg',
 '/home/username/images/s1/11.jpg',
 '/home/username/images/s1/12.jpg']

1
我想你也应该补充一点,与 .sort() 不同,sorted() 不会就地对列表进行排序,因此我们需要保存列表。例如: l = sorted(l, key=lambda i: int(os.path.splitext(os.path.basename(i))[0])) - Kancha
1
@Varun 谢谢,我没想到那个。 :) - Tbaki

1
其他回答已经很好了。但是无论如何,我想发表我的带有一些解释的回答。
from os.path import basename,splitext
path_list = ['/home/username/images/s1/4.jpg', '/home/username/images/s1/7.jpg',
             '/home/username/images/s1/6.jpg', '/home/username/images/s1/3.jpg',
             '/home/username/images/s1/5.jpg', '/home/username/images/s1/10.jpg',
             '/home/username/images/s1/9.jpg', '/home/username/images/s1/1.jpg',
             '/home/username/images/s1/2.jpg', '/home/username/images/s1/12.jpg',
             '/home/username/images/s1/11.jpg', '/home/username/images/s1/8.jpg']

new_list = [splitext(basename(x))[0] for x in path_list]

fin_list = list(zip(path_list,new_list))

fin_list = [x[0] for x in sorted(fin_list,key=lambda x: int(x[1]))]

print(fin_list)

1)创建一个仅包含文件名的列表。1, 2, ...等。

new_list = [splitext(basename(x))[0] for x in path_list]

注意:为什么是[0]?因为每个splitext(basename(x))[0]的输出结果会像这样,

('1','.jpg') , ('4','.jpg')

所以,[0] 0th 索引只给我们文件名!

2) zip 每个可迭代物品与彼此一起,并创建一个列表。因此,此列表具有以下值,

fin_list = list(zip(path_list,new_list))
#output
('/home/username/images/s1/4.jpg','4.jpg')

3) [x[0] for x in sorted(fin_list,key=lambda x: int(x[1]))]

这段代码从已排序的列表fin_list中创建一个列表,注意key是关键。Key将是元组中的第二个项目,即4,3,7,...等。基于此进行排序。

最终的输出结果

['/home/username/images/s1/1.jpg', '/home/username/images/s1/2.jpg',
 '/home/username/images/s1/3.jpg', '/home/username/images/s1/4.jpg',
 '/home/username/images/s1/5.jpg', '/home/username/images/s1/6.jpg', 
'/home/username/images/s1/7.jpg', '/home/username/images/s1/8.jpg',
 '/home/username/images/s1/9.jpg', '/home/username/images/s1/10.jpg',
 '/home/username/images/s1/11.jpg', '/home/username/images/s1/12.jpg']

0
借鉴Shir的回答,如果您的文件名是版本号,例如1.0.ext2.3.4.ext3.0.ext,您可以使用以下代码:
import re
from pathlib import Path

files = Path('/your/path/here').glob('*.ext')

files = [
    f for f in files
    if re.match("[0-9]+\.[0-9]+\.?[0-9]*", f.stem)
]

files = sorted(
    files,
    key=lambda s: [int(u) for u in s.stem.split('.')]
)

0

我觉得这很棒

from pathlib import Path  # pathlib comes with python
sorted_files = sorted(files, key=lambda image_path: Path(image_path).name)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接