Perl正则表达式捕获组并停止匹配。

3

我需要在这个perl正则表达式上获得一些帮助

s/.*?<\?lsmb if\s*?(\S*)\s*?\?>/$1/

在下面的代码中,解析出一些非空格字符 [A-Z] [a-z] [0-9] [_] ,这些字符被任意数量的空格和其他字符包围。我尝试了各种Perl正则表达式,在下面的程序中都有注释。
我认为我的主要问题是在结尾处停止匹配。
下面的代码运行8个测试,我希望找到一个能够通过所有8个测试的东西。
#!/usr/bin/perl

use strict;
use warnings;
use diagnostics;

my $count = 0;
my $t = 0;
#examples of things I need to match, match => catagory
my $self = {  'customerfax' => 'alpha',
             '_____' => 'Underscore',
             '000000' => 'numeric',
             'letter_reason_4' => 'alfa-numeric-underscore',
             'customerphone7' => 'alfa-numeric', 
             'customer_phone' => 'alfa-underscore',
           };
# must contain   <?lsmb 'varname from $self' ?> 
# may contain any amount of whitespace chars where one is depected
# will end with \n that is removed by chop below         
my $test1 = qq|<?lsmb if customerfax ?>  caacaacac\n|;
my $test2 = qq|<?lsmb if _____ ?> bbb\n|;
my $test3 = qq|<?lsmb if 000000 ?> cccc\n|;
my $test4 = qq|<?lsmb if letter_reason_4 ?><t \></'><><><>\n|;  # /
my $test5 = qq| <?lsmb if customerfax ?> |;
my $test6 = qq|<?lsmb if  customerphone7   ?> \<?lsmb ignore this >n|;
my $test7 = qq|<?lsmb if  customer_phone  ?>\n|;
my $test8 = qq| avcscc 34534534 <?lsmb if letter_reason_4 ?> 0xffff\n|;

strip_markup($test1);
strip_markup($test2);
strip_markup($test3);
strip_markup($test4);
strip_markup($test5);
strip_markup($test6);
strip_markup($test7);
strip_markup($test8);

if ($count == 8) { print "Passed All done\n";}
else { print "All done passed  $count out of 8 Try again \n"; }


sub strip_markup { 
    $_= shift;
    #print "strip_markup $_ \n";
    if (/<\?lsmb if /) {
        chop; # gets rid ot the new line
        #original
        #s/.*?<\?lsmb if (.+?) \?>/$1/;
        #What I have tried:
        #s/.*?<\?lsmb if(?:\s)*?(\S+?)(?:\s)*?\?>\b/$1/;
        s/.*?<\?lsmb if\s*?(\S*)\s*?\?>/$1/;
        #s/.*?<\?lsmb if\s*?([A-Za-z0-9_]*?)\s*?\?>/$1/;
        #s/.*?<\?lsmb if[\s]*?(\S*?)[\s]*?\?>/$1/;
        #s/.*?<\?lsmb if (\S*?) \?>/$1/;
        #s/.*?<\?lsmb if (\S+?) \?>/$1/;
        #s/.*?<\?lsmb if ([\S]+?)([\s]+?)\?>/$1/;
        #s/.*?<\?lsmb if[\s]+([\S]+)[\s]+\?>/$1/;
        #s/.*?<\?lsmb if\s*?([\S]*?)\s*?\?>/$1/;
        #s/.*?<\?lsmb if\s+?([\S]+?)[\s]+?\?>/$1/;
        #s/.*?<\?lsmb if ([\S]+?) \?>/$1/;
        #s/.*?<\?lsmb if\s*?([\S_]*?)\s*?\?>/$1/;
        #s/.*?<\?lsmb if\s*?([[a-zA-Z]|[\d]|[_]]*?)\s*?\?>/$1/;
        #s/.*?<\?lsmb if\s*?([a-zA-Z\d_]*?)\s*?\?>/$1/;
        #s/.*?<\?lsmb if\s*?([^[:space:]]+?)\s*?\?>/$1/;

        $t++;
        print "Test $t ";
        #look up the result as the hash key
        my $ok = $self->{$_};
        if ($ok) { 
                $count++;
                print "OK passed $ok,";
        }
        print qq|Test Value : '$_' \n|;
    }
}

