简体   繁体   中英

RegEx allowing digit, dash, comma

I would like to know the regular expression for c# fulfill the following pattern:

  • only allow numbers, comma and digit
  • must start and end with digit
  • range of digit is 0 to 999
  • digits (eg 1,2,3) or range (eg 1-3, 2-5) are separated by ','
  • between two ',' must be a digit or range
  • '-' must start and end with digit
  • Allow only 0-1 white space after ','

Example:

1-100,134,200 --> PASS. Maximum range of numbers 0-999
1,18,100      --> PASS
1, 18, 100    --> PASS. Allow 0-1 white space after ',' 
1,  18,100    --> FAIL. Due to more than 1 white space after ','
1-,18,100     --> FAIL. Due to no digit after '-'
-2,18,100     --> FAIL. Due to no digit before '-'
1,,18,100     --> FAIL. Due to no digit between ','
1, ,18,100    --> FAIL. Due to no digit between ','
,2,18,100     --> FAIL. Due to no digit before ','
1,18,100,     --> FAIL. Due to no digit after ','

I tried using the following code but it always return a true result:

string pattern = @"[0-9]+(?:-[0-9]+)?(,[0-9]+(?:-[0-9]+)?)*";
string test = @"1-5,13,238,-a";
bool result = Regex.IsMatch(test, pattern);

You can use this regex,

^(?:[1-9]\d\d|[1-9]?\d)(?:-(?:[1-9]\d\d|[1-9]?\d))?(?:,\s?(?:[1-9]\d\d|[1-9]?\d)(?:-(?:[1-9]\d\d|[1-9]?\d))?)*$

Explanation:

  • ^ - Start of string
  • (?:[1-9]\\d\\d|[1-9]?\\d) - Represents a number 0 to 999 and does not allow numbers with leading zeroes like 005
  • (?:-(?:[1-9]\\d\\d|[1-9]?\\d))? - Optionally also allows a number separated by hyphen - so together with first regex digit, it supports numbers like 22 and 22-33 etc
  • (?:,\\s?(?:[1-9]\\d\\d|[1-9]?\\d)(?:-(?:[1-9]\\d\\d|[1-9]?\\d))?)* - This part just supports comma separated optionally followed by a whitespace and whole of it zero or more times
  • $ - End of string

I could have used \\d{1,3} to represent a number from 0 to 999 but this would allow numbers like 004 which doesn't seem to be allowed seeing your sample data. But if indeed it is okay to allow numbers like 004 or 04 then you can replace [1-9]\\d\\d|[1-9]?\\d with \\d{1,3} in my regex to make it simple.

Regex Demo

You can try

   ^[0-9]{1,3}(?:\-[0-9]{1,3})?(?:,\s?[0-9]{1,3}(?:\-[0-9]{1,3})?)*$

pattern where

   ^                             String start
   0*[0-9]{1,3}                  1 to 3 digits
   (?:\-[0-9]{1,3})?             Possible minus (-) followed 1 to 3 digits (e.g. -456)
   ?:,\s?                        Comma and at most one whitespace  
   [0-9]{1,3}(?:\-[0-9]{1,3})?)* 1 to 3 digits or range repeated zero or more times
   $                             End of string        

Demo:

  string pattern = 
    @"^[0-9]{1,3}(?:\-[0-9]{1,3})?(?:,\s?[0-9]{1,3}(?:\-[0-9]{1,3})?)*$";

  string[] tests = new string[] {
    "123",
    "1234",
    "123-456",
    "123,456",
    "1-100,134,200",
    "1,18,100",
    "1, 18, 100",
    "1,  18,100",
    "1-,18,100",
    "-2,18,100",
    "1,,18,100",
    "1, ,18,100",
    ",2,18,100",
    "1,18,100,",
  };

  string[] results = tests
    .Select(test => $"{test,-20} --> {(Regex.IsMatch(test, pattern) ? "PASS" : "FAIL")}")
    .ToArray();

  string report = string.Join(Environment.NewLine, results);

  Console.Write(report);

Outcome:

