Trying to create a regex for pattern validation

a) Start and end with number
b) Hyphen must begin and end with a number
c) The comma should start and end near
d) The number range must be from 1-31

[Edit: need this rule in regular expression, thanks Ed-Heal!]
e) If a number starts with a hyphen (-), it cannot end with any other character than a comma AND follow all the rules listed above.
For example. 2-2.1 OR 2.2-1 valid and 1-1-1-1 invalid

E.G.
a) 1-5,5,15-29
b) 1.28.1-31.15
c) 15.25.3 [Edit: Replaced 56 by 3, thanks for pointing out Brian!]
d) 1-24.5-6.2-9

Tried this, but it passes even if the line starts with a comma:

/^[0-9]*(?:-[0-9]+)*(?:,[0-9]+)*$/ 
+3
source share
7 answers

How about this? This checks 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 make sure that all numbers are in the range 1-31, the expression will be much 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 the number 56, which is not in the range 1-31, so it will not pass the second expression.

+3
source

Here are my works

Numbers:

0|([1-9][0-9]*) call this expression A Note that this expression treats zero as a special case and prevents numbers starting from zero, for example, 0000001234

Number or Range:

A|(AA) call this expression B (ie (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 will 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 abbreviate this with \d for [0-9]

0
source

try it

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

DEMO

0
source

Other approaches do not limit the allowable range of numbers. It only allows from 1 to 31, and seems simpler than some of the monsters that people came up with ...

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

No check for reasonable ranges; adding that this will make the expression significantly more complex. In the end, you might be better off with a simpler regular expression and implementation of health checks in your code.

0
source

Using the same logic in my previous answer, but limiting the range

A becomes [1-9]\d|3[01] B becomes ([1-9]\d|3[01])|(([1-9]\d|3[01])-([1-9]\d|3[01]))

General expression

(([12]\d|3[01])|(([12]\d|3[01])-([12]\d|3[01])))(,(([12]\d|3[01])|(([12]\d|3[01])-([12]\d|3[01]))))*

0
source

I suggest the following regular expression:

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

It looks terrible, but it is not. In fact, the construction (?<name>...){0} allows us to define a named regular expression and say that it does not match where it is defined. So I defined a pattern for numbers named number and a pattern for what I called a thing, that is, a range or number called thing . Then I know that your expression is a sequence of these things, so I use the name regex thing to create it with the \g<thing> construct. It gives (\g<thing>,)*\g<thing> . It is easy to read and understand. If you allow spaces to be inconsequential in your regular expression, you can even backtrack 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 must support these groups to provide such 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
source

The optimal regex for this topic could be:

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

demonstration

0
source

All Articles