1

私は2つの配列を持っています:

  • @file_listディレクトリ内のファイルのリストを保持し、
  • @name_listいくつかの名前を保持しています。

たとえば、これらの配列には以下を含めることができます

@file_list = ('Bob_car', 'Bob_house', 'Bob_work', 'Fred_car', 'Fred_house', 'Fred_work', ...);
@name_list = ('Bob', 'Fred', ...);

(実際のデータはそれほど単純ではありません)。

私の目標は、各ファイルをすべての名前と比較して、それらが一致するかどうかを確認することです。ファイル文字列が名前で始まる場合、それらは一致します。

次に、これらの一致を使用して、対応する名前に基づいてファイルを新しいディレクトリに並べ替えることができます。

これが私のコードです:

for ( my $i = 0; $i < scalar @file_list ; $i++ )
   {
    for ( my $j = 0; $j < @name_list ; $j++ )
        {
         if ( $file_list[ $i ] =~ m/^$name_list[ $j ]/ )
            {
             print "$file_list[ $i ] goes with $name_list[ $j ]\n"; 
            } 
         else
            {
             print "no match\n";   
            }
        }
   }

しかし、私は一致を取得しません。個々のループをテストしましたが、動作しています。そうでなければ、正規表現に何か問題がありますか?

配列の作成方法について:

の場合@name_list、名前を含むファイルは一見ランダムな方法で編成されています。そのファイルの名前はいくつかの異なる行にあり、その間に多くの空白行があり、行内に多くの空白エントリがあります。名前は複数回表示できます。

次のコードを使用して作成しました@name_list

while (my $line = <$OriginalFILE>) 
    {
     chomp $line;
     my @current_line = split( "\t", $line );

     for ( my $i = 0; $i < scalar @current_line ; $i ++ )
         {
          if ( $current_line[ $i ] =~ m/^\s*$/ )
             {
              # print "$current_line[$i] is blank\n"; 
             }
          else 
             {
              push( @raw_name_list, $current_line[ $i ] );   
             }
         } # end of for
    } # while

# collect list without repeat instances of the same name

my %unique = ();
foreach my $name (@raw_name_list)
    {
     $unique{$name} ++;
    }
my @name_list = keys %unique; 

foreach my $name ( @name_list )
   {
    # print "$name\n";
    chomp $name; 

    unless(mkdir $name, 0700) 
        {
         die "Unable to create directory called $name\n";
        }
   }    

配列@file_listは以下を使用して作成されました。

opendir(DIR, $ARGV[1]);                             
my @file_list = grep ! /^\./, readdir DIR;
closedir(DIR); 
# print @file_list;

@amon、ループと正規表現をテストするために私がしたことは次のとおりです。

FILE: for my $file (@transposed_files) {
  print "$file\n";
  for my $name (@transposedunique) {
    print "i see this $name\n";
    if ($file =~ /^\Q$name\E/) {
      print "$file goes with $name\n";
      next FILE;
    }
  }
  #print "no match for $file\n";
}

ああ、配列を転置して、出力ファイルに別々の行に出力するようにしました。

4

4 に答える 4

2

短いバージョン:名前配列を間違って構築しています。この行を見てください:

$unique{name} ++;

nameハッシュのエントリを増やしているだけです。$nameあなたはおそらく変数が欲しかったでしょう。

長いバージョン

英語と Foreach-Loops について

あなたのコードは少し Perl らしくなく、Perl よりも C に似ています。Perl は、あなたが思っているよりずっと英語に近い言語です。あなたの質問の元の文言から:

から最初の要素を取得し、@file_listそれを の各要素と比較します@name_list

あなたはこれを次のように書きました

for (my $i = 0; $i < @file_list; $i++) {
  for (my $j = 0; $j < @name_list; $j++) {
    ...; # compare $file_list[$i] with $name_list[$j]
  }
}

むしろしたい

for my $file (@file_list) {
  for my $name (@name_list) {
    ...; # compare $file with $name
  }
}

配列の添字付けの煩わしさから解放されます。

正しい正規表現の構築

コードには次のテストが含まれています。

$file_list[ $i ] =~ m/^$name_list[ $j ]/

