Trying to build a regular expression to check pattern

后端 未结 7 744
我在风中等你
我在风中等你 2021-01-20 17:32

a) Start and end with a number
b) Hyphen should start and end with a number
c) Comma should start and end with a number
d) Range of number should be from 1-31

相关标签:
7条回答
  • 2021-01-20 18:10

    An optimal Regex for this topic could be:

    ^(?'int'[1-2]?[1-9]|3[01])((,\g'int')|(-\g'int'(?=$|,)))*$
    

    demo

    0 讨论(0)
  • 2021-01-20 18:12

    I propose the following regex:

    (?<number>[1-9]|[12]\d|3[01]){0}(?<thing>\g<number>-\g<number>|\g<number>){0}^(\g<thing>,)*\g<thing>$
    

    It looks awful but it isn't :) In fact the construction (?<name>...){0} allows us to define a named regex and to say that it doesn't match where it is defined. Thus I defined a pattern for numbers called number and a pattern for what I called a thing i.e. a range or number called thing. Next I know that your expression is a sequence of those things, so I use the named regex thing to build it with the construct \g<thing>. It gives (\g<thing>,)*\g<thing>. That's easy to read and understand. If you allow whitespaces to be non significant in your regex, you could even indent it like this:

    (?<number>[1-9]|[12]\d|3[01]){0}
    (?<thing>\g<number>-\g<number>|\g<number>){0}
    ^(\g<thing>,)*\g<thing>$/
    

    I tested it with Ruby 1.9.2. Your regex engine should support named groups to allow that kind of clarity.

    irb(main):001:0> s1 = '1-5,5,15-29'
    => "1-5,5,15-29"
    irb(main):002:0> s2 = '1,28,1-31,15'
    => "1,28,1-31,15"
    irb(main):003:0> s3 = '15,25,3'
    => "15,25,3"
    irb(main):004:0> s4 = '1-24,5-6,2-9'
    => "1-24,5-6,2-9"
    irb(main):005:0> r = /(?<number>[1-9]|[12]\d|3[01]){0}(?<thing>\g<number>-\g<number>|\g<number>){0}^(\g<thing>,)*\g<thing>$/
    => /(?<number>[1-9]|[12]\d|3[01]){0}(?<thing>\g<number>-\g<number>|\g<number>){0}^(\g<thing>,)*\g<thing>$/
    irb(main):006:0> s1.match(r)
    => #<MatchData "1-5,5,15-29" number:"29" thing:"15-29">
    irb(main):007:0> s2.match(r)
    => #<MatchData "1,28,1-31,15" number:"15" thing:"15">
    irb(main):008:0> s3.match(r)
    => #<MatchData "15,25,3" number:"3" thing:"3">
    irb(main):009:0> s4.match(r)
    => #<MatchData "1-24,5-6,2-9" number:"9" thing:"2-9">
    irb(main):010:0> '1-1-1-1'.match(r)
    => nil
    
    0 讨论(0)
  • 2021-01-20 18:13

    The other approaches have not restricted the allowed range of numbers. This allows 1 through 31 only, and seems simpler than some of the monstrosities people have come up with ...

    ^([12][0-9]?|3[01]?|[4-9])([-,]([12][0-9]?|3[01]?|[4-9]))*$
    

    There is no check for sensible ranges; adding that would make the expression significantly more complex. In the end you might be better off with a simpler regex and implementing sanity checks in code.

    0 讨论(0)
  • 2021-01-20 18:16

    Here is my workings

    Numbers:

    0|([1-9][0-9]*) call this expression A Note this expression treats zero as a special case and prevents numbers starting with a zero eg 0000001234

    Number or a range:

    A|(A-A) call this expression B (i.e (0|([1-9][0-9]*))|((0|([1-9][0-9]*))-(0|([1-9][0-9]*)))

    Comma operator

    B(,B)*

    Putting this togher should do the trick and we get

    ((0|([1-9][0-9]*))|((0|([1-9][0-9]*))-(0|([1-9][0-9]*))))(,((0|([1-9][0-9]*))|((0|([1-9][0-9]*))-(0|([1-9][0-9]*)))))*

    You can abbreviatge this with \d for [0-9]

    0 讨论(0)
  • 2021-01-20 18:19

    try this

    ^\d+(-\d+)?(,\d+(-\d+)?)*$
    

    DEMO

    0 讨论(0)
  • 2021-01-20 18:22

    How about this? This will check rules a, b and c, at least, but does not check rule d.

    /^[0-9]+(-[0-9]+)?(,[0-9]+(-[0-9]+)?)*$/

    If you need to ensure that all the numbers are in the range 1-31, then the expression will get a whole lot uglier:

    /^([1-9]|[12][0-9]|3[01])(-([1-9]|[12][0-9]|3[01]))?(,([1-9]|[12][0-9]|3[01])(-([1-9]|[12][0-9]|3[01]))?)*$/

    Note that your example c contains a number, 56, that does not fall within the range 1-31, so it will not pass the second expression.

    0 讨论(0)
提交回复
热议问题