如何按数字顺序排序glob.glob的结果?

7

我有一堆按数字排序的文件在一个文件夹里,当我尝试使用glob.glob进行排序时,我从来没有得到正确顺序的文件。


文件示例和期望输出排序

folder
------
C:\Users\user\Desktop\folder\1 sample.mp3
C:\Users\user\Desktop\folder\2 sample.mp3
C:\Users\user\Desktop\folder\3 sample.mp3
C:\Users\user\Desktop\folder\4 sample.mp3
C:\Users\user\Desktop\folder\5 sample.mp3
... over 800 files...

我尝试过的,但输出似乎是随机的

files = sorted(glob.glob(f'{os.getcwd()}/*.mp3'), key=lambda x: (os.path.splitext(os.path.basename(x))[0]))


C:\Users\user\Desktop\folder\1 speech.mp3
C:\Users\user\Desktop\folder\10 speech.mp3 
C:\Users\user\Desktop\folder\100 speech.mp3
C:\Users\user\Desktop\folder\101 speech.mp3
C:\Users\user\Desktop\folder\102 speech.mp3
C:\Users\user\Desktop\folder\103 speech.mp3  
C:\Users\user\Desktop\folder\104 speech.mp3
C:\Users\user\Desktop\folder\105 speech.mp3
C:\Users\user\Desktop\folder\106 speech.mp3
C:\Users\user\Desktop\folder\107 speech.mp3
C:\Users\user\Desktop\folder\108 speech.mp3
C:\Users\user\Desktop\folder\109 speech.mp3
C:\Users\user\Desktop\folder\11 speech.mp3 

尝试按日期或大小进行排序并不是解决方案。

更新 之前的所有答案都很好:

l = sorted(glob.glob(f'{os.getcwd()}/*.mp3'), key=len)
l = sorted(glob.glob(f'{os.getcwd()}/*.mp3'), key=lambda x: int(os.path.basename(x).split(' ')[0]))

def get_key(fp):
    filename = os.path.splitext(os.path.basename(fp))[0]
    int_part = filename.split()[0]
    return int(int_part)

l = sorted(glob.glob(f'{os.getcwd()}/*.mp3'), key=get_key)
4个回答

6

试试这个:

import glob
import os
files = sorted(glob.glob(f'{os.getcwd()}/*.txt'), key=len)
print(files)

这很好,你能解释一下为什么用len它能正常工作吗? - Y4RD13
2
因为C:\Users\user\Desktop\folder\10 speech.mp3C:\Users\user\Desktop\folder\1 speech.mp3更长,所以它们将按相同长度排序。 - user13914826
这个答案不太可行。路径长度 /11.mp3 与路径 /10.mp3 的长度相同。对于密钥,你应该使用像 Jonas 答案中的 lambda 函数。还可以在https://ubuntuforums.org/showthread.php?t=2114348 中查看示例。在我的情况下,我没有使用 os.getcwd,而是使用了一个变量来表示我的 images_path,例如:files = sorted(glob.glob(f'{images_path}/*.{extension}'), key=lambda a: int(a.replace(images_path + '/', '').replace(f'.{extension}', ''))) 。虽然我做了两次替换,但它非常易读和易于理解,希望能对你有所帮助。 - firepol

5

通常使用 re.match() 捕捉数字并使用 int() 将该数字(字符串)转换为整数。使用这些数字使用 sorted() 对文件进行排序。

代码

import re 
import math
from pathlib import Path 

file_pattern = re.compile(r'.*?(\d+).*?')
def get_order(file):
    match = file_pattern.match(Path(file).name)
    if not match:
        return math.inf
    return int(match.groups()[0])

sorted_files = sorted(files, key=get_order)

示例输入

考虑在文件名的任何地方都有一个整数的随机文件:

├── 012 some file.mp3
├── 1 file.txt
├── 13 file.mp3
├── 2 another file.txt
├── 3 file.csv
├── 4 file.mp3
├── 6 yet another file.txt
├── 88 name of file.mp3
├── and final 999.txt
├── and some another file7.txt
├── some 5 file.mp3
└── test.py

示例输出

get_order() 可用于对文件进行排序,当作为 key 参数传递给内置函数 sorted() 时。

In [1]: sorted(files, key=get_order)
Out[1]:
['C:\\tmp\\file_sort\\1 file.txt',
 'C:\\tmp\\file_sort\\2 another file.txt',
 'C:\\tmp\\file_sort\\3 file.csv',
 'C:\\tmp\\file_sort\\4 file.mp3',
 'C:\\tmp\\file_sort\\some 5 file.mp3',
 'C:\\tmp\\file_sort\\6 yet another file.txt',
 'C:\\tmp\\file_sort\\and some another file7.txt',
 'C:\\tmp\\file_sort\\012 some file.mp3',
 'C:\\tmp\\file_sort\\13 file.mp3',
 'C:\\tmp\\file_sort\\88 name of file.mp3',
 'C:\\tmp\\file_sort\\and final 999.txt',
 'C:\\tmp\\file_sort\\test.py']

简短说明

  • re.compile 的作用是为了提高速度(如果匹配多次相同的模式)。
  • re.match 用于匹配正则表达式模式。
  • 在正则表达式模式中,.*? 表示匹配任意字符(.),零次或多次(*),非贪婪地 (?)。\d+ 匹配一个或多个数字,并将其捕获到 groups() 列表中。
  • 如果没有匹配(文件中没有数字),则 match 将为 None,并且 get_order 将返回无穷大;这些文件将按任意顺序排序,但可以添加逻辑处理它们(本题未要求)。
  • sorted() 函数接受 key 参数,该参数应为可调用对象,接受一个参数:列表中的项。在此情况下,它将是那些文件字符串(完整文件路径)中的一个。
  • Path(file).name 只取完整文件路径中的文件名部分(不包括后缀)。

2

Here you go:

import os, glob

def get_key(fp):
    filename = os.path.splitext(os.path.basename(fp))[0]
    int_part = filename.split()[0]
    return int(int_part)

files = sorted(glob.glob(f'{os.getcwd()}/*.mp3'), key=get_key)

2

看起来你正在分割完整路径而不是基本名称。你可以尝试这样做:

files = sorted(glob.glob(f'{os.getcwd()}/*.mp3'), key=lambda x: int(os.path.basename(x).split(' ')[0]))

这将给我与在更新的问题中显示相同的结果 - Y4RD13
1
也许你需要将它转换为int():key=lambda x: int(os.path.basename(x).split(' ')[0])) - Jonas
1
是的,我的错。之前所有的答案都可以工作,但这个最接近我所做的事情。 - Y4RD13

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