0

我这里还有一个问题,我有几个数据并想合并它们。但是脚本首先检查所有 DAT 的标头,如果不匹配,它将引发错误并停止脚本。现在我想运行跳过有问题的 dat 的脚本,并在单独的文本文件中输出错误,并列出错误的 DAts 和原因。有人可以帮忙吗?这是我到目前为止所拥有的:

use strict;
my $rootdir = $ARGV[0];
die "usage: perl mergetxtfiles.pl <folder>" if ($#ARGV != 0);
#$rootdir =~ s/\\/\\\\/g;

print "\nFolder = $rootdir\n\n";
opendir(DIR, $rootdir)
    or die "failed opening the directory $rootdir";
open(OF,">:utf8",'combined_'.time.'.dat')
    or die "failed opening the file";

my $icr         = 0;
my $cnt         = 0;
my $header      = '';
my $header_flag = 0;

while(my $fname = readdir(DIR)) {

    # add extensions if needed
    if ($fname =~ m/(\.txt)|(\.dat)|(\.csv)$/i) {

        $icr++;
        my $fnamepath = $rootdir.'\\'.$fname;
        print "\($icr\) $fname\n";
        open(IF, "<:utf8", $fnamepath)
            or die "ERROR: cannot open the file\n$fnamepath "; 

        my $sep_icr = 0;
        while(<IF>) {

            my $line = $_;
            chomp $line;
            next if (/^$/);

            $sep_icr++;
            $cnt++;

            my @ar = split(/\t/,$line);

            if ($cnt == 1) {

                $header_flag = 1;
                $header      = $line;
            }

            if ($sep_icr == 1 and $header_flag == 1) {

                #print "$line \n $header\n";
                if ($line ne $header) {

                    die "Headers are not same\n";
                }
                elsif (($line eq $header) and ($cnt >1)) {

                    print "INFO\: ignoring the same header for $fname \n";
                    $cnt--; 
                    next; 
                }
            }
            print OF $line."\n";
        }
        print "\--Line count= $sep_icr\n\n";
        close IF;
        #print OF "\n";
    }
}

print "\-\-\> Total line count= $cnt\n";
4

1 回答 1

0

命名循环

在您的循环中,我们必须稍微更改您的 if 子句和外循环:

FILE:
while(my $fname = readdir(DIR)) {
  ...;
  if ($line ne $header) {
    logger($fname, "Headers not matching");
    next FILE;
  }
  ...;
}

在 Perl 中,循环可以被标记,因此我们可以指定我们执行哪个循环next,而不是设置和检查标志。我使用了下面给出的示例日志记录函数logger,但您可以用适当的打印语句替换它。

日志记录

这可能比要求的要多,但这里有一个小日志功能以提高灵活性。参数是文件名、原因和可选的严重性。如果不需要,您可以删除严重性代码。严重性无论如何都是可选的,默认为debug.

open my $logfile, ">>", "FILENAME" or die "..."; # open for append
sub logger {
  my ($file, $reason, $severity) = (@_, 'debug');
  $severity = {
    debug => '',
    info  => 'INFO',
    warn  => '!WARN!',
    fatal => '!!!ERROR!!!',
  }->{$severity} // $severity; # transform the severity if it is a name we know
  $severity .= ' ' if length $severity; # append space if we have a severity
  print {$logfile} $severity . qq{$reason while processing "$file"\n};
}

如果调用logger("./foo/bar", "Headers not matching", 'warn')它会输出:

!WARN! Headers not matching while processing "./foo/bar"

如果需要,将打印的错误消息更改为机器可读的内容。

风格提示和技巧:

如果发现这些行更优雅:

die "usage: ...\n" unless @ARGV;
my ($rootdir) = @ARGV;

注意末尾的换行符(抑制“第 3 行”等)。在标量上下文中,数组返回数组长度。在第二行中,我们可以通过在列表上下文中分配来避免数组下标。多余的元素被忽略。


反而

if ($fname =~ m/(\.txt)|(\.dat)|(\.csv)$/i) { ...; }

我们可以说

next unless $fname =~ m/(?: \.txt | \.dat | \.csv )$/xi;

并避免不必要的意图,从而提高可读性。

我修改了正则表达式,以便所有后缀都必须放在末尾,而不仅仅是.csv后缀,并添加了/x修饰符,以便我可以在正则表达式中使用非语义空格。


Windows 和几乎所有操作系统都理解路径名中的正斜杠。所以与其

my $fnamepath = $rootdir.'\\'.$fname;

我们可以写

my $fnamepath = "$rootdir/$fname";

我发现这更容易编写和理解。


while(<IF>) {
  my $line = $_;

构造可以简化为

while(my $line = <IF>) {...}

最后但同样重要的是,考虑养成使用my. 通常,不需要全局文件句柄,这可能会导致一些错误。

于 2012-09-09T18:03:50.920 回答