以下是一些测试以及它们应该返回的内容:

  • 测试1 = <?lsmb if customerfax ?> caacaacac\n 应该返回 customerfax
  • 测试2 = <?lsmb if _____ ?> bbb\n 应该返回 _____
  • 测试8 = avcscc 34534534 <?lsmb if letter_reason_4 ?> 0xffff\n 应该返回 letter_reason_4

1
你能展示一下你需要从输入字符串中得到的输出示例吗?解释不够清晰,有些字符被“解析”,而其他字符则没有。 - zdim
你为什么使用 chop 而不是 chomp?实际上,你为什么要在函数内修改全局变量 ($_)? - melpomene
也许我没有理解"the Tests ... should return"的含义--您可以通过 /<\?lsmb if (\w+)/ 捕获编辑中显示的内容。这是您需要的全部吗,提取 customerfax 或下划线或零或 letter_reason_4 ...? - zdim
@turtle 顺便说一下,如果要让人们收到评论通知,您需要在他们的用户名前面加上 @ 符号。帖子(问题或答案)的所有者始终会收到通知,我在这里使用它作为示例。 - zdim
这是一个XML示例吗?它看起来有点像,但我无法确定。 - Sobrique
显示剩余3条评论
1个回答

1
如果我理解您的要求是正确的,所需短语可以通过简单的方式提取。
my ($match) = $string =~ /<\?lsmb \s+ if \s+ (\w+)/x

列表上下文中,m// 匹配运算符返回一个带有匹配项的列表。即使只有一个,我们也需要列表上下文 - 在标量上下文中,其行为是不同的。列表上下文来自于从中分配给列表,my (...) =/x 修饰符仅允许我们在内部使用空格以便阅读。请参见perlretut以获取入门指南。 <?之前可能没有被指定,因为模式可以匹配字符串中的任何位置。 \w代表[A-Za-z0-9_](请参见perlrecharclass),似乎与您的示例和描述相匹配。 \S更加宽容。在\w+之后不需要任何内容。
此外,没有必要先测试模式是否存在。
sub strip_markup 
{
    my ($test_res) = $_[0] =~ /<\?lsmb if (\w+)/;

    if ($test_res) {
        # ...
    }

    return $test_res;         # return something!
}

没有理由进行替换,因此我们使用匹配。

我知道你正在处理无法更改的代码,但仍想发表评论

  • 这里不需要删除换行符。但是当你这样做时,请使用 chomp 而不是 chop

  • sub 使用全局变量。这可能会导致错误。在小范围内声明。传递

  • sub 修改全局变量。这经常导致错误,而很少有必要这样做

  • 使用数组重复相同的事情

  • 这可以以不同方式组织,以更清晰地分离工作


例如。
my @tests = (
    qq|<?lsmb if customerfax ?>  caacaacac\n|,
    # ...
);

my ($cnt, $t);

foreach my $test (@tests) 
{
    my $test_res = strip_markup($test);

    if (defined $test_res) {
        $t++;
        print "Test $t ";
        #look up the result as the hash key
        my $ok = $self->{$test_res};
        if ($ok) { 
                $count++;
                print "OK passed $ok,";
        }
        print qq|Test Value : '$_' \n|;
    }
    else { }  # report failure
}

sub strip_markup {
    my ($test_res) = $_[0] =~ /<\?lsmb \s+ if \s+ (\w+)/x;
    return $test_res;
}

defined测试$test_res的目的是允许假值(例如0'')作为有效结果。

报告代码可以并且应该在另一个子程序中。


谢谢你的工作。我会尝试重构原始代码,以避免使用$ _。 - turtle
@turtle 很好,谢谢你的更新 :) 这很好,我有两个评论。(1)当您在函数中使用参数时,为什么要将其命名为与结果相同的名称?这很容易出错。称之为,比如说,my $test_string = shift; 然后 my ($test_res) = $test_string =~ /.../; return $test_res;(2) 在 /.../x 中,x 是允许我们使用空格,只是为了可读性。当您不使用额外的空格时,您不需要 x modifier (就像它被称呼的那样)。 - zdim
@turtle 我在正则表达式下面的开头添加了一个带注释的段落。 - zdim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接