【问题标题】:perl regex matching same lineperl 正则表达式匹配同一行
【发布时间】:2017-03-14 05:57:21
【问题描述】:

第一个 if ($line =~ ...) 语句返回正确的匹配项 ($1),它被分配给 num。

第二个if ($line =~ ...) 语句没有返回任何值,因此没有为定义哈希分配任何内容。我单独测试了这个正则表达式语句,它确实返回了正确的匹配项 ($1)。

我希望将$definitions{$file}{$num} 分配给第二个正则表达式的返回值。

感谢您的帮助。我是 perl/regex 初学者。

my $dir = "/.../...";
my %definitions;
my $num;

opendir (DIR, $dir) or die $!;
    while (my $file = readdir(DIR)) {
        next if ($file =~ m/^\./);
        open FILE, "<$dir/$file" or die $!;
        my @lines = <FILE>;
            foreach my $line (@lines) {
                if ($line =~ m/\', (.*?)\, /g) {
                    $num = $1;
                }
                if ($line =~ m/\'(.*?)\'/g) { # no match is found
                    $definitions{$file}{$num} = $1; # nothing is assigned
                }
            }
        close FILE;
    }
closedir DIR;

编辑:

数据文件示例:(其他文件比较长,这个是最短的)

('钠离子', 169, 183), ('ID 3006450 289000 - 440000', 10, 36)

我正在尝试提取单引号之间的内容并将其分配给定义,然后取第一个数字并将其分配给 num。

【问题讨论】:

  • 您能提供文件中的示例行吗?
  • 您的意思是,简而言之,第二个正则表达式没有找到在线模式。你能给我们看一些数据吗?还有许多其他的 cmets,但这是第一件事。
  • if ($line =~ m/\', (.*?)\, /g) 代替 if ($line =~ m/\', (.*?)\, /) 和 ` if ($line =~ m/\'(.*?)\'/g)` 代替 ` if ($line =~ m/\' (.*?)\'/)` 可能会起作用。
  • 我可能没有说清楚:你的正则表达式没有什么是错误的,所以让它不匹配。这意味着您的数据可能没有'.*?' 模式。另外,请注意.* 也与 nothing 匹配,因此,如果您首先有 '' 在线,那么这就是您将捕获的内容。
  • 不应该 $CDE_instances$definitions 还是反之亦然?

标签: regex perl if-statement foreach hashmap


【解决方案1】:

在我们看到一些数据之前,无法完全回答这个问题。

同时,这里有一些关于代码的 cmets。首先是程序

use warnings;
use strict;
use 5.012;   # so readdir assigns to $_ in  while (readdir $dh)

my %definitions;

my $dir = '/path/to/dir';
opendir my $dh, $dir  or die "Can't open $dir: $!";

while (my $file = readdir($dh))
{
    next if $file =~ /^\./;
    next if -d $file;        # make sure we don't try to open() a dir

    open my $fh, '<', "$dir/$file" or die "Can't open $dir/$file: $!";

    while (my $line = <$fh>)
    {
        if (my ($num) = $line =~ m/', (.*?), / and    #' 
            my ($val) = $line =~ m/'(.*?)'/       )     
        {
            $definitions{$file}{$num} = $val;
        }   
    }   
    close $fh;
}    
close $dh;

很可能你可以在一个正则表达式中很好地完成两个匹配。请给我们一些数据。

评论

  • 始终拥有use warnings;use strict;

  • 额外的缩进可能看起来很有帮助,但通常会让人更难理解

  • 使用词法文件句柄,my $fh 而不是 FILE(等等)——它们更好

  • 从 v5.12 开始,readdir 在用作 while (readdir $dh) 时分配给 $_。然后谨慎地要求至少该版本,即use 5.012。我包括它以防它被使用

  • 我们很少需要先将文件读入数组。逐行阅读,while (&lt;$fh&gt;)

  • 在小范围内声明。 已发布代码中的全局 $num 允许出现错误:当第一个正则表达式失败但第二个匹配时,将值分配给找到的键 $num 以前。原因是对具有依赖关系的事物使用单独的 ifs,但本地 my $num 会有所帮助。

  • 可以从匹配项中分配,my ($var) = $string =~ /.../,除非有特定原因需要先测试然后分配。注意括号,将 list context 强加在 match operator 上,因为在这种情况下它会返回匹配的列表。见perlretut

  • 可以在if 条件内声明和赋值,就像你对while 所做的那样。在该范围内可以看到变量,就像它应该的那样。这会将处理拉到自己的块中,通常会使代码更清晰。我们需要查看数据以找到更好的方法来做到这一点。

  • /g 在这里毫无意义,虽然它以复杂的方式运行。见文末评论

  • 正则表达式中的额外转义:无需转义',

  • 可以编写while循环

    while (<$fh>) {
        if (my ($num) = /.../ and my ($val) = /.../)    ...
    

    使用$_ variable,这是正则表达式的默认值,就像 Perl 中的许多其他操作一样。

大胆猜测:

  • 模式.* 也匹配nothing,所以如果你在'stuff' 之前有'',那么你的模式'(.*?)' 将匹配'',捕获一个空字符串。

  • 第二个正则表达式匹配,但第一个失败,因此没有可分配的键。这与您单独对正则表达式的测试一致。 (你会在use warnings 生效时听到它。)


/g 在两个正则表达式中的存在都是错误的,很可能是直接错误(但请注意其他错误)。感谢 ikegami 的 cmets。来自Matching in list context in perlop

在标量上下文中,m//g 的每次执行都会找到下一个匹配项,如果匹配则返回 true,如果没有进一步匹配则返回 false。 [...]

这通常用在像while (/(...)/g) { ... } 这样的构造中,在每次迭代中对字符串的扫描继续从它上次匹配的地方,寻找下一个匹配。因此,在每次迭代中都会找到一个匹配项,因此在循环内一次处理一个匹配项。

文档中的短语“finds the next match”隐藏了复杂的行为,这两个 if 条件与 /g 严重混淆。这可以通过在单独的小脚本中进行测试来看到。

列表上下文中的行为不同,my @all_matches = /(...)/g;。请参阅文档。

【讨论】:

  • 有时有理由先测试然后赋值 - 如果您要覆盖现有变量,因为不匹配意味着您正在使用 undef 覆盖它。 (这是第二场比赛的结果,而不是第一场比赛)
  • @Sobrique 是的,说得好。更正了语句,将重新考虑代码。谢谢。
  • Re "/g 在这里没有意义",这就是造成 OP 问题的原因。
  • @ikegami 我想到了这一点,但是当我添加 $1 时,我的测试仍然将匹配项添加到 $1 ... 会尝试更多,谢谢。我不知道/g 在这里做了什么(似乎没有跳过迭代)
  • 那是因为你没有像 OP 那样的if (//g)。测试:perl -E'$_="\x27abc\x27, 123, "; say "!1" if /\x27, (.*?), /g; say "!2" if /\x27(.*?)\x27/g;'
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多