Schwartzian在“Python中的文本处理”中的排序示例

时间:2009-01-20 20:47:27

标签: python

我浏览了“Python中的文本处理”并尝试了example关于Schwartzian排序。

我使用以下结构来获取也包含空行的样本数据。我按第五栏对这些数据进行了排序:
383230 -49 -78 1 100034 '06 text'9562'text'720'text'8667
335067 -152 -18 3 100030 'text'2400'text'2322'text'696
136592 21 230 3 100035 '03。 text'10368'text'1838'text'977

用于Schwartzian排序的代码:

for n in range(len(lines)):       # Create the transform
    lst = string.split(lines[n])
    if len(lst) >= 4:             # Tuple w/ sort info first
        lines[n] = (lst[4], lines[n])
    else:                         # Short lines to end
        lines[n] = (['\377'], lines[n])

lines.sort()    # Native sort

for n in range(len(lines)):       # Restore original lines
    lines[n] = lines[n][1]

open('tmp.schwartzian','w').writelines(lines)

我不知道作者是如何使用此代码将短行或空行放到文件末尾的。行在if-else结构之后排序,从而将空行提升到文件顶部。当然,短行当像在示例中实现的自定义排序(fourth_word函数)一样工作。

这现在困扰着我,所以任何想法?如果我对此是正确的,那么你如何确保短线实际上保留在文件末尾?

编辑:我注意到'\ 377'周围的方括号。这搞砸了sort()所以我删除了那些括号并​​输出开始工作。

else:                         # Short lines to end
    lines[n] = (['\377'], lines[n])
print type(lines[n][0])
>>> (type 'list')

我接受了nosklo的回答,以便对'\ 377'的含义及其改进的算法进行详细说明。非常感谢其他答案!

如果好奇的话,我使用2 MB样本文件,使用自定义排序花费0.95秒,使用Schwartzian排序花费0.09,同时创建相同的输出文件。它有效!

5 个答案:

答案 0 :(得分:2)

与问题没有直接关系,但请注意在python的最新版本中(我认为是2.3或2.4),可以使用key参数自动执行转换和非转换,或sort()sorted()。例如:

def key_func(line):
    lst = string.split(line)
    if len(lst) >= 4:             
        return lst[4]
    else:                        
        return '\377'

lines.sort(key=key_func)

答案 1 :(得分:1)

我不知道这是什么问题,所以我会尝试以一般方式澄清事情。

此算法通过获取第4个字段并将其放在行的前面来对行进行排序。然后内置的sort()将使用此字段进行排序。稍后原始线路将恢复。

空行或短于5个字段的行落入此结构的else部分:

if len(lst) >= 4:             # Tuple w/ sort info first
    lines[n] = (lst[4], lines[n])
else:                         # Short lines to end
    lines[n] = (['\377'], lines[n])

它会在列表的第一个字段中添加['\377']进行排序。该算法希望'\ 377'(ascii表中的最后一个字符)将更大比在第5个字段中找到的任何字符串。因此,在进行排序时,原始行应该排在最后。

我希望澄清这个问题。如果没有,也许你应该指出exaclty你想知道什么。

同一算法的更好的通用版本:

sort_by_field(list_of_str, field_number, separator=' ', defaultvalue='\xFF')
    # decorates each value:
    for i, line in enumerate(list_of_str)):
        fields = line.split(separator)
        try:
             # places original line as second item:
            list_of_str[i] = (fields[field_number], line)
        except IndexError:
            list_of_str[i] = (defaultvalue, line)
    list_of_str.sort() # sorts list, in place
    # undecorates values:
    for i, group in enumerate(list_of_str))
        list_of_str[i] = group[1] # the second item is original line

您提供的算法与此算法相同。

答案 2 :(得分:0)

空行不会通过测试

if len(lst) >= 4:

所以它会将['\ _ 377']作为其排序键,而不是数据的第5列,即lst[4]lst[0]是第一列)。

答案 3 :(得分:0)

好吧,它会在最后对几乎的短行进行排序,但并不总是如此。

实际上,“幼稚”和施瓦兹版都有缺陷(以不同的方式)。 Nosklo和wbg已经解释了算法,如果你试图在schwartzian版本中找到错误,你可能会学到更多,因此我现在只给你一个提示:

  

包含特定文字的长行   在第四列中将稍后排序   而不是短线。

如果您需要更多帮助,请添加评论。

答案 4 :(得分:0)

尽管Schwartzian变换的使用已经过时了,但值得一提的是,你可以用这种方式编写代码,以避免以\377开头的行[4]的行被分类到错误的地方

for n in range(len(lines)):
    lst = lines[n].split()
    if len(lst)>4:
        lines[n] = ((0, lst[4]), lines[n])
    else:
        lines[n] = ((1,), lines[n])

由于元组比较元组,因此以1开头的元组将always排序到底部。

另请注意,测试应为len(list)>4而非>=

当使用现代等价的AKA key=函数

时,同样的逻辑也适用
def key_func(line):
        lst = line.split()
        if len(lst)>4:
            return 0, lst[4]
        else:
            return 1,

lines.sort(key=key_func)