在perl中未返回匹配的regex字符

xtfmy6hx  于 2023-03-19  发布在  Perl
关注(0)|答案(2)|浏览(142)

试着自学perl和正则表达式。
想知道如果用户在姓氏中输入Smith+,如何返回“+”。例如,“用户在姓氏字段中输入了无效字符,特别是“+”。”

unless (    $string1 =~ m/^[\w\.\s\-]+$/        &&  #last name must contain only words, periods, spaces and dashes
            $string2 =~ m/^[\w\.\s\-\@]+$/      &&  #email must contain only words, periods, spaces and dashes and at symbols
            $string3 eq "Find Order"            &&  #submit button field must match Find Order
            $string1 ne ''                      &&  #last name can't be empty
            $string2 ne ''                          #email can't be empty
       )
       {
       print "$& $1 $` $'";    #these don't return anything and it makes me sad
       &error;
       }
hl0ma9xz

hl0ma9xz1#

一种方法是显式测试错误的字符和/或条件

if ( $string1 eq '' ) { 
    say "Name can't be empty";
    error();
}
elsif ( my @bad_namechars = $string1 =~ /([^\w.\s-])/g ) { 
    say "Character(s) \"@bad_chars\" aren't expected in a name";
    error();
}
elsif ( $string2 eq '' ) { ... }
elsif ( my @bad_emailchars =~ /([^\w.\s-\@])/g ) { ... } # etc

如果您更愿意检测所有错误,以便通知用户,那么可以创建这些独立的if语句,并可能在每个语句中设置一个标志,以便能够在所有测试之后调用error()一次(如果发生任何错误)。
虽然这些测试可以用其他方式组织,但是一个包含多个正则表达式的复合测试并不能真正满足所述的需求,每个新的正则表达式都会重置大部分内部变量,而且无论如何,我们无法分辨哪个正则表达式匹配,因此错误报告也无法工作。
可以通过字符串表达式来实现这一点,这些表达式像上面那样分配捕获,但这会导致代码很容易被建议反对†--而且,由于Perl的&&(和and等)运算符短路,因此这样只能检测到一个错误。
我假设&error指的是一个定制的sub;如果是这样,则可能不需要&
†由于or短路,这仍然只能捕获一个(首次测试)错误

if ( 
    (my $empty_name = $string1 eq '')        or
    (my @bad_namechars = $string1 =~ /.../)  or
    ... 
) { ... }
wrrgggsh

wrrgggsh2#

我稍后会回答你的正则表达式问题,但首先要考虑一下这个特殊的问题。因为你只是在学习,所以用任何你喜欢的方式做玩具程序和实验都不是问题,但是一旦你开始做真实的的工作,还有一些其他的事情可以让你的生活更轻松。
我曾经用代码做过这样的事情,代码会遍历很多规则,而不关心它们是什么。在这个例子中,@rules是一个代码引用列表。如果规则失败,它会返回一个描述违规的定义字符串。否则,它什么也不返回,我们跳过它:

foreach my $rule ( @rules ) {
    my $problem = $rule->($test_string);
    next unless defined $problem;
    push @problems, $problem;
    }

这样做的好处是,问题的实质并不取决于规则本身。添加任意多的规则,这段代码都不会改变。您不必每次遇到需要检查的新情况时都编辑这段代码。相反,您只需在@rules中添加或删除代码引用。
因此,让我们为允许的字符制定一个规则:

my $test_string = 'Smith+';

my @rules = (
    sub { $_[0] =~ m/ \A [a-z] \z/xi ?
        undef : "Names can contain only letters" },
    );

my @problems;
foreach my $rule ( @rules ) {
    my $problem = $rule->($test_string);
    next unless defined $problem;
    push @problems, $problem;
    }

if( @problems ) { print join "\n", @problems }
else { print "No problems" }

输出:

Names can contain only letters

而且,这种方法允许我一次给予用户一个所有问题的列表,而不是在他们发现下一个问题之前抱怨他们要解决的一个问题。我将添加另一个规则。同样,我不必改变我用来检查规则的机制。我只需要在要检查的代码引用列表中添加另一个代码引用:

my @rules = (
    sub { $_[0] =~ m/ \A [a-z] \z/xi ? undef : "Names can contain only letters" },
    sub { $_[0] =~ m/ \A A-Z /xi ? undef : "Names must start with a capital letter" },
    );

现在输出为:

Names can contain only letters
Names must start with a capital letter

并不是说第二条规则应该是一条规则😼

更花哨的信息

现在,根据需要调整消息和代码引用。但是,请注意,这将检查它是否只有允许的字符,并指定一般规则。这通常就足够了。
但是,让我们把错误消息变成fancy buy,改变规则返回一个更花哨的消息:

my @rules = (
    sub {
        my @not_allowed = $_[0] =~ m/ ([^a-z]) /gxi;
        return unless @not_allowed;

        my %chars = map { $_, 1 } @not_allowed;
        my @chars = sort keys %chars;
        "Names can contain only letters. $_[0] also has @chars"
        },
    );

我本来可以打高尔夫球,但我不会。代码引用现在开始匹配所有不允许的单个字符。全局匹配(\g)in list context返回所有捕获。如果没有捕获,则没有违反规则,不返回任何内容。否则,我做了大量的工作来uniq字符列表并将它们添加到消息中。现在的输出是:

Names can contain only letters. Smith+ also has +

一般来说,我认为额外的工作不值得。

相关问题