我希望你能帮助我解决这个问题。
我想将一列的前五个元素与其余元素进行比较(逐列)。
- 如果前 5 个元素相等 --> 继续
- 如果前 5 个元素相等且与其余元素不同,则打印这些列
该表有100多列和12行,表之间用TABS分隔。
输入示例:
约翰·劳拉·本·克里斯·丹·汤姆 约翰·劳拉·本·莱恩·丹·汤姆 约翰·劳拉·本·克里斯·丹·汤姆 约翰·劳拉·本·克里斯·丹·汤姆 约翰·劳拉·本·克里斯·丹·汤姆 戴夫·劳拉·泰勒克里斯·多纳·萨拉 戴夫·劳拉·哈维克里斯·丹·萨拉
期望的输出:
约翰·本·汤姆 约翰·本·汤姆 约翰·本·汤姆 约翰·本·汤姆 约翰·本·汤姆 戴夫·泰勒·萨拉 戴夫·哈维·萨拉
我非常感谢您的所有想法和评论,
谢谢
答案1
通常最好对行(行)进行操作。诸如awk
一次只操作一行之类的工具。要将列转换为行(即转置),请使用GNUdatamash
。然后awk
可以分别对每一行进行适当的测试,打印或不打印。最后datamash
再次使用将结果转置为原始格式:
<data datamash transpose | awk '{
for (i=2; i<=5; i++) if ($1!=$i) next
for (i=6; i<=NF; i++) if ($1==$i) next
print
}' | datamash transpose
逻辑解释如下:
- 如果 2..5 中的任何一个字段与字段号 1 不同,第一个
for
会跳过该行并重新开始。所有 5 个字段都相等是允许程序继续执行下一行代码的唯一可能性。 for
如果以下任何字段与第一个字段相同,第二个字段将跳过该行并重新开始。- 如果程序超出了这两个
for
标准,则意味着根本没有发现与您的标准相矛盾的情况。只有这样才能打印整行。
笔记:
- 在原始问题(无需转置)中,仅当至少有一列尚未与您的标准相矛盾时,您才需要阅读下一行。根据数据的不同,在读取前几行后,所有列都可能被声明为不匹配。在这种情况下,算法可以快速退出并且什么也不打印。但
datamash transpose
无论数据如何,都需要读取和处理整个文件(或流)。在这种情况下,我的解决方案并不理想。 - 万一不可
datamash
用,有一个awk
解决方案这里。
答案2
我们需要将列转置为行,然后运行正则表达式检查要打印的行。那些具有相同前五个元素的元素不应在下面再次看到相同的元素。最后重新转置结果以获得所需的输出。
HTH。
rs -T inp |
perl -lane 'print if "@F" =~ /^(\S+)(?: \1){4}(?: (?:(?!\1(?=(?: |$))).)+)+$/' |
rs -T
输出:
Jhon Ben Tom
Jhon Ben Tom
Jhon Ben Tom
Jhon Ben Tom
Jhon Ben Tom
Dave Taylor Sara
Dave Harvey Sara