如何在Python中自然排序Pathlib对象?

13

我正在尝试创建一个已排序的文件列表,其中包含在./pages目录中的文件。这是我目前的进展:


import numpy as np
from PIL import Image
import glob
from pathlib import Path


# sorted( l, key=lambda a: int(a.split("-")[1]) )
image_list = []

for filename in Path('./pages').glob('*.jpg'):
#     sorted( i, key=lambda a: int(a.split("_")[1]) )
#     im=Image.open(filename)
    image_list.append(filename)

print(*image_list, sep = "\n")

当前输出:

pages/page_1.jpg  
pages/page_10.jpg  
pages/page_11.jpg  
pages/page_12.jpg  
pages/page_2.jpg  
pages/page_3.jpg  
pages/page_4.jpg  
pages/page_5.jpg  
pages/page_6.jpg  
pages/page_7.jpg  
pages/page_8.jpg  
pages/page_9.jpg  

预期输出:

pages/page_1.jpg   
pages/page_2.jpg  
pages/page_3.jpg  
pages/page_4.jpg  
pages/page_5.jpg  
pages/page_6.jpg  
pages/page_7.jpg  
pages/page_8.jpg  
pages/page_9.jpg  
pages/page_10.jpg  
pages/page_11.jpg  
pages/page_12.jpg

我已经尝试了重复的解决方案,但它们不起作用,因为pathlib文件是类对象,而不是字符串。只有在打印时才会显示为文件名。

例如:

print(filename) # pages/page_1.jpg  
print(type(filename)) # <class 'pathlib.PosixPath'>

最终,这是可以运行的代码。感谢大家。

from pathlib import Path
import numpy as np
from PIL import Image
import natsort

def merge_to_single_image():
    image_list1 = []
    image_list2 = []
    image_list3 = []
    image_list4 = []

    for filename in Path('./pages').glob('*.jpg'):
        image_list1.append(filename)

    for i in image_list1:
        image_list2.append(i.stem)
    #     print(type(i.stem))

    image_list3 = natsort.natsorted(image_list2, reverse=False)

    for i in image_list3:
        i = str(i)+ ".jpg"
        image_list4.append(Path('./pages', i))

    images = [Image.open(i) for i in image_list4]
    # for a vertical stacking it is simple: use vstack
    images_combined = np.vstack(images)
    images_combined = Image.fromarray(images_combined)
    images_combined.save('Single_image.jpg')

所有文件都有相同的 page_ 前缀吗? - accdias
文件名是由我生成的,所以page_不是必须的。它也可以是1.jpg、2.jpg、3.jpg、...、10.jpg、11.jpg。 - PrasadHeeramani
你只需先将路径对象转换为字符串。试试这个: for filename in sorted(Path('./pages').glob('*.jpg'), key=lambda a: int(str(a).split("_")[1])): - Lord Elrond
解决方案:由于文件名是由您创建的,请编写添加填充零的文件名,例如下面的示例。排序将变得容易。 - imbr
5个回答

5

只是为了记录,也许这更加简明?

natsorted(list_of_pathlib_objects, key=str)

1
请注意,此代码使用了 natsort 模块,该模块需要安装,因为它不是标准库的一部分。 - Thierry Lathuille

5

可以使用 natsort 库pip install natsort)进行排序。它看起来也很简单。
[! 这是有效的,至少已经在版本 5.5 和 7.1(当前)中测试过]

from natsort import natsorted

image_list = Path('./pages').glob('*.jpg')
image_list = natsorted(image_list, key=str)

# Or convert list of paths to list of string and (naturally)sort it, then convert back to list of paths
image_list = [Path(p) for p in natsorted([str(p) for p in image_list ])]

如果路径有不同的父级,请查看docs - Jaja

4
就像这样使用...
from pathlib import Path

- 按姓名排序:

sorted(Path('anywhere/you/want').glob('*.jpg'))

- 按修改时间排序:
import os
sorted(Path('anywhere/you/want').glob('*.jpg'), key=os.path.getmtime)

- 按大小排序:

import os
sorted(Path('anywhere/you/want').glob('*.jpg'), key=os.path.getsize)

等等。

提示:由于文件名也是由您创建的。请使用填充零的方式编写文件名,例如:

for i in range(100):        
    with open('filename'+f'_{i:03d}','wb'):  # py3.6+ fstring        
       # write your file stuff...
    # py3.3+ 'filename'+'_{:03d}'.format(i) for str.format()
 ...
 'filename_007',
 'filename_008',
 'filename_009',
 'filename_010',
 'filename_011',
 'filename_012',
 'filename_013',
 'filename_014',
 ...

3
请注意,sorted不会直接对你的数据进行排序,而是返回一个新列表,因此你必须对其输出进行迭代。
为了获取你的排序关键字,也就是文件名末尾的整数值:
- 你可以先获取路径的stem,也就是不带扩展名的最后一个组件(例如'page_13')。 - 然后,最好从右边拆分一次,以防文件名的第一部分包含其他下划线,比如'some_page_33.jpg'。 - 转换为int后,你就得到了所需的排序关键字。
因此,你的代码可能如下所示:
for filename in sorted(Path('./pages').glob('*.jpg'), 
                       key=lambda path: int(path.stem.rsplit("_", 1)[1])):

    print(filename)

示例输出:

pages/ma_page_2.jpg
pages/ma_page_11.jpg
pages/ma_page_13.jpg
pages/ma_page_20.jpg

1
这个问题并不像听起来那么简单,“自然”排序可能会很具有挑战性,特别是对于潜在的任意输入字符串,例如,如果您的数据中有“69_helloKitty.jpg”怎么办? 我曾经使用https://github.com/SethMMorton/natsort解决了类似的问题,也许它能帮到你。

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