Regex validation of two patterns multiple comma-separated doesn't work - asp.net

In an ASP.NET form I am trying to find a pattern allowing multiple comma-separated elements but it doesn't seem to work. I need to allow either 4 letters and 2 digits (JEAN01) or 2 digits and 4 letters (01JEAN) any number of times: JEAN01,JEAN02,03JEAN,JEAN04
My first attempt (see https://regex101.com/r/E4JZVv/1) is:
/^([a-z-A-Z]{4}[0-9]{2}|[a-z-A-Z]{2}[0-9]{4})(,[a-z-A-Z]{4}[0-9]{2}|[a-z-A-Z]{2}[0-9]{4})*$
My second attempt (https://regex101.com/r/HU9cOS/1) is
((^|[,])[a-z-A-Z]{4}[0-9]{2}|[a-z-A-Z]{2}[0-9]{4})+
The first accepts only a couple of elements.

That should do the trick:
^((\w{4}\d{2}|\d{2}\w{4})(,|$))+
See: https://regex101.com/r/8hoNXl/1
Explanation:
^: Asserts position at start of the string
\w{4}\d{2}: 4 letters and 2 digits
\d{2}\w{4}: 2 digits and 4 letters
(,|$): Either , or the end of the string
+: Repeat between once and an unlimited number of times (greedy)

The setup of your first try is good, only:
you want to match 4 letters and 2 digits or 2 digits and 4 letters which is different from this part [a-z-A-Z]{2}[0-9]{4}
In the repetition of the group in the second part, you have to repeat the comma and put that following alternation itself also in a group so that the comma can be prepended for both alternations
Using a case insensitive match:
(?i)^(?:[a-z]{4}[0-9]{2}|[0-9]{2}[a-z]{4})(?:,(?:[a-z]{4}[0-9]{2}|[0-9]{2}[a-z]{4}))*$
See a regex demo.
For the second pattern that you tried, the assertion for the start of the string should not be in an alternation, or else you can have partial matches as it will also allow a comma.
You can repeat the alternation with an optional comma at the end. If you don't want to allow the string to start with a comma, you can for example using a negative lookahead (?!,) and use the alternation after asserting the start of the string.
(?i)^(?!,)((?:,|^)(?:[a-z]{4}[0-9]{2}|[0-9]{2}[a-z]{4}))+$
See another regex demo.

Related

Extract up to two more digits

This may be a very simple question but I have not much experience with regex expressions. This page is a good source of regex expressions but could not figure out how to include them into my following code:
data %>% filter(grepl("^A01H1", icl))
Question
I would like to extract the values in one column of my data frame starting with this A01H1 up to 2 more digits, for example A01H100, A01H140, A01H110. I could not find a solution despite my few attempts:
Attempts
I looked at this question from which I used ^A01H1[0-9].{2} to select up tot two more digits.
I tried with adding any character ^A01H1[0-9][0-9][x-y] to stop after two digits.
Any help would be much appreciated :)
You can use "^A01H1\\d{1,2}$".
The first part ("^A01H1"), you figured out yourself, so what are we doing in the second part ("\\d{1,2}$")?
\d includes all digits and is equivalent to [0-9], since we are working in R you need to escape \ and thus we use \\d
{1,2} indicates we want to have 1 or 2 matches of \\d
$ specifies the end of the string, so nothing should come afterwards and this prevents to match more than 2 digits
It looks as if you want to match a part of a string that starts with A01H1, then contains 1 or 2 digits and then is not followed with any digit.
You may use
^A01H1\d{1,2}(?!\d)
See the regex demo. If there can be no text after two digits at all, replace (?!\d) with $.
Details
^ - start of strinmg
A01H1 - literal string
\d{1,2} - one to two digits
(?!\d) - no digit allowed immediately to the right
$ - end of string
In R, you could use it like
grepl("^A01H1\\d{1,2}(?!\\d)", icl, perl=TRUE)
Or, with the string end anchor,
grepl("^A01H1\\d{1,2}$", icl)
Note the perl=TRUE is only necessary when using PCRE specific syntax like (?!\d), a negative lookahead.

How to match more than one ending character? [duplicate]

I try to find a regex that matches the string only if the string does not end with at least three '0' or more. Intuitively, I tried:
.*[^0]{3,}$
But this does not match when there one or two zeroes at the end of the string.
If you have to do it without lookbehind assertions (i. e. in JavaScript):
^(?:.{0,2}|.*(?!000).{3})$
Otherwise, use hsz's answer.
Explanation:
^ # Start of string
(?: # Either match...
.{0,2} # a string of up to two characters
| # or
.* # any string
(?!000) # (unless followed by three zeroes)
.{3} # followed by three characters
) # End of alternation
$ # End of string
You can try using a negative look-behind, i.e.:
(?<!000)$
Tests:
Test Target String Matches
1 654153640 Yes
2 5646549800 Yes
3 848461158000 No
4 84681840000 No
5 35450008748 Yes
Please keep in mind that negative look-behinds aren't supported in every language, however.
What wrong with the no-look-behind, more general-purpose ^(.(?!.*0{3,}$))*$?
The general pattern is ^(.(?!.* + not-ending-with-pattern + $))*$. You don't have to reverse engineer the state machine like Tim's answer does; you just insert the pattern you don't want to match at the end.
This is one of those things that RegExes aren't that great at, because the string isn't very regular (whatever that means). The only way I could come up with was to give it every possibility.
.*[^0]..$|.*.[^0].$|.*..[^0]$
which simplifies to
.*([^0]|[^0].|[^0]..)$
That's fine if you only want strings not ending in three 0s, but strings not ending in ten 0s would be long. But thankfully, this string is a bit more regular than some of these sorts of combinations, and you can simplify it further.
.*[^0].{0,2}$

Need some help building a somewhat simple REGEX expression

I'm trying to build a somewhat REGEX expression of the of only numbers including decimal with a maximum of 3 numbers to the right of the decimal (thousandths) and 50 to the left. Valid entries would like something like these.
1
1.0
.1
1.011
.011
1202938.123
1237923782.0
So far I have ^([0-9]*|\d*\.\d{1}?\d*){1,999}$.. Any help appreciated. Thanks.
I believe this should suffice:
^(?=.)\d{0,50}(?:\.\d{0,3})?$
See the regex demo. Note this will also match 1., if this is undesired change \d{0,3} to \d{1,3}. Similarely, this regex will match .5 (with no integer part), if you dont want this then use \d{1,50} instead of \d{0,50}.
You could try:
^(?=.+)\d{0,50}(?:\.\d{1,3})?$
Demonstration here at regex101.com
Explanation -
^ tells the regex that the match will begin at the start of the string,
\d{0, 50} matches 0 - 50 digits,
(?=.+) is a positive look-ahead, that tells the regex that the matching should only start if the line contains some characters in it (as rightly pointed out in the comments!),
(?:\.\d{1,3})? matches an optional dot (.), followed by 1 - 3 digits,
$ tells the regex that whatever it has matched so far will be followed by the end of the string.
Other way: You can check if the string isn't empty and if the dot is always followed by digits, putting a word-boundary at a strategic place:
^\d{0,50}\.?\b\d{0,3}$
As you can see, all is optional in the pattern except the word-boundary that does the magic.
demo

Regex to maintain matched parts

I would like to achieve this result : "raster(B04) + raster(B02) - raster(A10mB03)"
Therefore, I created this regex: B[0-1][0-9]|A[1,2,6]0m/B[0-1][0-9]"
I am now trying to replace all matches of the string "B04 + B02 - A10mB03" with gsub("B[0-1][0-9]]|[A[1,2,6]0mB[0-1][0-9]", "raster()", string)
How could I include the original values B01, B02, A10mB03?
PS: I also tried gsub("B[0-1][0-9]]|[A[1,2,6]0mB[0-1][0-9]", "raster(\\1)", string) but it did not work.
Basically, you need to match some text and re-use it inside a replacement pattern. In base R regex methods, there is no way to do that without a capturing group, i.e. a pair of unescaped parentheses, enclosing the whole regex pattern in this case, and use a \\1 replacement backreference in the replacement pattern.
However, your regex contains some issues: [A[1,2,6] gets parsed as a single character class that matches A, [, 1, ,, 2 or 6 because you placed a [ before A. Also, note that , inside character classes matches a literal comma, and it is not what you expected. Another, similar issue, is with [0-9]] - it matches any ASCII digit with [0-9] and then a ] (the ] char does not have to be escaped in a regex pattern).
So, a potential fix for you expression can look like
gsub("(B[0-1][0-9]|A[126]0mB[0-1][0-9])", "raster(\\1)", string)
Or even just matching 1 or more word chars (considering the sample string you supplied)
gsub("(\\w+)", "raster(\\1)", string)
might do.
See the R demo online.

Regular expression to match maximium of five words

I have a regular expression
^[a-zA-Z+#-.0-9]{1,5}$
which validates that the word contains alpha-numeric characters and few special characters and length should not be more than 5 characters.
How do I make this regular expression to accept a maximum of five words matching the above regular expression.
^[a-zA-Z+#\-.0-9]{1,5}(\s[a-zA-Z+#\-.0-9]{1,5}){0,4}$
Also, you could use for example [ ] instead of \s if you just want to accept space, not tab and newline. And you could write [ ]+ (or \s+) for any number of spaces (or whitespaces), not just one.
Edit: Removed the invalid solution and fixed the bug mentioned by unicornaddict.
I believe this may be what you're looking for. It forces at least one word of your desired pattern, then zero to four of the same, each preceded by one or more white-space characters:
^XX(\s+XX){0,4}$
where XX is your actual one-word regex.
It's separated into two distinct sections so that you're not required to have white-space at the end of the string. If you want to allow for such white-space, simply add \s* at that point. For example, allowing white-space both at start and end would be:
^\s*XX(\s+XX){0,4}\s*$
You regex has a small bug. It matches letters, digits, +, #, period but not hyphen and also all char between # and period. This is because hyphen in a char class when surrounded on both sides acts as a range meta char. To avoid this you'll have to escape the hyphen:
^[a-zA-Z+#\-.0-9]{1,5}$
Or put it at the beg/end of the char class, so that its treated literally:
^[-a-zA-Z+#-.0-9]{1,5}$
^[a-zA-Z+#.0-9-]{1,5}$
Now to match a max of 5 such words you can use:
^(?:[a-zA-Z+#\-.0-9]{1,5}\s+){1,5}$
EDIT: This solution has a severe limitation of matching only those input that end in white space!!! To overcome this limitation you can see the ans by Jakob.

Resources