如何按数字顺序对文件进行排序?

45

我正在处理一个目录中的一些文件,需要对这些文件进行数字排序。我在wiki.python.org上找到了一些关于排序的示例,特别是使用lambda模式的示例,然后我把它们组合起来:

import re

file_names = """ayurveda_1.tif
ayurveda_11.tif
ayurveda_13.tif
ayurveda_2.tif
ayurveda_20.tif
ayurveda_22.tif""".split('\n')

num_re = re.compile('_(\d{1,2})\.')

file_names.sort(
    key=lambda fname: int(num_re.search(fname).group(1))
)

有更好的方法吗?


5
为一个合适的问题标题点赞。 - Geoffrey
4
你正在做的事情的正确方式是在问题部分只问问题,然后在回答部分添加你的答案。然后坐下来等待…… - paxdiablo
@paxdiablo:感谢您的指导……我已经阅读了常见问题解答,以确保我能够回答问题,只是对具体操作不太确定。下次我会做得更好。 - Zach Young
没问题,Zachary,只是“我怎么做xyzzy?”这个问题更有用(更可能引出各种可能的答案),而不是“我已经做了xyzzy。你觉得我的方法怎么样?” :-) - paxdiablo
6个回答

70

这被称为“自然排序”或“人类排序”(与默认的词典序排序相对)。Ned B写了一个快速版本。

import re

def tryint(s):
    try:
        return int(s)
    except:
        return s

def alphanum_key(s):
    """ Turn a string into a list of string and number chunks.
        "z23a" -> ["z", 23, "a"]
    """
    return [ tryint(c) for c in re.split('([0-9]+)', s) ]

def sort_nicely(l):
    """ Sort the given list in the way that humans expect.
    """
    l.sort(key=alphanum_key)

这与你所做的类似,但可能更加通用。


2
谢谢你,Daniel!这正是我在寻找的。我跟着你提供的链接进入了一个兔子洞... 嘿嘿!我学到了一些关于try/except性能和(当然)预编译regexps的知识。 :) - Zach Young
如果我们返回一个生成器而不是列表推导,这样会起作用吗? - Karl Knechtel
无法正确处理负嵌入数字。 - martineau
@martineau:我明白正则表达式只在数字处分割,因此任何符号字符都会在数字前面的组中。由于这只是从1开始的索引文件列表,我认为这不会成为问题。 - Zach Young
2
@Zachary Young:我猜你可能不重视处理负数,但我评论只是为其他人引起注意(毕竟,你的问题只是说“数字”)。这很容易解决,只需使用re.split('(-*[0-9]+)', s)即可……甚至更普遍地,可以通过使用re.split('(-*\d+\.\d*)' , s)来处理[有符号]实数,例如-3.14。最后,如果您不想定义一个单独的函数像sort_nicely(),您可以像您在问题代码中所做的那样使用tiffFiles.sort(key=alphanum_key) - martineau
1
如果使用实数,应该将数字转换为浮点数而不是整数(即创建一个tryfloat(s)函数而不是tryint(s))。 - MD004

20

只需使用:

tiffFiles.sort(key=lambda var:[int(x) if x.isdigit() else x for x in re.findall(r'[^0-9]|[0-9]+', var)])

使用try/except相比之下要慢。


1
如果文件名包含例如“②”字符,则此操作将失败。 - reika

5
如果您在排序方法中使用key=,则不应使用已从Python的最新版本中删除的cmpkey应与一个函数等同,该函数将记录作为输入,并返回任何对象,以便按您希望对列表进行排序的顺序进行比较。它不需要是lambda函数,并且可能作为一个独立的函数更清晰。另外,正则表达式可能评估得很慢。
您可以尝试像以下这样隔离并返回文件名的整数部分:
def getint(name):
    basename = name.partition('.')
    alpha, num = basename.split('_')
    return int(num)
tiffiles.sort(key=getint)

谢谢你,唐。我非常感谢你的解释:非常易懂。--扎卡里 - Zach Young
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - KnightWhoSayNi

5

@AprilHow is Pythons glob.glob ordered? 提供了一个不错的解决方案,你可以尝试一下。

#First, get the files:
import glob
import re

files = glob.glob1(img_folder,'*'+output_image_format)

# Sort files according to the digits included in the filename
files = sorted(files, key=lambda x:float(re.findall("(\d+)",x)[0]))

0
这是@Don O'Donnell答案的修改版本,因为我无法按原样使其工作,但我认为这是最好的答案,因为它解释得很清楚。
def getint(name):
    _, num = name.split('_')
    num, _ = num.split('.')
    return int(num)

print(sorted(tiffFiles, key=getint))

变更:

1)alpha字符串不会被存储,因为它不是必需的(因此使用_, num

2)使用num.split('.')来分离数字和.tiff

3)根据https://docs.python.org/2/howto/sorting.html使用sorted而不是list.sort


0

元组中的分区结果

def getint(name):
    (basename, part, ext) = name.partition('.')
    (alpha, num) = basename.split('_')
    return int(num)

你真的尝试过那个吗?(a, b, c) = 'ayurveda_11.tif'.split('.'),抛出值错误:需要多于2个值进行解包。 - Zach Young

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