123                  --> PASS
1234                 --> FAIL 
123-456              --> PASS
123,456              --> PASS
1-100,134,200        --> PASS
1,18,100             --> PASS
1, 18, 100           --> PASS
1,  18,100           --> FAIL
1-,18,100            --> FAIL
-2,18,100            --> FAIL
1,,18,100            --> FAIL
1, ,18,100           --> FAIL
,2,18,100            --> FAIL
1,18,100,            --> FAIL

Edit:

  • If you want to allow arbitrary many leading zeros (eg 000123 which is in fact 123 ), change each [0-9]{1,3} fragment into 0*[0-9]{1,3}
  • If you want ban leading zeroes ( 012 must fail, when 12 or 0 must) pass, change each [0-9]{1,3} fragment into (?:0|[1-9][0-9]{0,2})

Try following pattern: ^(?:\\d{1,3}-\\d{1,3}|\\d{1,3})(?:, ?(?:\\d{1,3}-\\d{1,3}|\\d{1,3}))*$

Explanation:

^ - match beginning of a string

(?:...) - non-capturing group

\\d{1,3} - match between 1 and 3 digits

- - match dash literally

| - alternation, match what's on the right ( \\d{1,3} ) or what on the left ( \\d{1,3}-\\d{1,3} )

, ? - match , followed by zero or one space

* - match (?:, ?(?:\\d{1,3}-\\d{1,3}|\\d{1,3})) zero or more times

$ - match end of a string

Demo

Here is my attempt using \\b a word boundary , maybe it's accurate enough for your needs.

^(?:\d{1,3}(?:-\d{1,3})?(?:, ?)?\b)+$

Play with it in this demo at regex 101 (explanation on the right side).

If you need the 0-999 check without leading zeros, replace \\d{1,3} with (?:[1-9]\\d\\d?|\\d)

There seem to be a lot of unusually complex answers here, so here's my 2 cents:

^([1-9]\d{0,2}|0)((\-|, ?)([1-9]\d{0,2}|0))*$

I consider this answer to be "less complex" not because its shorter (it's not the shortest here), but because it uses no "complex" regex behavior.
It uses only common matching groups () , common quantifiers ( * , {,} and ? ) and common matching symbols such as \\d and [1-9]

No lookarounds, lookaheads, lookbehinds, non-capturing groups, back-references, or any other "complex" regex behavior

Explanation:

^([1-9]\d{0,2}|0)((\-|, ?)([1-9]\d{0,2}|0))*$
^                                               Start of string
 (                                              Start of capture group:
  [1-9]\d{0,2}                                    A non-zero digit followed by 0 to 2 digits.
                                                    Matches any 1-999 number without leading zeroes
              |                                   OR
               0                                  Just a zero
                )                               End of capture group
                 (                             Start of capture group:
                  (                              Start of capture group:
                   \-                              Dash
                     |                             OR
                      , ?                          Comma Followed by 1 or 0 spaces
                         )                       End of capture group
                          ([1-9]\d{0,2}|0)       Same 0-999 capture group as earlier
                                          )     End of capture group
                                           *    Previous capture group matched 0 or more times
                                            $   End of string

In short, in case you want a slightly less technical description:

Number is a number from 0-999
(Represented with the regex: ([1-9]\\d{0,2}|0) )

Separator is a comma plus 0 or 1 spaces, or a dash
(Represented with the regex: (\\-|, ?) )

Group is a Separator , followed immediately by a Number
(Represented with the regex: ((\\-|, ?)([1-9]\\d{0,2}|0)) )

This regex matches:
A Number followed by zero or more Groups

Update

@bobblebubble in the comments pointed out that this will allow, for example 1-2-3 , which may not be allowed given - is intended to specify a range.

Here is a modification that fixes this:

^(([1-9]\d{0,2}|0)(-([1-9]\d{0,2}|0))?)(, ?(([1-9]\d{0,2}|0)(-([1-9]\d{0,2}|0))?))*$

This is much longer than the original, so in C# I would split it up as such:

// This pattern will match a single 0-999 number, or a range
string numPatt = "(([1-9]\d{0,2}|0)(-([1-9]\d{0,2}|0))?)";
// This pattern will match a csv of numPatts
string pattern = $"^{numPatt}(, ?{numPatt})*$";

Then use pattern as desired.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM