【问题标题】:Matching all the numbers匹配所有数字
【发布时间】:2013-07-31 21:38:26
【问题描述】:

我在另一个主题中询问了有关匹配数字(例如 123)的问题。这太狭窄了,随着我对 Regex 的深入了解,我发现您确实必须定义任何东西。所以我要求指数符号并在this post/^keyword\s+(-?(?:\d+|\d*\.\d*)(?:[Ee]-?(?:\d+|\d*\.\d*))?)/得到答案。我试图理解这一点,但到目前为止失败了。

所以我现在问的更具体。我需要匹配数字,我在这里举一些例子:

13
-999
83.12300
.151
-.213
1e14
124e2
-9e-4

你明白了,普通的数学东西。

更具体地说,我给你我的 Perl 代码。我在一行上搜索keyword,需要从这一行中获取一个值。我想在一个正则表达式中获得这个值,因为我使用 or 语句 || 的解决方法似乎会导致问题。

my $value;
open(FILE,"data.dat") or die "error on opening data: $!\n";
while (my $line = <FILE>) {
        if (($line =~ /^keyword\s+(-?(?:\d+|\d*\.\d*)(?:[Ee]-?(?:\d+|\d*\.\d*))?)/x) || ($line =~ /^keyword\s*(\d*\.\d*)/)) {
                $value = $1;
        };
}
close(FILE);

编辑

感谢所有到目前为止的提示。

