0

ここで別の質問があります。複数のデータがあり、それらをマージしたいと考えています。ただし、スクリプトは最初にすべての DAT のヘッダーをチェックし、一致しない場合はエラーをスローしてスクリプトを停止します。ここで、問題のあるデータをスキップしてスクリプトを実行し、エラーが発生したデータと理由のリストを含む別のテキスト ファイルにエラーを出力したいと考えています。誰でもこれについて助けてください。これが私がこれまでに持っているものです:

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ましたが、適切な print ステートメントに置き換えることができます。

ロギング

これはおそらく要求よりも少し多いですが、ここでは柔軟性のために小さなロギング機能を示します。引数は、ファイル名、理由、およびオプションの重大度です。重大度コードが必要ない場合は削除できます。とにかく重大度はオプションで、デフォルトは です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行目」などを抑制します)。スカラー コンテキストでは、配列は配列の長さを返します。2 行目では、リスト コンテキストで代入することにより、配列の添字付けを回避できます。余分な要素は無視されます。


その代わり

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

言うことが出来る

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

不必要な意図を避けることで、可読性を向上させます。

サフィックスだけでなく、すべてのサフィックスが最後に来るように正規表現を変更し.csv、修飾子を追加して/x、正規表現内で非意味的な空白を使用できるようにしました。


Windows とほとんどすべての OS は、パス名のスラッシュを認識します。だから代わりに

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

私たちは書くことができます

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

その方が書きやすく、理解しやすいと思います。


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

構造は次のように簡略化できます

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

大事なことを言い忘れましたが、ファイルハンドルを で使用する習慣を始めることを検討してmyください。多くの場合、グローバル ファイルハンドルは必要なく、バグが発生する可能性があります。

于 2012-09-09T18:03:50.920 に答える