我的正则表达式可以改进吗?

时间:2016-03-07 10:40:45

标签: c# regex validation

是的,另一个正则表达式问题。不客气;-P

这是我第一次在C#中编写自己的正则表达式进行简单的字符串验证。我想我已经开始工作,但作为一个学习练习,我想知道它是否可以改进以及我是否犯过任何错误。

字符串看起来都像这样:

  

T20160307.0001

规则:

  • 以字母T开头。
  • 日期格式为YYYYMMDD。
  • 一个句号。
  • 最后4个字符始终为数字。应该有4个。

这是我的正则表达式(fiddle):

  

^(?I)[T] 20 [0-9] {2} [0-1] [0-9] [0-3] [0-9]。\ d {4} $

  • ^断言字符串的开头。
  • (?i)[T]检查我们是否有字母T,不区分大小写。
  • 20 YYYY从20开始(我将在2100年前去世,所以我不在乎任何事情:-P)
  • [0-9]{2} YYYY第二部分的0到99之间的任何数字。
  • [0-1][0-9] 0或1表示月初,0-9表示月份的第二部分。
  • [0-3][0-9] 0-3为第一天的部分,0-9为第二部分。
  • .完全停止。
  • \d{4} 4个数字字符。
  • $断言字符串结尾。

我已经看到的一个陷阱是日期验证。 2016 - 1935年(第19个月的第35天)被视为有效。我已阅读some / other / posts关于实现这一点,我认为这与数字范围匹配,但我无法理解格式。

我会接受一个简单解决日期问题的答案,如果有人会对ELI5如何运作有所帮助,但其他改进将是一个值得欢迎的奖励。

编辑为了避免进一步混淆,我应该声明我知道DateTime.TryParse等。如上所述,我正在利用这个作为学习正则表达式的机会,并认为这是一个很好的起点。对于任何浪费时间的人都很抱歉,我应该在原帖中说清楚。

3 个答案:

答案 0 :(得分:4)

你可以做的事情是:

  • 避免匹配所有unicode数字的\d字符类(因为您只需要ascii数字)
  • 代替[0-1],您可以撰写[01]
  • 转义点以计算文字点(而不是任何字符)
  • 如果它是唯一的字符
  • ,则无需将T放入字符类中
  • 最终您可以删除内联修饰符并使用[Tt]代替T


^(?i)T20[0-9]{2}[01][0-9][0-3][0-9]\.[0-9]{4}$

^[Tt]20[0-9]{2}[01][0-9][0-3][0-9]\.[0-9]{4}$

其他的事情:你真的需要添加额外的日期检查,因为你无法真正测试日期格式是否正确? (想一想闰年)为什么不呢:

^(?i)T(20[0-9]{6})\.[0-9]{4}$

如果您想知道日期是否真的存在,请捕获它并使用DateTime.TryParse方法对其进行测试。

答案 1 :(得分:3)

为什么使用正则表达式只需使用DateTime.TryParseExact方法。我会这样实现它,并对其他字符进行额外检查:

bool IsCorrectFormat(string input)
{
    //14 is a magic number for the length of the expected format
    if (input.Length == 14 && input.StartsWith("T", StringComparison.OrdinalIgnoreCase))
    {
        DateTime dt;
        if (DateTime.TryParseExact(input.Substring(1), "yyyyMMdd.ffff", CultureInfo.InvariantCulture, DateTimeStyles.None, out dt))
        {
            return true;
        }
    }

    return false;
}

我不知道格式是否正确但你总是可以从1到6子串获得yyyyMMdd然后检查最后5个字符的小数点和数字。

编辑:如果必须使用正则表达式

我过去曾使用过这个正则表达式。请注意,它不会检查闰年

@"^(((0[1-9]{1}|[1-2][0-9]{1}|3[01]{1})(0[13578]{1}|1[12]{1}))" //For a 31 day month
+ @"|"
+ @"((0[1-9]{1}|[1-2][0-9]{1}|30)(0[469]{1}|11))" //For a 30 day month
+ @"|"
+ @"((0[1-9]{1}|1[0-9]{1}|2[0-8]{1})(02)))" //For a 28 day month(feb)
+ @"([0-9]{4})$"; //For the year

答案 2 :(得分:1)

  

如上所述,我将此作为学习正则表达式的机会,并认为这是一个很好的起点。

使用正则表达式验证日期肯定不是一件容易的事情,特别是考虑到闰年所涉及的复杂规则。但这是有可能的。

如果以YYYYMMdd格式输入有效日期,则以下表达式将匹配:

(?=\p{IsBasicLatin}{8}) # ensures \d matches only 0-9
(?!0000)\d{4} # year any 4-digit year, except 00
(?:0[1-9]\d|1[012]) # month 01-12
(?: 
   # day logic for leap years
   (?:
      (!00)[012]\d # Days 01-29 (we exclude 2/29 later)
      | (?<!02)30  # Day 30 valid for all months except Feb
      | (?<=0[13578]|1[02])31 # Day 31 valid for some months
   )
   # Non-Leap-year logic.  Do not allow 2/29 if the provided year
   # is not a leap year.
   (?<!
     (?:
        [13579] # years ending with odd number are not leap years
        | [02468][26]|[13579][048] # years not divisible by 4
                                     # are not leap years (02, 06, 10, ...)
        | (?:[02468][\d-[048]]|[13579][\d-[26]])00 # years divisible by
                                                 # 100 are not leap years,
                                                 # unless divisible by 400

     )0229)
)

RegexOptions.IgnorePatternWhitespace汇编。您可以使用^T~\.\d{4}$匹配示例中的完整字符串,将~替换为上述表达式。