【问题标题】:Remove multiple duplicate lines from a file从文件中删除多个重复行
【发布时间】:2018-12-13 03:23:37
【问题描述】:

我有一个在 crontab 中运行的 Perl 脚本,它会生成一个包含大量重复条目的文件,因为在每次运行时它都会重写以前写入的信息。

我会使用 sort -u 的文件,但是,我会在 Perl 脚本文件的末尾这样做。

我的清单

10/10/2017 00:01:39:000;Sagitter
10/11/2017 00:00:01:002;Lupus
10/12/2017 00:03:14:109;Leon
10/12/2017 00:09:00:459;Sagitter
10/13/2017 01:11:03:009;Lupus
12/13/2017 04:29:00:609;Ariet
10/11/2017 00:00:01:002;Lupus
10/12/2017 00:03:14:109;Leon
...

我的代码

#!/usr/bin/perl

# Libraries
use strict;
use warnings 'all';

%lines = ();

# Remove duplicate

open( TMP_GL_OUTPUT, '>', $OUTPUT_FILE ) or die $!;

while ( <TMP_GL_OUTPUT> ) {
    $lines{$_}++;
}

open( OUTFILE, '>', $TMPOUTPUT_FILE ) or die $!;
print OUTFILE keys %lines;
close( OUTFILE );

close( TMP_GL_OUTPUT );

我哪里错了?在 shell 中感觉比在 Perl 中短。

sort -u $TMPOUTPUT_FILE > $OUTPUT_FILE 

根据 ikegamy 用户的建议,我做了以下操作:

move $OUTPUT_FILE, $TMPOUTPUT_FILE; # Copy file
run [ 'sort', '-u', '--', $TMPOUTPUT_FILE ], '>', $OUTPUT_FILE; # Remove duplicate
unlink $TMPOUTPUT_FILE;

【问题讨论】:

  • 您的代码无法编译($OUTPUT_FILE$TMPOUTPUT_FILE%lines 未声明),并且您尝试从打开的文件中读取以进行输出(您在第一次打开时使用了 &gt; )。
  • 首先,你打开TMP_GL_OUTPUT是为了写,而不是读。
  • @Dada 感谢您的支持,我已经使用 ikegami 的命令解决了这个问题

标签: perl duplicates


【解决方案1】:

我想你是在问为什么你的 Perl 程序比你的 shell 脚本长。

首先,您的 shell 脚本所做的事情与您的 Perl 程序完全不同。

  • 您的 shell 脚本执行一个程序,并将其存储在一个文件中。
  • 您的 Perl 程序读取一个文件,操作它读取的数据,并将输出存储在一个文件中。

Perl 等价于

sort -u -- "$TMPOUTPUT_FILE" > "$OUTPUT_FILE"

use IPC::Run qw( run );

run [ 'sort', '-u', '--', $TMPOUTPUT_FILE ], '>', $OUTPUT_FILE;

(这两者在错误处理上存在差异。)

它们的长度没有那么不同。

这带来了第二个区别。 shell 专门用于执行程序,但 Perl 是一种通用语言。如果它不在 Perl 中,那将是令人惊讶的!

(现在尝试将您的 Perl 程序的大小与 sort 的源代码进行比较...)

【讨论】:

  • 啊???我没有发布任何解决方案。我刚刚回答了一个关于语言差异的问题。这篇文章不应被视为对执行 sort 实用程序的认可。 (有理由认为这是一个好主意,也有理由认为它不是。)
【解决方案2】:

List::Util 是一个核心模块。

use List::Util 'uniq';

print for uniq <>

【讨论】:

    【解决方案3】:

    您的代码看起来几乎没问题。

    我的提议只对chomp每一行,在你之前 在哈希中保存一个元素。

    原因是例如最后一行,未终止 \n 可能看起来与前几行相同, 但如果没有chomp,上一行将包含 终止的\n,而最后一个 - 不是。

    结果是这两行都将是散列中的不同键。

    将我的示例程序(工作,如下所示)与您的进行比较,有 除了阅读__DATA__ 和 写入控制台。

    在我的程序中,出于演示目的,我放置了 2 个打印输出变体, 一个带有键值(重复计数),另一个只打印键。 在你的程序中只留下第二个打印输出。

    use strict; use warnings; use feature qw(say);
    
    my %lines;
    while(<DATA>) {
        chomp;
        $lines{$_}++;
    }
    while(my($key, $val) = each %lines) {
        printf "%-32s / %d\n", $key, $val;
    }
    say '========';
    foreach my $key (keys %lines) {
        say $key;
    }
    __DATA__
    10/10/2017 00:01:39:000;Sagitter
    10/11/2017 00:00:01:002;Lupus
    10/12/2017 00:03:14:109;Leon
    10/12/2017 00:09:00:459;Sagitter
    10/13/2017 01:11:03:009;Lupus
    12/13/2017 04:29:00:609;Ariet
    10/11/2017 00:00:01:002;Lupus
    10/12/2017 00:03:14:109;Leon
    

    编辑

    您的代码没有为$OUTPUT_FILE$TMPOUTPUT_FILE 指定名称, 你甚至没有声明这些变量,但我假设,在你的实际 你做的代码。

    另外一个细节是%lines前面应该是my, 否则,当您输入 use strict; 时,编译器会打印错误。

    编辑 2

    有一个比你的更快更短的解决方案。

    而不是将行写入哈希并在 第二步,你可以在 single 循环中完成:

    • 阅读该行。
    • 检查哈希是否已经包含与刚刚读取的行相等的键。
    • 如果不是,那么:
      • 将行写入散列,以阻止打印输出,如果只是 同一行再次出现,
      • 打印该行。

    您甚至可以将此程序编写为 Perl 单行程序:

    perl -lne"print if !$lines{$_}++" input.txt
    

    如果您从 Windows cmd 运行上述命令,它将打印输出 到控制台。如果您使用 Linux,则可以使用撇号而不是双引号。

    您当然可以将输出重定向到任何文件,将&gt; output.txt 添加到 上面的命令。

    代码针对每个输入行执行,由于-l 选项而被截断。

    如果您不知道有关 Perl 单行代码的任何其他详细信息,请搜索网络。

    【讨论】:

    • 无需清空新的哈希,因此无需在单行中使用 BEGIN 块。它也不能在 Linux 平台上工作,因为您需要在参数周围加上单引号 -e。这个perl -lne 'print unless $lines{$_}++' input.txt 可以正常工作。
    • Re "你当然可以将输出重定向到任何文件",见Specifying file to process to Perl one-liner
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-05
    • 2014-06-03
    • 2010-11-17
    • 2019-10-09
    相关资源
    最近更新 更多