【问题讨论】:

    标签: regex perl


    【解决方案1】:

    转到 cpan 并获取Regexp::Common

    这样使用

    use Regexp::Common;
    
    my $re = $RE{num}{real};
    
    if ( $line =~ /^keyword\s+($re)/ ) {
      $value = $1;
    }
    

    比自己动手的正则表达式滚动要容易得多。

    【讨论】:

    • 也感谢这个提示,但我真的很想了解这一点。正则表达式似乎非常强大,只需要让我的大脑支持它
    • @Borodin 谢谢你的意见,但你可以相信我已经读过一些东西。 Ofc 我不像其他人那样有经验......我有一个教程中的例子,它说明/\d\d:\d\d/ 应该像12:15 这样数学。只是为了用冒号展示我的假设。抱歉,我没有时间阅读数小时的教程,我打算稍后再获取。但是 atm 我有一个特定的问题并尝试解决这个问题。谢谢。
    • @bashophil:现在,您应该通过采纳 xcramps 的建议并使用Regexp::Common 来解决您的“特定问题”。我改进了他的答案以包含示例代码。
    【解决方案2】:

    还有另一种方法可以做到这一点,您不需要正则表达式。你可以从Scalar::Util使用looks_like_number

    这是一个示例:How do I tell if a variable has a numeric value in Perl?我已将其粘贴在这里。


    例子:

    #!/usr/local/bin/perl
    
    use warnings;
    use strict;
    
    use Scalar::Util qw(looks_like_number);
    
    my @exprs = qw(1 5.25 0.001 1.3e8 foo bar 1dd);
    
    foreach my $expr (@exprs) {
        print "$expr is", looks_like_number($expr) ? '' : ' not', " a number\n";
    }
    

    给出这个输出:

    1 is a number
    5.25 is a number
    0.001 is a number
    1.3e8 is a number
    foo is not a number
    bar is not a number
    1dd is not a number
    

    编辑:@borodin 的评论

    你可以这样使用它:

    my $value;
    open(FILE,"data.dat") or die "error on opening data: $!\n";
    while (my $line = <FILE>) {
            if (($line =~ /^keyword +(.*)/)) {
                 my $number = $1;
                 if ( looks_like_number($number) ) { 
                     $value = $number;
                 }
            };
    }
    

    编辑:如果你必须有一个正则表达式,你可以这样的表达式:

     #!/bin/perl
     use strict;
     use warnings;
    
     my @numbers = ( 'keyword 13',
                     ' word   25',
                     'keyword -999',
                     'keyword 83.12300',
                     'keyword  .151',
                     'keyword -.213',
                     'keyword 1e14',
                     'keyword 124e2',
                     'keyword -9e-4 ',
                     ' keyword  e43e',
                     'keyword 4.5.6',
                     'keyword 4..e',
                     'keyword NaN',
                     'keyword Inf');
    
     for (@numbers) {
    
          if ( /^keyword +(-?((\d+\.?\d*)|(\d*\.?\d+))([Ee]-?\d+)?)/ ) {
    
             print "$1 is a number\n";
    
         } else {
             print "$_ does not match keyword or is not a number\n";
         }
    
     }
    

    【讨论】:

    • 这不会从字符串中间拉出一个数字,这似乎是必需的。
    • @borodin,见编辑。我想我的意思是,我们不需要制作正则表达式来匹配数字,因为该功能已经存在于核心中。
    • 但是现在您依赖的数字是keyword 之后字符串其余部分的整个。假设您要查找keyword 3.14+.67+7e4 中的数字。 looks_like_number 所做的只是,给定一个字符串,告诉你 整个事物 是否看起来像一个数字。
    • 您可以匹配/^keyword (.*?)+(.*?)+(.*?)/ 并验证每个数字,如果这是您所期望的。如果您担心空格,可以将 .* 更改为 \S*。您仍然需要一个正则表达式,但我认为它使您的代码更具可读性,您可以检查它是否是带有子例程的数字而不是长正则表达式。
    【解决方案3】:

    您代码中的第二个正则表达式似乎是多余的,您可以安全地删除它。第一个正则表达式应该匹配你所有的测试用例。有什么它似乎无法使用的东西吗?

    您还应该调整您的正则表达式,因为目前它认为-.e-. 是一个数字。这来自与 . 匹配的 \d*\.\d*。您可以尝试 (?:\d+(?:\.\d*)?|\.\d+) 而不是您所拥有的,这将匹配 1) 数字,2) 数字后跟小数,可能还有更多数字,或 3) 小数后跟数字。

    【讨论】:

    • 谢谢丹。第一个正则表达式(在 or 语句中)适用于 exp 表示法。但它不适用于例如12.124。所以我尝试添加第二个适用于这个数字的正则表达式,如果我单独使用它的话。但在 or 语句中它不起作用。
    • @bashophil:你越来越糊涂了。您的第一个正则表达式在开头匹配 12.124 just fine, except for the keyword`,但您神秘地未能解释。
    • @Borodin 请阅读我的整个帖子。我搜索一个关键字并尝试从该行获取一个数字(无论哪种格式)。
    • @Borodin 我再次检查了它:if ($line =~ /^keyword\s+(-? (?:\d+|\d*\.\d*) (?:[Ee]-? (?:\d+|\d*\.\d*) ) ?)/x) { 得到了 exp 符号。使用if ($line =~ /^keyword\s*(\d*\.\d*)/) {,我可以得到类似12.124 的东西,但是如果我将这两个语句与 or 语句结合起来,它不适用于这个浮点数。
    • 您的测试字符串中的keyword12.124 之间是否有空格?一个正则表达式使用\s+,另一个使用\s*
    【解决方案4】:

    感谢您的信息性帖子和我最近几天阅读的内容,我能够了解更多的正则表达式结构。所以对于这个相当简单的任务,我不想使用额外的模块/包,而是想坚持使用正则表达式。我进行了一些测试和更改,以消除冗余并适应我的任务。所以我不会在一行上有几个数字,而且一行可以有空格。此外,数字的结尾由分号定义。总而言之,我发布了我的最终代码。谢谢大家的帮助。

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    my @numbers=(
    "keyword 152;",
    "keyword 12.23;",
    "keyword -2.001;",
    "keyword .123;",
    "keyword -12.;",
    "keyword 55.44.33;",
    "keyword 3e14;",
    "keyword -3.000e0014;",
    "keyword 5e-04;",
    "   keyword     5e-04;  ",
    "keyword 5e-04  ;",
    "keyword .1e2;",
    "keyword 9.e3;",
    "keyword -0.01E-03;",
    "keyword 1.3e-03;",
    "keyword 1dd;",
    "keyword -12E3e1;",
    "keyword -.e.;",
    "keyword -.e-.;");
    
    for (@numbers) {
    
    if (    /\s* keyword \s+        # stuff before matched number
        ( -?            # optional minus sign
          (?:           # no saving of group in brackets
            (?:\d+\.?\d*)       # match trailing digit and possible floating point number
            |           # or
            (?:\.\d+)       # no trailing digit and forced fpn
          )
        (?:[Ee]-?\d+)?      # optional exponential notation
        )           # end of group to be matched
        ;\s*            # stuff after matched number
        /x) {
    
    print "<<__$_\__>>\n\t $1 \n";
    } else { 
    print "<<__$_\__>>\n\t !!!!! no matching here !!!!!\n";
    }
    }
    

    输出:

    <<__keyword 152;__>>
         152 
    <<__keyword 12.23;__>>
         12.23 
    <<__keyword -2.001;__>>
         -2.001 
    <<__keyword .123;__>>
         .123 
    <<__keyword -12.;__>>
         -12. 
    <<__keyword 55.44.33;__>>
         !!!!! no matching here !!!!!
    <<__keyword 3e14;__>>
         3e14 
    <<__keyword -3.000e0014;__>>
         -3.000e0014 
    <<__keyword 5e-04;__>>
         5e-04 
    <<__    keyword     5e-04;  __>>
         5e-04 
    <<__keyword 5e-04   ;__>>
         !!!!! no matching here !!!!!
    <<__keyword .1e2;__>>
         .1e2 
    <<__keyword 9.e3;__>>
         9.e3 
    <<__keyword -0.01E-03;__>>
         -0.01E-03 
    <<__keyword 1.3e-03;__>>
         1.3e-03 
    <<__keyword 1dd;__>>
         !!!!! no matching here !!!!!
    <<__keyword -12E3e1;__>>
         !!!!! no matching here !!!!!
    <<__keyword -.e.;__>>
         !!!!! no matching here !!!!!
    <<__keyword -.e-.;__>>
         !!!!! no matching here !!!!!
    

    PS:我读到?: 可能不会在代码运行时保存资源,这会使正则表达式不太友好,因此可能会忽略这一点。

    【讨论】:

      猜你喜欢
      • 2020-07-30
      • 2016-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-21
      相关资源
      最近更新 更多