python字符串之间的自然比较?

时间:2011-12-06 22:58:34

标签: python unix sorting

Python是否具有在两个字符串之间进行自然排序的快速功能?不一定排序,只是一个返回0,-1或1的比较函数,具体取决于自然顺序或相同的前面。

编辑:建议的功能是正确的,但它太慢了。如何在Python中快速完成?

注意这不是许多人建议的帖子的重复 - 因为这些其他线程无法解决效率问题。当前的解决方案工作正常,但是对每一行进行正则表达式调用,这非常昂贵。我想要一个有效的解决方案,可用于进行数百万次比较。

3 个答案:

答案 0 :(得分:5)

cmp是内置函数。

>>> a = 'hello'
>>> b = 'world'
>>> cmp(a, b)
-1

编辑: “自然排序”您是指人类会对数字进行排序吗?如果是这种情况,那么this可能是一个秘诀。

答案 1 :(得分:5)

改编自这个问题的答案:Does Python have a built in function for string natural sort?

import re

def nat_cmp(a, b):
    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 cmp(alphanum_key(a), alphanum_key(b))

print nat_cmp('foo10z', 'foo100z')
print cmp('foo10z', 'foo100z')  # <- notice the builtin yields a different result

输出:

-1
1

<强>更新

使用ipython定时(使用示例输入):

In [1]: %timeit nat_cmp('foo10z', 'foo100z')
100000 loops, best of 3: 11.6 us per loop

更新2

说到性能......与pure-python代码相比,我不确定你理解re lib实际上有多快。为了演示,我已经使用了关键函数(带有re的部分),并在pure-python中重写了几次,并将它们的速度与更简单的re.split的使用进行了比较。

import re
from itertools import groupby

def regex_key(key):
    """Traditional, regular-expression-based nat-sort key."""
    convert = lambda text: int(text) if text.isdigit() else text.lower()
    return [convert(c) for c in re.split('([0-9]+)', key)]

def fast_key(value):
    """Attempt #1 to go faster than 'slow' 're' library."""
    result = []
    for is_int, chunk in groupby(value.lower(), str.isdigit):
        if is_int:
            result.append(int(''.join(chunk)))
        else:
            result.append(tuple(chunk))
    return result

def faster_key(value):
    """Attempt #2.  'Low-level' python."""
    start_idx = 0
    is_num = None
    result = []
    for idx, c in enumerate(value.lower()):
        now_is_num = c.isdigit()
        if is_num is not None and now_is_num != is_num:
            buf = value[start_idx:idx]
            result.append(int(buf) if is_num else buf)
            start_idx = idx
            is_num = None
        is_num = now_is_num
    buf = value[start_idx:]
    result.append(int(buf) if is_num else buf)
    return result

接下来,我针对一个简单的基准测试运行这些:

from datetime import datetime

def benchmark(fn):
    print "Benching %s (run 1000 times)" % fn.__name__

    start = datetime.now()
    for x in xrange(1000):
        # run key function on something approx 100 chars long
        fn('asdf1234sdfg234jhd88123j2134 - 123d34123djfsk'*2)
    print "\t%s" % (datetime.now() - start)

benchmark(regex_key)
benchmark(fast_key)
benchmark(faster_key)

结果如下:

Benching regex_key (run 1000 times)
    0:00:00.025908
Benching fast_key (run 1000 times)
    0:00:00.065567
Benching faster_key (run 1000 times)
    0:00:00.042654

现在,我确信有些事情可以让我的key-func实现更快,但除非我遗漏了一些巨大的东西,否则很难像re.split一样快速代码(使用pure-python,即)。

答案 2 :(得分:2)

这将允许您自然地对字符串列表进行排序:

import re

unsorted_list = ["a1", "a2", "a11", "b1", "b2", "b11"]

def natural_key(s):
    return [ int(c) if c.isdigit() else c for c in re.split(r'(\d+)', s) ]

sorted_list = sorted(unsorted_list, key = lambda x : natural_key(x))

print sorted_list

这将返回-1,0或1,具体取决于x&gt; ÿ

def natural_key(x, y):
     x = [int(c) if c.isdigit() else c for c in re.split(r'(\d+)', x)]
     y = [int(c) if c.isdigit() else c for c in re.split(r'(\d+)', y)]
     if x == y:
          return 0
     elif x > y:
          return 1
     else:
          return -1

这适用于python 2.X和3.X