1

我有两个数组:

  • @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";
}

哦,我转置了数组,以便它们将打印到一个 outfile 到单独的行中。

4

4 回答 4

2

简短版本:您正在构建错误的名称数组。看看这一行:

$unique{name} ++;

您只是在增加name哈希的条目。你可能想要这个$name变量。

更长的版本

关于英语和 Foreach 循环

你的代码有点不成熟,看起来更像 C 而不是 Perl。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)

这表示相同的条件。

循环控制

我会做两个假设:

  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函数接受一个条件和一个列表,并将返回列表中与条件匹配的所有元素。当前元素是 in $_,默认情况下匹配正则表达式。有关正则表达式的解释,请参见上文。

注意:这仍然允许字段包含空格,即使处于领先位置。要拆分所有空格,您可以提供split包含单个空格的字符串的特殊参数:split ' '. 这将使grep不必要的。

for循环也可以用作语句修饰符,即like EXPR for LIST。当前元素在$_. $_我们为哈希中的条目分配了一些东西%uniq(它已经初始化为空哈希)。这可以是一个数字,但undef也可以。

密钥以看似随机的顺序返回。但是由于多个名称可以匹配一个文件,但我们只想选择一个匹配项,我们必须首先匹配最具体的名称。因此,我按照名称的长度按降序对名称进行排序。

于 2013-04-20T11:06:25.243 回答
1

你的代码似乎对我有用。我所做的只是构造两个这样的数组:

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 回答