【问题标题】:How to remove redundant fields and merge resulting lines如何删除冗余字段并合并结果行
【发布时间】:2011-05-03 17:36:50
【问题描述】:

我正在尝试处理纯文本文件。它基本上是名称和相关数字字段的索引,格式如下:

诺沃谢尔斯基,马修,484、584、777
诺沃谢尔斯基,马修,1151
努内斯,保利诺,116
努斯鲍姆, 迈克, 1221, 444,
努斯鲍姆,迈克,156 岁

我想处理成这个

诺沃谢尔斯基,马修,484、584、777、1151
努内斯,保利诺,116
努斯鲍姆,迈克,156、444、1221

如您所见,这些行的结尾不一致:有些可能是空格,有些可能是换行符,有些可能是逗号。实际上,我需要合并以重复全名开头的行,丢弃多余的名称条目,同时合并并保留数字字段的数字顺序。

我的直觉告诉我要快速学习一些 perl 或 awk,但我的技能组合对于两者来说都是空的。我对两者都进行了研究,经过一些搜索和阅读后,无法找到明确或干净的解决方案。

因此,我的问题是:对于我可以有效学习并且足以完成这项任务的工作的最佳工具是什么?此外,鉴于建议的工具,是否有关于如何解决问题的任何建议?

当然,我可以手动编辑这个文件,但这不是很有趣,而且似乎是一种非常愚蠢、笨拙的解决问题的方法。我以这个任务为借口来学习一些关于文本处理的知识,因为这感觉像是一个可能有一个很好的现有工具来解决的问题。

任何指针?

【问题讨论】:

  • 需要合并的行是否总是连续的?
  • 重复行有时会有重复的数字需要删除吗?
  • 行总是连续的,不应该有重复的数字,尽管这还没有经过眼球验证。

标签: regex perl sorting sed awk


【解决方案1】:

正如布赖恩所说,使用哈希表。以下删除换行符,用逗号分割每条记录,使用“姓氏,名字”原始形式作为哈希的键,将剩余的值推入数组并使用对所述数组的引用作为上述键的值.

然后它只是迭代哈希中的键/值对并相应地格式化。

修正方案——数字排序、省略中间名、排序输出

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

my %merged;

while (my $record = <DATA>) {
    chomp $record;
    my ($lname, $fname, @stuff) = split /[, ]+/, $record;
    push @{ $merged{"$lname, $fname"} }, grep { m/^\d+$/; } @stuff;
}

foreach my $name (sort keys %merged) {
    print $name, ", ", join( ', ', sort { $a <=> $b } @{$merged{$name}}), "\n";
}

__DATA__
Nowosielski, Matthew, 484, 584, 777
Nowosielski, Matthew, 1151
Nunes, Paulino, 116
Nussbaum, Mike, 1221, 444,
Nussbaum, Mike, 156
Nowosielski, Matthew, Kimball, 485, 684, 277

修正输出

Nowosielski, Matthew, 277, 484, 485, 584, 684, 777, 1151
Nunes, Paulino, 116
Nussbaum, Mike, 156, 444, 1221

原方案

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

my %merged;

while (my $record = <DATA>) {
    chomp $record;
    my ($lname, $fname, @stuff) = split /,/, $record;

    push @{ $merged{"$lname, $fname"} }, @stuff;
}

while (my ($name, $stuff) = each %merged) {
    print $name, join( ',', @$stuff), "\n"; 
}

__DATA__
Nowosielski, Matthew, 484, 584, 777
Nowosielski, Matthew, 1151
Nunes, Paulino, 116
Nussbaum, Mike, 1221, 444,
Nussbaum, Mike, 156

【讨论】:

  • 在问题的示例中,新数字不只是附加到末尾,而是按排序顺序放置。
  • 是的,ysth 有这个权利,数字顺序应该保留在合并的行中。
  • 在我的示例中没有明确说明的另外一个考虑因素是,这些行并不总是以 $lastname, $firstname 的形式开始。他们经常开始,$lastname, $firstname $middlename。我选择了一个无视该案例的示例——我的疏忽。
  • @ryanklee:数据的排序很简单:将连接更改为:join(',', sort { $a &lt;=&gt; $b } @$stuff)。运算符确保比较是数字的 - 但如果数据中有中间名,则会遇到问题。这需要一个额外的技巧......
  • @Jonathan,@Pedro,非常感谢您对我的问题的处理。
【解决方案2】:

将此作为学习的借口,我会编写一个快速的python脚本。

让自己成为一个字典(映射),字符串作为键和值。读一行并抓住名字。在字典中查找名称。如果它在那里,请将新数字附加到字典条目的末尾。阅读整个文件后,遍历字典并打印出键和值。

【讨论】:

  • 注意:提到 python 只是因为它是我选择的脚本语言,你可以在 perl 中使用关联数组来做同样的事情
  • 在问题的示例中,新数字不只是附加到末尾,而是按排序顺序放置。
  • 啊,我错过了那部分。在这种情况下,映射中的条目将是单个整数的数组。在打印地图时,在打印之前对数组进行排序(例如使用 perl 的 sort())。
【解决方案3】:

要清楚地做到这一点,您需要一种具有关联数组的语言(Perl - 哈希;Python - 字典;Awk - 关联数组)。这排除了sed(和C)。

awk:

awk '{ for (i = 3; i <= NF; i++) {names[$1, $2] = names[$1, $2] " " $i } }
     END { for (name in names) { printf "%s: %s\n", name, names[name]; } }'

您可能更愿意将逗号指定为带有“-F,”的字段分隔符。

额外的要求——按顺序排列数字和处理中间名——在awk 中处理起来比在perl 中处理起来要复杂得多;如果有额外的要求,我会选择perl 而不是awk。 (请注意,GNU Awk 具有内置函数 asortasorti 来对数组进行排序,但我不确定你是否可以让 'names[$1,$2] 识别 awk 中的整数数组。)我更多Perl 比 Python 更流利——但毫无疑问,Python 也可以做 Perl 处理的事情。

【讨论】:

【解决方案4】:

尝试使用 AWK

#!/usr/bin/awk -f
$1 == lastOne && $2 == lastTwo { $1=""; $2=""; printf ", %s", $0 ;lastOne=$1; lastTwo=$2 }
$1 != lastOne && $2 != lastTwo { printf "\n%s", $0 ;lastOne=$1; lastTwo=$2 }
END {printf "\n" }

此脚本假定数据已在您的前两个字段中排序...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-06
    • 1970-01-01
    • 1970-01-01
    • 2012-09-09
    • 2021-12-12
    • 1970-01-01
    • 2016-05-05
    相关资源
    最近更新 更多