在perl中排序文本块

时间:2012-06-24 17:12:06

标签: regex perl sed grep

我有一个看起来像的文件:

SECTION1 id name  
 sub section1
 sub section2
SECTION2 id name  
 sub section3
 sub section4
 sub section6
SECTION1 id name  
 sub section7
 sub section8
SECTION3 id name  
 sub section9
 sub section10
 sub section11
 sub section12
SECTION2 id name  
 sub section13
 sub section14
SECTION1 id name  
 sub section15
 sub section16
SECTION3 id name  
 sub section17
 sub section18

我需要明智地对此文件进行排序。我唯一知道的是我有'SECTION1','SECTION2'和'SECTION3'。 排序后的预期输出为:

SECTION1 id name  
 sub section1
 sub section2
SECTION1 id name  
 sub section7
 sub section8
SECTION1 id name  
 sub section15
 sub section16
SECTION2 id name  
 sub section3
 sub section4
 sub section6
SECTION2 id name  
 sub section13
 sub section14
SECTION3 id name  
 sub section9
 sub section10
 sub section11
 sub section12
SECTION3 id name  
 sub section17
 sub section18

在perl中使用grep,sed等实用程序有没有简单的方法?

5 个答案:

答案 0 :(得分:3)

使用perl的另一种方式:

假设infile包含问题的内容以及script.pl的以下内容:

use warnings;
use strict;
use sort qw/stable/;

my ($section, @section);

while ( <> ) { 

    ## Save text if first line or when line doesn't begin with 'SECTION' word.
    if ( $. == 1 || $_ !~ m/\ASECTION\d+/ ) { 
        $section .= $_; 
        next unless eof;
    }   

    ## Save the text and the number of section.
    if ( $section =~ m/\ASECTION(\d+)/ ) { 
        push @section, [ $1, $section ];
        $section = q||;
    }   

    ## Begin to save next section.
    $section .= $_; 
}

## Print them sorted by section number.
for ( sort { $a->[0] <=> $b->[0] } @section ) { 
    printf qq|%s|, $_->[1];
}

像以下一样运行:

perl script.pl infile

使用以下输出:

SECTION1 id name  
 sub section1
 sub section2
SECTION1 id name  
 sub section7
 sub section8
SECTION1 id name  
 sub section15
 sub section16
SECTION2 id name  
 sub section3
 sub section4
 sub section6
SECTION2 id name  
 sub section13
 sub section14
SECTION3 id name  
 sub section9
 sub section10
 sub section11
 sub section12
SECTION3 id name  
 sub section17
 sub section18

答案 1 :(得分:3)

看起来像需要特殊排序的东西。 Perl的默认排序无法使用数字正确排序字符串,因此我们需要在排序之前提取数字。如果是大数据集,我使用Schwartzian transform优化它。

它的基本要点是首先提取部分编号,然后是子部分编号,然后先对部分编号进行排序,如果是平局,则对子部分编号进行排序。仅考虑子节中的第一个数字,因此它假定这些行已经排序。

要在文件中使用它,只需将<DATA>更改为<>,然后运行:

perl script.pl inputfile > outputfile

<强>代码:

use strict;
use warnings;

local $/;           # read entire file
my $data = <DATA>;  # slurp input file into scalar
my @records = split /(?=^SECTION)/m, $data;  # split into records
my @sorted =    map  {  $_->[0] }
                sort {  $a->[1] <=> $b->[1] ||
                        $a->[2] <=> $b->[2] }  
                map   { getnum($_) } @records;   # Schwartzian transform sort
print @sorted;

sub getnum {    # extract section and subsection numbers
    my ($sec) = $_[0] =~ /SECTION(\d+)/;
    my ($sub) = $_[0] =~ /\n.*?(\d+)/;
    return [ $_[0], $sec, $sub ];    # return anonymous array
}

__DATA__
SECTION1 id name  
 sub section1
 sub section2
SECTION2 id name  
 sub section3
 sub section4
 sub section6
SECTION1 id name  
 sub section7
 sub section8
SECTION3 id name  
 sub section9
 sub section10
 sub section11
 sub section12
SECTION2 id name  
 sub section13
 sub section14
SECTION1 id name  
 sub section15
 sub section16
SECTION3 id name  
 sub section17
 sub section18

答案 2 :(得分:1)

#!/usr/bin/perl
use strict;
use warnings;

my @data;
{   # limit change to $/ to this scope
    local $/ = "SECTION";
    @data = map {chomp; $_ || ()} <DATA>;   
}

{   # limit change to 'warnings' to this scope
    no warnings 'numeric';
    print "SECTION$_" for sort {$a <=> $b} @data;
}

这将保留各个部分。

或者从命令行:

perl -F/SECTION/ -0ane "print qq{SECTION$_} for grep $_, sort {$a <=> $b} @F" o33.txt

答案 3 :(得分:1)

这可能适合你(GNU sed):

sed ':a;$!N;/\nSECTION/!s/\n/\x00/;ta;s/n\([0-9][\x00\n]\|$\)/n0\1/g;P;D' file |
sort |
sed 's/\x00/\n/g;s/n0/n/g'

说明:

  • SECTIONssub sections加入单行。 :a;$!N;/\nSECTION/!s/\n/\x00/;ta
  • 0添加到sub sectionss/n\([0-9][\x00\n]\|$\)/n0\1/g
  • 打印每一行然后将其删除。 P;D
  • 对管道输出进行排序。 sort
  • 解构排序后的输出。 sed 's/\x00/\n/g;s/n0/n/g'

答案 4 :(得分:1)

这很简单,只需根据章节标签在三个sparate列表中累积记录即可。

此程序使用哈希来执行此操作,并通过将文件中的每一行附加到最新记录来构建完整的部分。如果该行是新部分的开头,则在追加该行之前将另一个空记录添加到列表中。

显示结果只是按照部分标签的顺序打印列表的所有元素。

use strict;
use warnings;

open my $fh, '<', 'sections.txt' or die $!;

my %sections;
my $current_list;

while (<$fh>) {
  if (/^(SECTION[123])/) {
    $current_list = $sections{$1} //= [];
    push @$current_list, '';
  }
  $current_list->[-1] .= $_ if $current_list;
}

for my $name (sort keys %sections) {
  print for @{ $sections{$name} };
}

<强>输出

SECTION1 id name  
 sub section1
 sub section2
SECTION1 id name  
 sub section7
 sub section8
SECTION1 id name  
 sub section15
 sub section16
SECTION2 id name  
 sub section3
 sub section4
 sub section6
SECTION2 id name  
 sub section13
 sub section14
SECTION3 id name  
 sub section9
 sub section10
 sub section11
 sub section12
SECTION3 id name  
 sub section17
 sub section18