Skip to main content
 首页 » 编程设计

python之在 Python 中,如何自然地对字母数字字符串列表进行排序,使字母字符排在数字字符之前

2025年05月04日21sxdcgaq8080

这是我最近遇到的一个有趣的小挑战。我将在下面提供我的答案,但我很好奇是否有更优雅或更高效的解决方案。

向我描述的要求:

  1. 字符串是字母数字(见下面的测试数据集)
  2. 字符串应该自然排序(解释见this question)
  3. 字母字符应排在数字字符之前(即“abc”在“100”之前)
  4. 字母字符的大写实例应排在小写实例之前(即“ABc”、“Abc”、“abc”)

这是一个测试数据集:

test_cases = [ 
    # (unsorted list, sorted list) 
    (list('bca'), ['a', 'b', 'c']), 
    (list('CbA'), ['A', 'b', 'C']), 
    (list('r0B9a'), ['a', 'B', 'r', '0', '9']), 
    (['a2', '1a', '10a', 'a1', 'a100'], ['a1', 'a2', 'a100', '1a', '10a']), 
    (['GAM', 'alp2', 'ALP11', '1', 'alp100', 'alp10', '100', 'alp1', '2'], 
        ['alp1', 'alp2', 'alp10', 'ALP11', 'alp100', 'GAM', '1', '2', '100']), 
    (list('ra0b9A'), ['A', 'a', 'b', 'r', '0', '9']), 
    (['Abc', 'abc', 'ABc'], ['ABc', 'Abc', 'abc']), 
] 

奖励测试用例

这是受 Janne Karila's comment 的启发低于所选答案当前失败(但在我的情况下并不是真正的实际问题):

(['0A', '00a', 'a', 'A', 'A0', '00A', '0', 'a0', '00', '0a'], 
        ['A', 'a', 'A0', 'a0', '0', '00', '0A', '00A', '0a', '00a']) 

请您参考如下方法:

re_natural = re.compile('[0-9]+|[^0-9]+') 
 
def natural_key(s): 
    return [(1, int(c)) if c.isdigit() else (0, c.lower()) for c in re_natural.findall(s)] + [s] 
 
for case in test_cases: 
    print case[1] 
    print sorted(case[0], key=natural_key) 
 
['a', 'b', 'c'] 
['a', 'b', 'c'] 
['A', 'b', 'C'] 
['A', 'b', 'C'] 
['a', 'B', 'r', '0', '9'] 
['a', 'B', 'r', '0', '9'] 
['a1', 'a2', 'a100', '1a', '10a'] 
['a1', 'a2', 'a100', '1a', '10a'] 
['alp1', 'alp2', 'alp10', 'ALP11', 'alp100', 'GAM', '1', '2', '100'] 
['alp1', 'alp2', 'alp10', 'ALP11', 'alp100', 'GAM', '1', '2', '100'] 
['A', 'a', 'b', 'r', '0', '9'] 
['A', 'a', 'b', 'r', '0', '9'] 
['ABc', 'Abc', 'abc'] 
['ABc', 'Abc', 'abc'] 

编辑:我决定重新审视这个问题,看看是否有可能处理奖金案件。它需要在 key 的决胜局部分更加复杂。为了匹配所需的结果,必须在数字部分之前考虑键的字母部分。我还在键的自然部分和决胜局之间添加了一个标记,以便短键始终位于长键之前。

def natural_key2(s): 
    parts = re_natural.findall(s) 
    natural = [(1, int(c)) if c.isdigit() else (0, c.lower()) for c in parts] 
    ties_alpha = [c for c in parts if not c.isdigit()] 
    ties_numeric = [c for c in parts if c.isdigit()] 
    return natural + [(-1,)] + ties_alpha + ties_numeric 

这会为上面的测试用例生成相同的结果,加上奖励用例所需的输出:

['A', 'a', 'A0', 'a0', '0', '00', '0A', '00A', '0a', '00a']