文本文件中的perl就地排序行

时间:2011-04-22 22:39:29

标签: python perl

我希望通过基于给定密钥对每一行进行排序来修改文本文件,并将旧文件保存为备份。键是每行中包含的数字字符。

是否有一个简单的脚本来完成这项工作,最好是就地?

谢谢!

3 个答案:

答案 0 :(得分:1)

存在具有O(n log n)复杂度的就地排序算法,例如Heapsort,但我不明白为什么你想要使用它而不是简单的东西,比如Unix {{1命令。除非你有严格的性能要求或庞大的数据集......但是,perl和python可能不是最好的工具。

答案 1 :(得分:1)

假设您的排序键是每行开头的数字运行,如下例所示。

5 Fine
2 Good
1 Every
4 Does
3 Boy

要对命令行中指定的一个或多个文件进行排序,您可以使用以下代码。

#! /usr/bin/env perl

use strict;
use warnings;

die "Usage: $0 file ..\n" unless @ARGV;

$^I = ".bak";
undef $/;

while (<>) {
  print map $_->[0],
        sort { $a->[1] <=> $b->[1] }
        map { [ $_, /^(\d+)/ ? $1 : -1 ] }
        /^(.*\n?)/mg;
}

@ARGV包含命令行中的参数。运行没有参数的程序会产生标准错误的使用指南。

$^I保留在为就地编辑创建备份时添加到文件名的扩展名,您也可以使用perlrun documentation中涵盖的Perl的-i切换启用该扩展程序。

  

<强> -i [延伸]
  指定由<>构造处理的文件将进行就地编辑。它通过重命名输入文件,按原始名称打开输出文件,并选择该输出文件作为print语句的默认值来完成此操作。

$/是输入记录分隔符。将其设置为未定义的值意味着您希望后续调用readline operator来读取文件结尾。性能将因非常大的输入而受到影响。

while循环的每次迭代中,特殊变量$_将整个保存当前文件的内容。为了对线条进行排序,我们首先将它们分开。

不要被循环内的print吓倒。这是Schwartzian Transform,这是Perl中的常用技术,即使它首次亮相less-than-rave reviews。要了解发生了什么,请从头到尾阅读。

  1. 捕获当前文件中所有行的列表。 /m正则表达式开关使^在行的开头匹配,而不仅仅是在目标字符串的开头。
  2. 对于每一行,尝试捕获该行开头的一个或多个数字,或者默认为-1。
  3. 按排序键的升序对行进行排序。
  4. 最后,按排序顺序打印行。启用就地编辑后,print输出到正在排序的当前文件。
  5. 在更程序化的风格中,你将循环编写为

    while (<>) {
      my @lines = /^(.*\n?)/mg;
      my @augmented = map { [ $_, /^(\d+)/ ? $1 : -1 ] } @lines;
      my @sorted = sort { $a->[1] <=> $b->[1] } @augmented;
      print map $_->[0], @sorted;
    }
    

    一旦你理解了Schwartzian变换正在发生的事情,所有临时工作看起来都是过分的混乱。

答案 2 :(得分:0)

没有一个简单的脚本可以做到这一点,因为你所建议的实际上是相当复杂和低效的。除非你的行在文件中的长度完全相同,否则几乎是不可能的(或者,非常愚蠢)。

如果你绝对不能在内存中做,并且想自己编写代码,那么你最好的方法可能是disk based merge sort。有关如何使用磁带机执行此操作的示例应该为您提供一些指导。