【问题标题】:How can I use a regular expression to validate month input?如何使用正则表达式来验证月份输入?
【发布时间】:2020-09-18 09:20:13
【问题描述】:

我正在设置这个示例 Perl sn-p 来验证日期中的月份:

我想接受的一些场景是:

MM M

#!/usr/bin/perl
use strict;
use warnings;

my $pattern;
my $month = "(0[1-9]|1[012])";
my $day = "(0[1-9]|[12]\d|3[01])";

system("cls");

do {

    print "Enter in a month: ";
    chomp($pattern = <STDIN>);

    # We only want to print if the pattern matches
    print "Pattern matches\n" if ($pattern =~ /$month/);


} while ($pattern ne "Q");

当我运行它时,它会从 01-12 正确过滤,但是当我将正则表达式更改为:

$month = "(0?[1-9]|1[012])";

那么正则表达式允许 13、14 等...什么给出?

【问题讨论】:

  • (0?[1-0]|1[012]),正则表达式中的 [] 范围“1-0”无效。错别字?
  • 我同意 Greg Hewgill 的回答,但你没有得到你想要的原因是因为你的正则表达式缺少元字符来告诉正则表达式匹配你的字符串的开头和结尾(即 ^ 和 $ )。就目前而言,它匹配字符串的任何部分。

标签: regex perl


【解决方案1】:

如果你真的很喜欢用正则表达式,你需要把^和$,like

"^(0?[1-9]|1[012])$"

它不会匹配 13、14 ....

【讨论】:

  • 模式匹配或搜索问题。
  • 请注意,这将匹配011。对于我们中的一些只是从 SO 复制和粘贴的人来说可能并不明显......
  • @DanGordon 是对的。您可以将正则表达式更改为 ^(0[1-9]|1[012])$,这样可以解决问题。
  • 我会在0 之后删除?,因为验证1991-7 似乎不是很“标准”。
  • 不喜欢这个解决方案
【解决方案2】:

您不应该使用正则表达式来进行数值范围验证。你想要的正则表达式是:

/^(\d+)$/

那么,

if ($1 >= 1 && $1 <= 12) {
    # valid month
}

这比任何用于验证数字范围的正则表达式都更容易阅读。

顺便说一句,Perl 通过搜索 目标中的匹配表达式来评估正则表达式。所以:

/(0[1-9]|1[012])/

搜索 0 后跟 1 到 9,或 1 后跟 0、1 或 2。例如,这将匹配“202”以及许多其他数字。另一方面:

/(0?[1-9]|1[012])/

搜索可选的 0 1 到 9,或 1 后跟 0、1 或 2。因此“13”在这里匹配,因为它包含 1,匹配正则表达式的前半部分。为了使您的正则表达式按预期工作,

/^(0?[1-9]|1[012])$/

^$ 分别将搜索锚定到字符串的开头和结尾。

【讨论】:

  • /(0[1-9]|1[012])/ 不会匹配“202”,它会匹配“202”的 02 部分,但这完全不同。
  • +1:用于简单数值验证的正则表达式是多余的。
  • @Paul:取决于您所说的“匹配”是什么意思。在这种情况下,使用正则表达式 match202(即$pattern =~ m/$month/,注意m 前缀的含义)将返回一个真值,并且对于给出的代码是正是我们不想要的。但是,它只会捕获带有括号的02 部分。因此,如果您使用匹配来表示捕获,那么不,它不会。
【解决方案3】:

给你提示 - 月份数字“120”也匹配你的版本:-)

变化:

my $month = "(0[1-9]|1[012])";

my $month = /^(0[1-9]|1[012])$/;

然后用它玩更多

【讨论】:

    【解决方案4】:

    (0[1-9]|1[012])

    括号是为了让您可以在另一个块中使用它,例如,如果匹配整个 yyyy-MM-dd 日期格式

    来源:http://www.regular-expressions.info/dates.html

    【讨论】:

      【解决方案5】:

      不要使用正则表达式。

      Perl 能够根据上下文自动评估为数字或字符串。 01-09 将在数字上下文中计算为 1-9。因此,您可以简单地检查一个值:

      print "Enter in a month: ";
      chomp($pattern = <STDIN>);
      # We only want to print if the pattern matches
      print "Pattern matches\n" if ($pattern < 13 && $pattern > 0);
      

      【讨论】:

      • 而且,在你检查了范围之后,如果你需要前导零,你可以用 "%02d" 打印它。
      【解决方案6】:

      "^(1[012]|0?[1-9])$" 会更好,因为正则表达式首先被评估。假设你想匹配 '12' 并且你写了 "^(0?[1-9]|1[012])$",然后会选择 '1',因为 0?[1-9] 是第一个。

      【讨论】:

        【解决方案7】:

        这是一种方法

        while(1){
            print "Enter in a month: ";
            $pattern = <STDIN>;
            chomp($pattern);
            if ($pattern =~ /^(Q|q)$/ ){last;}
            if ($pattern =~ /^[0-9]$/ || $pattern =~ /^[0-9][12]$/ ) {
                print "Pattern matches\n";
            }else{
                print "try again\n";
            }
        }
        

        输出

        $ perl perl.pl
        Enter in a month: 01
        Pattern matches
        Enter in a month: 000
        try again
        Enter in a month: 12
        Pattern matches
        Enter in a month: 00
        try again
        Enter in a month: 02
        Pattern matches
        Enter in a month: 13
        try again
        Enter in a month:
        

        【讨论】:

        • 这个不行,41、42、51、52等都说“Pattern matches”。
        【解决方案8】:

        测试month/year

        ^(0?[1-9]|1[012])\/([2-9][0-9)]{3})$
        

        【讨论】:

          【解决方案9】:
          [^d>12|d<0] OR ^[d>12|d<0]
          

          【讨论】:

          • 也许您可以添加一个简短的解释,说明此正则表达式如何/为何起作用以进行澄清。
          猜你喜欢
          • 2011-02-22
          • 1970-01-01
          • 1970-01-01
          • 2013-12-12
          • 1970-01-01
          • 2016-02-26
          • 2019-01-07
          • 1970-01-01
          相关资源
          最近更新 更多