Can I write a regular expression that checks two lengths are equal? - regex

I want to match strings with two numbers of equal length, like : 42-42, 0-2, 12345-54321.
I don't want to match strings where the two numbers have different lengths, like : 42-1, 000-0000.
The two parts (separated by the hyphen) must have the same length.
I wonder if it is possible to do a regexp like [0-9]{n}-[0-9]{n} with n variable but equal?
If there is no clean way to that in one pattern (I must put that in the pattern attribute of a HTML form input), I will do something like /\d-\d|\d{2}-\d{2}|\d{3}-\d{3}|<etc>/ up to the maximum length (16 in my case).

This is not possible with regular expressions, because this is neither a type-3 grammatic (can be done with regular expression) nor a type-2 grammatic (can be done with regular expressions, which support recursion).
The higher grammar levels (type-1 grammatic and type-0 grammatic) can only be parsed using a Turing machine (or something compatible like your programming language).
More about this can be found here:
https://en.wikipedia.org/wiki/Chomsky_hierarchy#The_hierarchy
Using a programming language, you need to count the first sequence of digits, check for the minus and then check if the same amount of digits follows.
Without the minus symbol, this would be a type-2 grammatic and could be solved using a recursive regular expression (even if the right sequence shall not contain digits), like this: ^(\d(?1)\d)$
So you need to write your own, non-regular-expression check code.

You should probably split the String around the separator and compare the length of both parts.
The tool of choice in regex to use when specifying "the same thing than before" are back-references, however they reference the matched value rather than the matching pattern : no way of using a back-reference to .{3} to match any 3 characters.
However, if you only need to validate a finite number of lengths, it can be (painfully) done with alternation :
\d-\d will match up to 1 character on both sides of the separator
\d-\d|\d{2}-\d{2} will match up to 2 characters on both sides of the separator
...

Related

A Regex to ignore a set of words

Is there a way to set regex to ignore a set of words separated by space?
I have different products names like:
"Matrix 10X, 10 ml + DISPENSER"
"Matrix 10X,10ml + DISPENSER" where the quantity varies
What I'm trying to do is to replace using regex all words except for:
"10 ml" | "10 ML" | "10ml" ---> these are to be ignored
I have found a code to replace all characters except words separated by space (like "10 ml")
https://regex101.com/r/bG8vB4/5
and to replace them when they are together (like "10ml")
https://regex101.com/r/bG8vB4/4
but can find a way to mix them together to keep just "10 ml" OR "10 ML" OR "10ml" and remove other characters up to the end of the string
Regexps are a mathematical model to do efficient computer recognition of strings. As easy as getting a regular expression to match a string if it has any of some words, math demonstrates that the regexp to get a matcher of strings that just matches a string if it has none of those words is possible. The way to get such a regexp, although is far more complex.
On regular expressions theory, a regular language is one that allows you to set a finite automaton from a regular expression, and the automaton that recognizes a string if the original doesn't is feasible by just switching all accept states into non-accepting states. Once done this, the hardest part is to build a regular expression that matches that automaton (that is possible, but the final regular expression is far more complex, in general than the original) This can be solved with an example (a simple one) and you'll see that that is a complex thing (of course, some regexp libraries allow you to use an operand for this, but you don't specify if the one you are using does) One such sample is when you have to recognize a simple C language comment. A comment is a string delimited by the sequences /* and */ but in the inner part, you cannot have the sequence */.
The first approach could be to use the following regexp:
\/\*.*\*\/
but that fails, as the inner regexp includes the recognition of */ as part of it, so /* bla bla bla */ bla bla bla */ will be recognized as a comment in whole (it should end at the first */) so wee need a regexp that recognizes anything but not something that includes */
Such subexpression is:
([^*]|\*[^/])*
which means and undefinite concatenation of characters different that *, or sequences that, including the first character as * are not followed by /. If you follow that concatenation, you'll see that it's impossible to form a sequence */ leading to our final regexp:
\/\*([^*]|\*[^/])*\*\/
(now you see how the things complicate)
To extend this to a single word (as word, more than two letters) you have to consider that you can allow:
([^w]|w[^o]|wo[^r]|wor[^d])*
in the set, and if you have two words (like foo and bar) you have to write:
([^f]|f[^o]|fo[^o]|[^b]|b[^a]|ba[^r])*
meaning that for each word you have such regexps, making the final regexp a bit complicated. Also, there can be interactions between words if some can be the prefix to another or some have the same prefix chars. This also can have the problem that the compilation of regexps into finite automata has produced many libraries that consider the | operator non conmutative and resolve them in a non conmutative way, leading to erroneous results.
You have not explained also what you mean with ignoring. If you mean matching them and pass around, is different to mean to ignore the whole line they could appear on. The regexps then (an the definition of the problem you need to solve is quite different ---my explanation was in the sense of rejecting a full sentence if it has any of the words on it, which probably is not what you mean) So please, explain (in your question) what do you mean with:
accepting you have matched a sentence containing a word.
rejecting such a sentence.
what are you rejecting (or ignoring) at all.
Rejecting just a word, is simply selecting a sencence that contains that word, and mark the word to be able to pass over it. But that's a different problem, and it requires to select sentences that do have the word.

In regex, when using star, can you make some stars, in the regex being repeated, repeat the same number of times?

Sorry for the long-winded question. It's probably best to show an example.
I'm trying to match a matrix as a string:
[[]]
[[][][]]
[[0][1][2]]
[[,,][,,][,,]]
[[0,1,2][3,4,5]]
Although regex is rarely pretty to look at, this is what I have come up with:
\[(\[-?(\d+(\.\d*)?)*(,-?(\d+(\.\d*)?)*)*\])+\]
It matches everything. HOWEVER the commas must always be repeated n times. If there are 5 commas, then 4 commas, then 6 commas, it's not a valid matrix. Is there a flag or variables I can use? Or is it outside the scope of regex?
No, a regular expression cannot do that.
Here is a formal proof that a very similar case to yours is not possible. It proves that the language L = {anbn : n ≥ 0} is not a "regular language", so it cannot be matched with a regular expression. That language is essentially a*b* with the two stars repeating the same number of times. Your case also involves a star repeating the same number of times, so it's also not possible.
In your case the best option is probably to use a regexp to match each "row" of the matrix, that is: do a global search for \[([^][]*)\], for each match grab group 1, which is the row's content, then count the number of commas and compare all the counts (or do a split on comma, using your language's split operation, if you actually need the numbers.)
PS: [^][] is the character class of all chars except open and close brackets. The closing bracket needs to be the first char after the ^, it's a special case.

