nice guy
nice guy

Reputation: 151

Multiple spaces, multiple commas and multiple hypens in alphanumeric regex

I am very new to regex and regular expressions, and I am stuck in a situation where I want to apply a regex on an JSF input field.

Where

  1. alphanumeric
  2. multiple spaces
  3. multiple dot(.)
  4. multiple hyphen (‐)

are allowed, and Minimum limit is 1 and Maximum limit is 5.

And for multiple values - they must be separated by comma (,)

So a Single value can be:

3kd-R

or

k3

or

-4

And multiple values (must be comma separated):

kdk30,3.K-4,ER--U,2,.I3,

By the help of stackoverflow, so far I am able to achieve only this:

(^[a-zA-Z0-9 ]{5}(,[a-zA-Z0-9 ]{5})*$)

Upvotes: 0

Views: 106

Answers (2)

Luv2code
Luv2code

Reputation: 1109

You've done pretty good. You need to add hyphen and dot to that first character class. Note: With the hyphen, since it delegates ranges within a character class, you need to position it where contextually it cannot be specifying a range--not to say put it where it seems like it would be an invalid range, e.g., 7-., but positionally cannot be a range, i.e., first or last. So your first character class would look something like this:

[a-zA-Z 0-9.-]{1,5} or [-a-zA-Z0-9 .]{1,5}

So, we've just defined what one segment looks like. That pattern can reoccur zero or more times. Of course, there are many ways to do that, but I would favor a regex subroutine because this allows code reuse. Now if the specs change or you're testing and realize you have to tweak that segment pattern, you only need to change it in one place.

Subroutines are not supported in BRE or ERE, but most widely-used modern regex engines support them (Perl, PCRE, Ruby, Delphi, R, PHP). They are very simple to use and understand. Basically, you just need to be able to refer to it (sound familiar? refer-back? back-reference?), so this means we need to capture the regex we wish to repeat. Then it's as simple as referring back to it, but instead of \1 which refers to the captured value (data), we want to refer to it as (?1), the capturing expression. In doing so, we've logically defined a subroutine:

([a-zA-Z 0-9.-]{1,5})(,(?1))*

So, the first group basically defines our subroutine and the second group consists of a comma followed by the same segment-definition expression we used for the first group, and that is optional ('*' is the zero-or-more quantifier).

If you operate on large quantities of data where efficiency is a consideration, don't capture when you don't have to. If your sole purpose for using parenthesis is to alternate (e.g., \b[bB](asset|eagle)\b hound) or to quantify, as in our second group, use the (?: ... ) notation, which signifies to the regex engine that this is a non-capturing group. Without going into great detail, there is a lot of overhead in maintaining the match locations--not that it's complex, per se, just potentially highly repetitive. Regex engines will match, store the information, then when the match fails, they "give up" the match and try again starting with the next matching substring. Each time they match your capture group, they're storing that information again. Okay, I'm off the soapbox now. :-)

So, we're almost there. I say "almost" because I don't have all the information. But if this should be the sole occupant of the "subject" (line, field, etc.--the data sample you're evaluating), you should anchor it to "assert" that requirement. The caret '^' is beginning of subject, and the dollar '$' is end of subject, so by encapsulating our expression in ^ ... $ we are asserting that the subject matches in it's entirety, front-to-back. These assertions have zero-length; they consume no data, only assert a relative position. You can operate on them, e.g., s/^/ / would indent your entire document two spaces. You haven't really substituted the beginning of line with two spaces, but you're able to operate on that imaginary, zero-length location. (Do some research on zero-length assertions [aka zero-width assertions, or look-arounds] to uncover a powerful feature of modern regex. For example, in the previous regex if I wanted to make sure I did not insert two spaces on blank lines: s/^(?!$)/ /)

Also, you didn't say if you need to capture the results to do something with it. My impression was it's validation only, so that's not necessary. However, if it is needed, you can wrap the entire expression in capturing parenthesis: ^( ... )$.

I'm going to provide a final solution that does not assume you need to capture but does assume the entire subject should consist of this value:

^([a-zA-Z 0-9. -]{1,5})(?:,(?1))*$

I know I went on a bit, but you said you were new to regex, so wanted to provide some detail. I hope it wasn't too much detail.

By the way, an excellent resource with tutorials is regular-expressions dot info, and a wonderful regex development and testing tool is regex101 dot com. And I can never say enough about stack overflow!

Upvotes: 1

nu11p01n73R
nu11p01n73R

Reputation: 26667

Something like

^[-.a-zA-Z0-9 ]{1,5}(,[-.a-zA-Z0-9 ]{1,5})*$

Changes made

  • [-.a-zA-Z0-9 ] Added - and . to the character class so that those are matched as well.

  • {1,5} Quantifier, ensures that it is matched minimum 1 and maximum 5 characters

Regex demo

Upvotes: 1

Related Questions