Perl - 需要在文件中附加重复项并仅写入唯一值

时间:2014-07-28 21:52:11

标签: perl duplicates append

我搜索了一下,希望我不会复制某人已经问过的内容。我有什么相当于特定格式的CSV(供应商要求)。有四个值分隔如下:

"Name","Description","Tag","IPAddresses"

列表很长(并且有大约150个唯一名称 - 下面的示例中只有2个)但它基本上是这样的:

"2B_AppName-Environment","desc","tag","192.168.1.1"
"2B_AppName-Environment","desc","tag","192.168.22.155"
"2B_AppName-Environment","desc","tag","10.20.30.40"
"6G_ServerName-AltEnv","desc","tag","1.2.3.4"
"6G_ServerName-AltEnv","desc","tag","192.192.192.40"
"6G_ServerName-AltEnv","desc","tag","192.168.50.5"

我希望在Perl(或sed / awk等)中提供以下方法:

"2B_AppName-Environment","desc","tag","192.168.1.1,192.168.22.155,10.20.30.40"
"6G_ServerName-AltEnv","desc","tag","1.2.3.4,192.192.192.40,192.168.50.5"

所以基本上,生成的文件会将重复项附加到第一个匹配项 - 每个应用程序/服务器名称只应该有一行,其中包含逗号分隔的IP地址列表,就像上面显示的那样。

请注意" Decription"和#34;标记"在复制删除/附加逻辑中不需要考虑这些字段 - 让我们假设这些字段是空白的,以便使事情变得更容易。此外,在供应商提供的列表中,"名称"所有条目都已经排序在一起。

2 个答案:

答案 0 :(得分:1)

这个简短的Perl程序应该适合你。它期望输入CSV文件的路径作为命令行上的参数,并将结果打印到STDOUT。它会跟踪name数组中新@names字段的外观,以便它可以按照每个名称首次出现的顺序打印输出,并获取desctag的值来自每个唯一name第一个出现的use strict; use warnings; use Text::CSV; my $csv = Text::CSV->new({always_quote => 1, eol => "\n"}); my (@names, %data); while (my $row = $csv->getline(*ARGV)) { my $name = $row->[0]; if ($data{$name}) { $data{$name}[3] .= ','.$row->[3]; } else { push @names, $name; $data{$name} = $row; } } for my $name (@names) { $csv->print(*STDOUT, $data{$name}); }

"2B_AppName-Environment","desc","tag","192.168.1.1,192.168.22.155,10.20.30.40"
"6G_ServerName-AltEnv","desc","tag","1.2.3.4,192.192.192.40,192.168.50.5"

<强>输出

use strict;
use warnings;

use Text::CSV;
use Regexp::Common;

my $csv = Text::CSV->new({always_quote => 1, eol => "\n"});

my (@names, %data);

while (my $row = $csv->getline(*ARGV)) {
  my ($name, $address) = @{$row}[0,3];
  next unless $address =~ $RE{net}{IPv4};

  if ($data{$name}) {
    $data{$name}[3] .= ','.$address;
  }
  else {
    push @names, $name;
    $data{$name} = $row;
  }
}

for my $name (@names) {
  $csv->print(*STDOUT, $data{$name});
}

<强>更新

这是一个忽略在第四个字段中没有有效IPv4地址的记录的版本。我已经使用Regexp::Common,因为它是获得复杂正则表达式模式的最简单方法。它可能需要在您的系统上安装。

{{1}}

答案 1 :(得分:1)

我建议您使用像Text::CSV这样的CSV解析器来解决此类问题。

Borodin已经贴了一个如何做到这一点的好例子。

我建议 NOT 使用的方法之一是正则表达式。

以下单行演示了如何做到这一点,但与实际的csv解析器相比,这是一种非常脆弱的方法:

perl -0777 -ne '
    while (m{^((.*)"[^"\n]*"\n(?:(?=\2).*\n)*)}mg) {
      $s = $1;
      $s =~ s/"\n.*"([^"\n]+)(?=")/,$1/g;
      print $s
    }' test.csv

输出:

"2B_AppName-Environment","desc","tag","192.168.1.1,192.168.22.155,10.20.30.40"
"6G_ServerName-AltEnv","desc","tag","1.2.3.4,192.192.192.40,192.168.50.5"

说明:

切换

  • -0777:覆盖整个文件
  • -n:为输入文件中的每个“行”创建一个while(<>){...}循环。
  • -e:告诉perl在命令行上执行代码。

<强>代码

  • while (m{^((.*)"[^"]*"\n(?:(?=\2).*\n)*)}mg):将文字分隔为匹配的部分。
  • $s =~ s/"\n.*"([^"\n]+)(?=")/,$1/g;:在匹配的部分用逗号加入所有IP地址。
  • print $s:打印结果。