Regular Expression Challenge: For every consecutive 6 characters, there must be two 1s (alphabet {"0", "1"})

So, I want to built a regular expression that I can pass in a string of 0s and 1s (e.g. "0010101000111100100011110001101100011") and then make sure that for every 6 consecutive characters, there needs to be at least two 1s in that block.
Also, strings less than length 6 should pass.
Examples of passing strings:
""
"00"
"11000011"
"01010100"
Examples of failing strings:
"110000000011"
"000001"
These examples are of very small strings, but I want to build one to take any length string.
Now, I'm looking for a nice way to express this in a regular expression, rather than having solution with a loop and such.
Just use this regex and check that it doesn't match:
/000000|000001|000010|000100|001000|010000|100000/
Here is a regex that should do the trick (matches valid strings):
^((?!0{6}|10{5}|010{4}|001000|000100|0{4}10|0{5}1)[01])+$
Example: http://www.rubular.com/r/VelZ1Iqml6
This uses a negative lookahead inside of a repetition so that the condition is checked at every location in the string.
If you are able to just check for strings that don't match, that is more straightforward, and you can use davidrac's solution or this slightly shortened version (which I use in the lookahead of my regex):
0{6}|10{5}|010{4}|001000|000100|0{4}10|0{5}1

Algorithm to match list of regular expressions

I have two algorithmic questions for a project I am working on. I have thought about these, and have some suspicions, but I would love to hear the community's input as well.
Suppose I have a string, and a list of N regular expressions (actually they are wildcard patterns representing a subset of full regex functionality). I want to know whether the string matches at least one of the regular expressions in the list. Is there a data structure that can allow me to match the string against the list of regular expressions in sublinear (presumably logarithmic) time?
This is an extension of the previous problem. Suppose I have the same situation: a string and a list of N regular expressions, only now each of the regular expressions is paired with an offset within the string at which the match must begin (or, if you prefer, each of the regular expressions must match a substring of the given string beginning at the given offset).
To give an example, suppose I had the string:
This is a test string
and the regex patterns and offsets:
(a) his.* at offset 0
(b) his.* at offset 1
The algorithm should return true. Although regex (a) does not match the string beginning at offset 0, regex (b) does match the substring beginning at offset 1 ("his is a test string").
Is there a data structure that can allow me to solve this problem in sublinear time?
One possibly useful piece of information is that often, many of the offsets in the list of regular expressions are the same (i.e. often we are matching the substring at offset X many times). This may be useful to leverage the solution to problem #1 above.
Thank you very much in advance for any suggestions you may have!
I will assume that you really have the power of regular expressions.
To determine whether a string is matched by one of expressions e_1, e_2, ..., e_n, just match against the expression e_1 + e_2 + ... + e_n (sometimes the + operator is written as |).
Given expression-offset pairs (e_1, o_1), ..., (e_n, o_n) and a string, you can check whether there is i such that the string is matched by expression e_i at offset o_i by matching against the expression .{o_1}e_1 + ... + .{o_n}e_n.
Depending on the form of the individual expressions, you can get sublinear performance (not in general though).
If your expressions are sufficiently simple (wildcard patterns are), AND your set of expressions are predetermined, i.e. only the input to be matched changes, THEN you may construct a finite state machine that matches the union of your expressions, i.e., the expression "(r1)|(r2)|...".
Constructing that machine takes time and space at least O(N) (but I guess it is not exponential, which is the worst case for regular expressions in general). Matching is then O(length(input)), independent of N.
OTOH, if your set of expressions is to be part of the program's input, then there is no sublinear algorithm, simply because each expression must be considered.
(1) Combine all the regular expressions as a big union: (r1)|(r2)|(r3)|...
(2) For each regex with offset n add n dots to the beginning plus an anchor. So his.* at offset 6 becomes ^......his.*. Or if your regex syntax supports it, ^.{6}his.*.

Tools that turn regular phrases into regular expressions?

I know there are a lot of tools that allow you to create regular expressions and test regular phrases against them, but is there a tool that allows you to type just a regular phrase or word, etc and it will generate the regular expression for you. For example, typing:
xyz555.. would generate the correct regular expression. It may not be the most ideal expression, but it would be a useful learning tool.
Because such analysis can't be done deterministically. It's impossible to take a single sample (or any particular number of samples) and generate a pattern.
For example, your example data could mean three alphabetic characters followed by three numeric characters...
...or it could be any number of alphabetic characters followed by three numerics
...or three alphabetic followed by three '5' characters.
It's impossible to determine exactly what the pattern is when more than one pattern fits the data.