如何使用Perl将多行合并为单行

时间:2015-07-03 16:10:33

标签: regex perl

我尝试使用Perl从输入文本文件格式转换为显示的输出文本文件格式,但不成功。

有人可以帮忙吗?

输入:

row1 multiline 1
row1 multiline 2
row1 multiline 3
row2 multiline 1
row2 multiline 2

预期产出:

row1 multiline 1 multiline 2 multiline 3
row2 multiline 1 multiline 2

4 个答案:

答案 0 :(得分:3)

这会按照你的要求行事。它检查每行的第一个字段是否已更改,以决定是继续输出当前行还是开始新行

它希望输入文件的路径作为命令行上的参数

use strict;
use warnings;

my $row;

while ( <> ) {

    next unless /\S/;
    chomp;

    my ( $new_row, $rest ) = split ' ', $_, 2;

    if ( defined $row and $row eq $new_row ) {
        print ' ', $rest;
    }
    else {
        print "\n" if defined $row;
        print $_;
        $row = $new_row;
    }
}

print "\n";

输出

row1 multiline 1 multiline 2 multiline 3
row2 multiline 1 multiline 2

答案 1 :(得分:1)

在一个正则表达式?不太可能。然而,多次使用相同的正则表达式是合理的。只需匹配,直到它停止匹配:

while ($input =~ s/row(\d+)((?: multiline \d+)+)\n+row\1/row$1$2/gm){}

循环将在每次迭代时将未合并线的数量减少一半。因此它只会循环O(ln(n))次。

您可以在此处看到它:https://ideone.com/RP30h6

<小时/> 上述解决方案更加深奥而实用。以下是真实解决方案的外观:

my $row_number = 0;
my ($row, $column);

while ($input =~ /(row(\d+) multiline (\d+))/gm) {
  if ($row_number != $2) {
    $row_number = $2;
  } else {
    $row = $1;
    $column = $3;
    $input =~ s/\n+$row/ multiline $column/g;
  }
}

演示:https://ideone.com/Mk2QqZ

答案 2 :(得分:1)

这可以使用替换回调来完成 在Perl中,通常使用s///e 评估表单来完成。

这只是获取捕获缓冲区中的公共行块 缓冲区1是第一行,缓冲区3是剩余的公共行。

这些传递给合并子 合并子通过另一个正则表达式修剪公共行 然后将第一行与公共行组合起来 然后它作为替代品被传回。

Perl代码:

use strict;
use warnings;

$/ = undef;

my $input = <DATA>;

sub mergeRows {
    my ($first_row, $other_rows) = @_;
    $other_rows =~ s/(?m)\s*^\w+\s*(.*)(?<!\s)\s*/$1 /g;
    return $first_row . " " . $other_rows . "\n";
}

$input =~ s/(?m)(^(\w+).*)(?<!\s)\s+((?:\s*^\2.*)+)/ mergeRows($1,$3) /eg;

print $input, "\n";

__DATA__
row1 multiline 1

row1 multiline 2

row1 multiline 3

row2 multiline 1

row2 multiline 2

输出:

row1 multiline 1 multiline 2 multiline 3

row2 multiline 1 multiline 2

主要正则表达式:

 (?m)                          # Multi-line mode
 (                             # (1 start), First of common row
      ^ 
      ( \w+ )                       # (2), common row label
      .* 
 )                             # (1 end)
 (?<! \s )                     # Force trim of trailing spaces
 \s+                           # Consume a newline, also get all the next whitespaces
 (                             # (3 start), Remaining common row's
      (?:
           \s* ^ \2  .* 
      )+
 )                             # (3 end)

合并子正则表达式:

 (?m)                          # Multi-line mode
 \s*                           # remove
 ^ \w+ \s*                     # remove
 ( .* )                        # (1), What will be saved
 (?<! \s )                     # remove, force trim of trailing spaces
 \s*                           # remove, possibly many newlines (whitespace)

答案 3 :(得分:1)

您有一个关键字段作为第一个单词,然后该行的其余部分作为值。

所以我会像这样处理你的问题:

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

my %rows;
while (<DATA>) {
    my ( $key, $rest_of_line ) = (m/^(\w+) (.*)/);
    push( @{ $rows{$key} }, $rest_of_line );
}

foreach my $key ( sort keys %rows ) {
    print "$key ", join( " ", @{ $rows{$key} } ), "\n";
}

__DATA__
row1 multiline 1
row1 multiline 2
row1 multiline 3
row2 multiline 1
row2 multiline 2

与其他人的方法略有不同,因为我们将每行读入哈希值,然后输出哈希值。

它不会维护原始文件的顺序,而是按“行”值排序&#39;顺序。

相关问题