、、$name_list[$j]などの特殊文字が含まれている場合、これはあなたの考えとは異なります。で囲むことにより、変数のリテラルの内容を一致させることができます。これにより、コードが作成されます(.+\Q ... \E

$file =~ /^\Q$name\E/

(ループの私のバリアントで使用する場合)。

気の利いた方法で、先頭の部分文字列を直接比較することもできます。

$name eq substr $file, 0, length($name)

これは同じ状態を表しています。

オンループ制御

私は2つの仮定をします:

  1. 任意のファイルの最初に一致する名前にのみ関心があります
  2. no match名前が見つからない場合にのみメッセージを印刷したい

Perl では、他の言語で行うように、フラグを使用せずに、任意のループから抜け出したり、現在の反復を再開したり、次の反復に直接進むことができます。ループに のようなラベルを付けるだけですLABEL: for (...)

したがって、一致するファイルが見つかったら、次のファイルの検索を開始できます。no matchまた、次のファイルに移動せずに内側のループを離れた場合にのみ印刷したいと考えています。このコードはそれを行います:

FILE: for my $file (@file_list) {
  for my $name (@name_list) {
    if ($file =~ /^\Q$name\E/) {
      print "$file goes with $name\n";
      next FILE;
    }
  }
  print "no match for $file\n";
}

否定の禅

ファイル解析コードでは、条件を表現します

if ($field =~ /^\s*$/) {
} else {
  # do this stuff only if the field does not consist only of
  # zero or more whitespace characters
}

その説明は非常に複雑です。どうですか

if ($field =~ /\S/) {
  # do this stuff only if the field contains a non-whitespace character.
}

同じ条件ですが、よりシンプルで効率的です。

パースを簡素化する

つまり、ファイル解析コードは次のように要約できます。

my %uniq;
while (<$OriginalFILE>) {
  chomp;
  $uniq{$_} = undef for grep /\S/, split /\t/;
}
my @name_list = sort { length($b) <=> length($a) } keys %uniq;

このsplit関数は最初の引数として正規表現を取り、$_他の文字列が指定されていない場合は分割されます。フィールドのリストを返します。

このgrep関数は条件とリストを受け取り、条件に一致するリストのすべての要素を返します。現在の要素は に$_あり、正規表現はデフォルトで一致します。正規表現の説明については、上記を参照してください。

注:これにより、先頭の位置であっても、フィールドに空白を含めることができます。すべての空白で分割するにはsplit、単一のスペースを含む文字列の特別な引数を指定できます: split ' '. これはgrep不必要なものになります。

ループは、ステートメント修飾子forとしても使用できます。つまり、likeです。現在の要素は にあります。ハッシュのエントリに何かを割り当てます(すでに空のハッシュに初期化されています)。これは数値の場合もありますが、同様に機能します。EXPR for LIST$_$_%uniqundef

キーは一見ランダムな順序で返されます。ただし、1 つのファイルに複数の名前が一致する可能性がありますが、一致するものを 1 つだけ選択したい場合は、最も具体的な名前を最初に一致させる必要があります。したがって、名前の長さの後に降順で並べ替えます。

于 2013-04-20T11:06:25.243 に答える
1

あなたのコードは私のために働くようです。私がしたことは、次のような2つの配列を構築することだけでした:

my @file_list = qw/Bob_car Bob_house Bob_work Fred_car Fred_house Fred_work/;
my @name_list = qw/Fred Bob Mary/;

次に、コードを実行すると、次のような出力が生成されます。

no match
Bob_car goes with Bob
no match
no match
Bob_house goes with Bob
no match
no match
Bob_work goes with Bob
no match
Fred_car goes with Fred
no match
no match
Fred_house goes with Fred
no match
no match
Fred_work goes with Fred
no match
no match

それで、それは機能しているように見えます。

ファイルまたはユーザーから入力を読み取る際の一般的な問題は、入力の末尾から改行文字を削除するのを忘れることです。これはあなたの問題かもしれません。もしそうなら、について読んで、配列に追加するときに各値perldoc -f chompだけを読んでください。chomp

于 2013-04-20T03:39:14.863 に答える
1

私は常に効率的な方法で物事を行うことに興味があるので、O(N^2) アルゴリズムを見るたびにベルが鳴ります。O(N+M) ではなく O(N*M) である必要があるのはなぜですか?

my $re = join('|',map quotemeta, @name_list);
$re = qr/$re/;
for my $file (@file_list) {
  if($file =~ /^($re)/) {
    my $name = $1;
    ... do what you need
  }
}
于 2013-04-20T20:12:27.370 に答える
0

ループで何か間違っているように見えます。

コード内のコメントに従う

for ( my $i = 0; $i < scalar @file_list ; $i++ )
{
    #use some string variable assign it ""
for ( my $j = 0; $j < @name_list ; $j++ )
    {
     if ( $file_list[ $i ] =~ m/^$name_list[ $j ]/ )
        {
        # assign string variable to founded name_list[$j]  
        break loop
        } 

    }
     # check condition if string not equal to  "" match found print your requirement with string value else match not found

}
于 2013-04-20T04:25:27.357 に答える