0

「顧客パッケージ」を含むテキスト ファイルを検索するソースとして CSV ファイルから情報を取得するプログラムに取り組んでいます。一部のエントリでのみ奇数のカウントが発生しており、カウントが重複している原因がわかりません。誰かが私のコードを見て、私のロジック/構文がオフになっているかどうかを教えてもらえますか? (おそらくそうです)。私が達成しようとしているのは、csv ファイル (packageid、package_description) 内のエントリのテキスト ファイル内の合計出現回数を数えることだけです。

助けてくれてありがとう!私はここで気が狂います。

#!/usr/bin/perl

use strict;
use Text::CSV;

# Variables already declared in the other PL file ** Remove if consolidating **

my $file2 = 'master_plist.csv';
my $csv2 = Text::CSV->new(); # Create a Text::CSV object

open (CSV2, "<", $file2) or die $!; #open CSV file for parsing

while (<CSV2>) {

    if ($csv2->parse($_)) {
            my @columns2 = $csv2->fields(); # Parse CSV and load into an array for each row.
            my $packID = $columns2[0];
            my $packDESC = $columns2[1];



my $val = 'customer_packages_report.txt';

chomp ($val);

my $cnt=0;

open (HNDL, "$val") || die "wrong filename";

while ($val = <HNDL>)
{
while ($val =~ /$packID - $packDESC/ig)
{
    $cnt++;
}
}

#if ($packDESC =~ /\(/g) {
#       $packDESC =~ s/\(/\(/g;
#} 
print "Total iterations of $packDESC: $cnt\n";

close (HNDL);
# End original code

    } # Close IF
} # Close WHILE

close CSV;
4

5 に答える 5

2
#!/usr/bin/perl

use strict;
use warnings;
use Text::CSV;

# Variables already declared in the other PL file ** Remove if consolidating **

my $file2 = 'master_plist.csv';
my $csv2 = Text::CSV->new(); # Create a Text::CSV object

open (CSV2, "<", $file2) or die "I die while opening $file2!  $!"; #open CSV file for parsing

while ($each_csv2_line=<CSV2>) {

    if ($csv2->parse($each_csv2_line)) {
            my @columns2 = $csv2->fields(); # Parse CSV and load into an array for each row.
            my $packID = $columns2[0];
            my $packDESC = $columns2[1];



            my $val = 'customer_packages_report.txt';

            chomp ($val);

            my $cnt=0;

            open (HNDL,"<","$val") or die "wrong filename: $val! $!";

            while (<HNDL>){
                $cnt++ while (/$packID - $packDESC/ig);
            }

#if ($packDESC =~ /\(/g) {
#       $packDESC =~ s/\(/\(/g;
#} 
            print "Total iterations of $packDESC: $cnt\n";

            close (HNDL);
            # End original code

    } # Close IF
} # Close WHILE

# end of script
close CSV;

私の推奨事項:

  • $HNDL instead of HNDLファイルハンドルに <- レキシカル変数をより適切に使用します。
  • すべての間違いをキャッチしてみてください (defined==0と でeq "")
  • 私はあなたのコードをフォーマットし、時々使用するいくつかの機能を追加しようとします. 私より上手になって、Little Perl Monk の最初の Style Coding を読んでください。そして、この言語でより印象的になり、writeonlyコードだけでなく書くこともできます。

例(および引用):

「状況は、行入力演算子の場合とまったく同じですが、<>Perl はこれを自動的に行います。
この間、STDIN からの行をテストしているようです:

    while (<STDIN>) {
       do_something($_);
    }

しかし、これは Perl が自動的に定義をチェックするように変換する特殊なケースです$_:

     while ( defined( $_ = <STDIN> ) ) {  # implicitly done
       do_something($_);
     }

" 効果的な Perl プログラミング、24 ページ。

于 2013-03-05T14:38:57.160 に答える
2

コードを改善するためにいくつかのことを行うことができます。

  1. use warnings;.
  2. 適切なインデントを使用してください。
  3. わかりやすい変数名を使用してください。$file2(意味がなく、なぜファイル 1 がないのですか?) の代わりに、または意味のあるものを使用します$package_file
  4. をすでに使用している場合はText::CSV、 を使用$csv->getline()してファイルを 1 行ずつ調べることができます。これにより、コードが簡素化されます。 例については、ドキュメントを参照してください
  5. chomp($val)文字列の末尾から改行を削除します。改行を持たない、宣言したばかりの文字列リテラルで使用しています。それは意味がありません。
  6. 同じ変数 ( $val) を使用して、まったく異なる 2 つのことを行うことは絶対に避けてください。これは非常に紛らわしいです。
  7. 正規表現で補間している変数に特殊文字が含まれている可能性がありますか? もしそうなら、あなたはそれらをエスケープする必要があります。たとえば$packDESC、ピリオドが含まれている場合、正規表現の任意の文字と一致します。変数の内容を文字どおりに扱うには、次の\Q..\E例のように を使用します/\Q$packID - $packDESC\E/ig

  8. customer_packages_report.txt を開き、csv ファイルのすべての行で 1 行ずつ調べています。一度に読み取り、結果を配列に格納することで、これを簡素化できます。

  9. 一致をカウントするために while ループは必要ありません: $cnt = () = /$packID - $packDESC/ig;。これにより、一致が配列コンテキストに配置され、一致の配列が返されます。次に、一致をカウントするためにスカラー コンテキストに戻されます。少しトリッキーですが、よりシンプルです。

データを見ずに問題の原因を正確に特定することは困難です。両方のファイルに対するネストされたループに起因する不必要な繰り返しが発生する可能性がありますか? コードを改善するために書き直すことから始め、問題がまだ存在するかどうかを確認します。

于 2013-03-05T14:49:59.553 に答える
1

非常に多くの人があなたのプログラム自体についてコメントしているので、私はあなたがより良い Perl プログラマーになる方法と、あなたの問題の多くを解消するのに役立つような方法で書くのを助ける方法について話します.

Perl::Tidyを見て、プログラムを実行してください。これは、構文と Perl を改善するのに役立ち、抱えているさまざまな問題の多くを把握するのに役立ちます。

また、 Perl Tidy のほとんどが引用されているPerl Best Practicesのコピーを入手する必要があります。そして、誰かがすでにEffective Perl Programmingを参照しているように、もう1つの優れた本です。

Perl の大きな問題は、Perl を学ぶ人がほとんどいないことです。ほとんどは、自分で拾わなければならない状況に放り込まれています。さらに、Perl はかなり古く、かなり扱いにくい言語です。ほとんどの Perl 書籍は、依然として Perl 3.x のプログラミング方法に大きく依存しており、 や の使用などの基本については言及していませuse strict;use warnings;

古いプログラミング手法と、ほとんどの人が古い構文を使って古いプログラムをハッキングして Perl を学んでいる (そしておそらく、さらに古いプログラムをハッキングして Perl を学んだ人によって書かれている) ことを組み合わせると、Perl が高い評価を得ている理由がわかります。書き込み専用言語であること。

于 2013-03-05T15:25:48.233 に答える
1

perl -cあなたのコードはエラーなしでコンパイルされているように見えるので、それは良いことです。私が推測すると、あなたの問題は、いくつかのフィールドにメタ文字が含まれていることにあると思います。正規表現/$packID - $packDESC/は、メタ文字に対して脆弱です。例えば

my $str = "foo? bar";
$str =~ /$str/;       # returns false, because ? is a meta character

上記の例では、疑問符?はその前にあるものに影響を与える量指定子であるため、o?「0 または 1 o」を意味します。メタ文字の問題を解決するには、\Q ... \Eエスケープを使用します。

$str =~ /\Q$str/;   # will now match

エスケープ シーケンスの終了\Eはオプションです。


その他の注意事項:

  • 使っていただいてとても良いですuse strict。また、常にする必要がありますuse warnings。そうしないと、コードの問題が取り除かれるわけではなく、問題が隠されるだけです。
  • Text::CSVデフォルト設定でオブジェクトを作成します。入力に応じて、それが適切である場合とそうでない場合があります。ドキュメントでは設定binary => 1を推奨しています。
  • 関数を使用するparse()ことは最善の選択肢ではないかもしれません。ドキュメントには について良いことが書かれていgetlineます。
  • loldopがコメントで指摘しているように、$valファイルからの読み取りを再利用しています。技術的にはうまくいくはずですが、トラブルを求めています。

スタイルと練習のメモと実用的なヒント:

  • 3 引数のオープンおよびレキシカル ファイル ハンドルを使用するのは良いことです。本質的に 3 つの引数は、スクリプトをより安全に使用できる明示的なオープン モードを使用することを意味します。レキシカル ファイル ハンドルを使用すると、ファイル ハンドルにグローバル スコープがないことを意味します。これは良いことです。
  • このコード

my @columns2 = $csv2->fields(); 
my $packID = $columns2[0];
my $packDESC = $columns2[1];

このように書ける

my ($packID, $packDESC) = $csv2->fields();
  • あなたは$valそれを割り当てた直後にむさぼり食っています。chompデフォルトでは、文字列の末尾から改行のみを削除し、そのようなものを追加していないため、これは冗長です。何も変更しませんが、ここでは必須ではありません。ただし、標準入力またはファイルから何かを読み取る場合は、おそらく を使用したいと思うでしょうchomp
  • dieエラーを参照せずに使用する$!と、確実にイライラします。
  • 適切なインデントを使用すると、コードの記述がどれほど簡単になるかを過小評価しないでください。自動インデントと色付けを備えたテキスト エディターを使用します。vim (Windows を使用している場合は gvim) を強くお勧めします。学習曲線はありますが、強力なエディターであり、多くのシステムに既にインストールされていることがよくあります。
于 2013-03-05T14:49:24.250 に答える
0

getlineのメソッドを使用すると、Text::CSV数行のコードを節約できます。

問題は、検索している文字列に正規表現のメタ文字が含まれていることが原因である可能性があります。正規表現でエスケープして\Q...\E、文字どおりに解釈されるようにします。以下の書き直し\s*では、ハイフンの両側に正確に 1 つのスペースがない場合に備えて、リテラル スペースの代わりに追加しました。

また、ファイルハンドルをレキシカルなものに変更しました。これには、ハンドルが範囲外になると自動的に閉じられるという利点があります。

#!/usr/bin/perl

use strict;
use warnings;

use Text::CSV;

my $file2 = 'master_plist.csv';
my $csv2  = Text::CSV->new();

open(my $csv_fh, '<', $file2) or die $!;

while (my $row = $csv2->getline($csv_fh)) {

  my ($packID, $packDESC) = @$row;

  my $val = 'customer_packages_report.txt';
  chomp($val);

  open(my $fh, '<', $val) or die "wrong filename";
  my $cnt = 0;
  while ($val = <$fh>) {
    while ($val =~ /\Q$packID\E\s*-\s*\Q$packDESC\E/ig) {
      $cnt++;
    }
  }

  print "Total iterations of $packDESC: $cnt\n";
}
于 2013-03-05T15:45:42.347 に答える