Regex Isn't Hard (2023)
108 comments
·April 21, 2025michaelt
vitus
It's especially ironic given that the title of the post is "Regex Isn't Hard", and then it proceeds to make several (syntactical and logical) errors in the one real-world example.
Syntax error aside (there's an extra ] floating around), it's not even close to correct -- it'll match "999.999.999.000.999." among other things, will never match just one digit (there's a missing ?), and always insists on the trailing dot.
mannykannot
In practice, the first unpaired ] is treated as an ordinary character (at least according to https://regex101.com/) - which does nothing to make this regex fit for its intended purpose. I'm not sure whether this is according to spec. (I think it is, though that does not really matter compared to what the implementations actually do.)
Characters which are sometimes special, depending on context, are one more thing making regexes harder than they appear at first sight.
The author's willingness to publish code without even minimal testing does not inspire confidence.
vitus
Agreed entirely, on all those points.
Calling the extra ] a syntax error was a slight exaggeration on my behalf, but that was clearly an unintended extra character -- there's no way the author thinks "123].45].67].89]" is a valid IP address. But yes, it does compile and is interpreted as a valid regex, albeit not a useful one in this context.
The out-of-range values are not ideal but can be fixed with post-validation in code (which is cleaner than writing unnecessarily complicated regex, anyways). The missing ? leads to a bunch of false negatives, and the trailing . causes even more problems.
michaelt
Correct - it'll accept "999.999.999.000.999." but it'll reject "127.0.0.1"
ninkendo
Writing one correctly is pretty complicated task if you’re trying to write a simple tutorial… off the top of my head, you’d need:
(
(
25[0-5] # 250-255
|
2[0-4][0-9] # 200-249
|
1[0-9]{2} # 100-199
|
[1-9][0-9] # 10-99
|
[0-9]
)
\.
){3}
(
25[0-5] # 250-255
|
2[0-4][0-9] # 200-249
|
1[0-9]{2} # 100-199
|
[1-9][0-9] # 10-99
|
[0-9]
)
… but without all the nice white space and comments, unless you’re willing to discuss regex engines that let you do multi-line/commented literals like that… I think ruby does, not sure what other languages.The problem is that expressing “an integer from 0-255” is surprisingly complicated for regex engines to express. And that’s not even accounting for IP addresses that don’t use dots (which is legal as an argument to most software that connects to an IP address), as other commenters have pointed out.
wat10000
Regex can be good but you need to be willing to bail out when it’s not appropriate.
For something like locating IP addresses in text, using a regex to identify candidates is a great idea. But as you show, you don’t want to implement the full validation in it. Use regex to find dotted digit groups, but validate the actual numeric values as a separate step afterwards.
vitus
> I think ruby does, not sure what other languages.
You're right that Ruby has it. Perl also has /x, of course (since most of Ruby regex was "inspired" directly by Perl's syntax), as well as Python (re.VERBOSE). Otherwise, yeah, it's disappointingly rare.
bazoom42
.net also supports verbose regex.
stavros
Well, it depends on how specific you want to be. You could do `.*`, and this will match an IP address, or you can be as specific as trying to specify number ranges digit by digit, which is so complicated that it doesn't merit a "can't even".
Also, `16843009` is an IP address, try pinging it.
aadhavans
Shameless plug: My Regex engine (https://pkg.go.dev/gitea.twomorecents.org/Rockingcool/kleing...) has dedicated syntax for this kind of task.
<0-255>\.<0-255>\.<0-255>\.<0-255>
will only match full IPv4 addresses, but is a lot stricter than the one in the article.EDIT: formatting
nialv7
When the article starts with an AI generated image that adds nothing to the explanation, it tends to make me suspicious if the article itself was written by an AI as well...
iugtmkbdfil834
Is it because everyone tries to make it look short?
edit: asking partly, because in my current work I occassionally have to convince non-technical users to use one type of entry over other. For that reason, easy to read, simple regex wins over fancy, but convoluted regex.
vitus
> For that reason, easy to read, simple regex wins over fancy, but convoluted regex.
Sure, I'd take \d+\.\d+\.\d+\.\d+ over... "((2(5[0-5]|[0-4][0-9])|1[0-9]{2}|[1-9]?[0-9])\.){3}(2(5[0-5]|[0-4][0-9])|1[0-9]{2}|[1-9]?[0-9])", assuming that I then validate the results afterwards.
TheDong
"matches an ip address" is a vague enough specification that of course people fail.
Is it what `inet_addr` accept? In that case, "1", "0x1", "00.01", "00000.01", and more are all ip addresses. `ping` accepts all of em anyway.
Is a valid ipv6 address one with the square brackets around it? Is "::1" a valid ip address? What about "fe80::1%eth2"? ping accepts both of these on my machine (though probably not on yours, since you probably don't have an eth2 interface)
NikkiA
square brackets around an IP address predates IPv6, it was/is? used to bypass DNS lookups and some (very) old programs required IP addresses inside [...] otherwise they were assumed to be a domain name with all the rules that implied.
null
russfink
^^^ this ^^^ I can’t understand my own regexes after a couple weeks - much less the ones I got the AI to write for me because I’m lazy or time constrained.
gwd
So my brother doesn't code for a living, but has done a fair amount of personal coding, and also gotten into the habit of watching live-coding sessions on YouTube. Recently he's gotten involved in my project a bit, and so we've done some pair programming sessions, in part to get him up to speed on the codebase, in part to get him up to speed on more industrial-grade coding practices and workflows.
At some point we needed to do some parsing of some strings, and I suggested a simple regex. But apparently a bunch of the streamers he's been watching basically have this attitude that regexes stink, and you should use basically anything else. So we had a conversation, and compared the clarity of coding up the relatively simple regex I'd made, with how you'd have to do it procedurally; I think the regex was a clear winner.
Obviously regexes aren't the right tool for every job, and they can certainly be done poorly; but in the right place at the right time they're the simplest, most robust, easiest to understand solution to the problem.
kelafoja
My problem is that regexes are write-only, unreadable once written (to me anyway). And sometimes they do more than you intended. You maybe tested on a few inputs and declared it fit for purpose, but there might be more inputs upon which it has unintended effects. I don't mind simple, straight-forward regexes. But when they become more complex, I tend to prefer to write out the procedural code, even if it is (much) longer in terms of lines. I find that generally I can read code better than regexes, and that code I write is more predictable than regexes I write.
bazoom42
> I tend to prefer to write out the procedural code, even if it is (much) longer in terms of lines.
This might work for you, but in general the amount of bugs is proportional to the amount of code. The regex engine is alredy throughly tested by someone else while a custom implementation in procedural code will probably have bugs and be a lot more work to maintain if the pattern changes.
justin66
> This might work for you, but in general the amount of bugs is proportional to the amount of code.
If you wanted to look for cases which serve as an exception to this rule, code relying on regexes would be an excellent place to start.
kelafoja
That is quite a generalization. The regex engine is tested, but my specific regular expression isn't. My ability to write correct regular expressions is weak, so there can be many bugs in the one line of regular expession.
rerdavies
In general, the correctness of the code is proportional to its readability.
I also prefer procedural code instead of regexes.
latexr
> unreadable once written (to me anyway). (…) there might be more inputs upon which it has unintended effects.
https://regex101.com can explain your regex back to you, and allows you to test it with more inputs.
Though I’m not trying to convince you to always use regular expressions, I agree with GP:
> Obviously regexes aren't the right tool for every job, and they can certainly be done poorly; but in the right place at the right time they're the simplest, most robust, easiest to understand solution to the problem.
jcelerier
What makes them unreadable to you ? 99% of the time you can just read them character by character with maybe some groups and back references
bluecheese452
I don’t think this is a particularly useful question. If they could accurately describe what exactly is confusing they wouldn’t be confused.
rusk
These are all valid criticisms of regex
but they’re not an excuse to avoid regex. Similarly git has many warts but there’s no getting around it. Same with CSS
If you want to run with the herd though you need to know these things, even enjoy them.
You can rely on tooling and training wheels like Python VERBOSE but you’re never going to get away from the fact that the “rump” of the population works with them.
Easier to bite the bullet and get practised. I’ve no doubt you have the intellect - you only need be convinced it’s a good use of your time.
bena
Kind of fair.
I don't incorporate a lot of regular expressions into my code. But where I do like them is for search and replace. So I do treat them as mostly disposable.
TrackerFF
Confession: Regex knowledge is one of those things I've let completely atrophy after integrating LLMs into my workflow. I guess if the day comes that AI/ML models suddenly disappear, or become completely unavailable to me, I'll have to get into the nitty gritty of Regex again...but until that time, it is a "solved problem" for my part.
TheOtherHobbes
It's hilarious that the most reliable way to write a complex regex is to fire up billions of dollars of state of the art ML code and ask for what you want in English.
throw-qqqqq
IMO it’s a “language” you need to understand in order to use.
Just like you wouldn’t copy/paste any random snippet into your source code if you don’t understand exactly what it does.
I see a lot of broken regex at work from people who use regular expressions but don’t understand them (for various reasons).
It used to come with a “found this on stackoverflow”-excuse, but mostly now it’s “AI told me to use this” instead.
qiine
yeah programmers famously understands all the random boilerplate incantations they copy past in their code to get things going.
totally definitively
tomsmeding
I know some people consider this fine. I do not. The fact that the world is not ideal does not mean that we cannot continue to improve things.
throw-qqqqq
We all have our own ideas of Utopia I guess :)
SnowingXIV
Yeah, this is my heaviest use case too. Mostly because it generally does save me a bit of time and is easily verifiable with tools like rubular and then can tweak what is needed once 90% there.
noxer
> Instead, use a range negation, like [^%] if you know the % character won’t show up. It doesn’t hurt to be a little more explicit.
This is absolutely horrible, pattern are fairly readable if they follow the syntax logic. Matching "everything but that random character that will not appear" is absurd. Also the idea that a . (dot) behaves arbitrary in different languages shows a sever lack up understanding about regex syntax. Ofc you can't write a proper pattern if you don't know which syntax is used. If anything you would force override the behavior of the . (dot) with the appropriate flag to ensure it works the same with different compatible regex engines.
strunz
Agreed, I wanted to write the whole article off after that suggestion. That is such a terrible anti pattern that would confuse everyone who looked at it, even people with decades of experience.
latexr
I’m a fan of regular expressions, though I understand why many people wince at the sight. You should avoid showing them to a non-programmer who is interested in learning to code, because they’ll immediately fear programming is intractable.
Even as much as I like regex, I wouldn’t recommend this post. One reason is the code style is too close to regular text:
> a matches a single character, always lowercase a.
That sentence uses “a” three times, two of them as code and once as an indefinite article, but it’s not immediately obvious to eye. VoiceOver completely fumbles it, especially considering the sentence immediately after.
A more important reason against recommending the article is that I find a bunch of the arguments to be unhelpful. If you’re trying to convince people to give regular expressions a chance, telling them to ignore `.` and use `[^%]` is going to bite them. That’s not super common (important when trying to learn more from other sources) and even an experienced regexer must do a double take to figure out “is there a reason this specific character must not be matched?” Furthermore, no new learner is going to remember that four character incantation, and neither are they going to understand what’s happening when their code doesn’t work because there was a `%` in their text. People need to learn about `.` (possibly the most common character in regex) if only because they also need to learn to escape it and not ignore it when there is a literal period in the text. Don’t tell people to ignore repetition ranges either, they aren’t difficult to reason about and are certainly simpler to read than the same blob of intractable text multiple times.
LaputanMachine
I've also seen people use `[\s\S]` to match all characters when they couldn't use `.`.
tomsmeding
This is a common approach when the regex needs to match any character including newlines; `.` often doesn't.
dimava
I generally use `[^]`
Also you can use . with the dotAll /s
BMc2020
Regex is much easier if you don't do it all at once. It's perfectly acceptable to, say, trim all the leading spaces, store the result in a temp variable, trim all the trailing spaces, store the result in a temp variable, remove all the hyphens. etc. etc.
Everyone tries to create the platonic ideal regex that does everything in one line.
justlikereddit
Nothing is hard once you've learned to do it intuitively.
The hardest part is remembering how you struggled with it when you started.
criddell
It can help to learn what the “regular” part of regular expression refers to.
nickez
Found an error immediately "Any lowercase character" doesn't match all Swedish lowercase characters.
iugtmkbdfil834
Ok. This sounds like an interesting detour. Can you elaborate on that one? I doubt I will ever use that knowledge, but it sounds like it is worth knowing anyway.
lalaithion
The author says “any lowercase character” but they mean “any character between the character ‘a’ and the character ‘z’”, which happens to correspond to the lower case letters in English but doesn’t include ü, õ, ø, etc.
comrade1234
lol really? Why not? Is that true for all encodings? Is it a bug or a feature? What about a simple character set like gsm-7 Swedish?
lalaithion
The author says “any lowercase character” but they mean “any character between the character ‘a’ and the character ‘z’”, which happens to correspond to the lower case letters in English but doesn’t include ü, õ, ø, etc.
Someone
> but they mean “any character between the character ‘a’ and the character ‘z’”, which happens to correspond to the lower case letters in English
‘Only’ in the most commonly used character encodings. In EBCDIC (https://en.wikipedia.org/wiki/EBCDIC), the [a-z] range includes more than 26 characters.
That’s one of the reasons POSIX has character classes (https://en.wikipedia.org/wiki/Regular_expression#Character_c...). [:lower:] always gets you the lowercase characters in the encoding that the program uses.
comrade1234
I would expect [a-z] to mean any lowercase in any language, not lowercase but only a to z. So I’d get bitten by that one.
criddell
The Swedish alphabet includes characters outside of the a-z range.
goku12
If you take the regex subset that works uniformly across all regex engines (even for just perl-compatible engines), you would probably get nothing done. They all have some minor variations that make it impossible to write a regex for a particular engine without a reference sheet open nearby, even if you have years of experience writing them. And those 'shortcuts' like look-ahead and look-behind are often too useful to be neglected completely.
Crafting regexes is story of its own. The other commentor has described it. Just to summarize, regexes are fine for simple patterns. But their complexity explode as soon as you need to handle a lot of corner cases.
null
mannykannot
Here’s a regex crossword:
https://jimbly.github.io/regex-crossword/
See also: Are Regex Crosswords NP-hard?
https://cs.stackexchange.com/questions/30143/are-regex-cross...
boricj
In a previous job I've done some stupid tricks with regexes. Inside a MongoDB database I had documents with a version field in string form ("x.y.z") and I needed to exclude documents with a schema too old to process in my queries.
One can construct a regex that matches a number between x and y by enumerating all the digit patterns that fit the criteria. For example, the following pattern matches a number between 1 and 255: ^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$
This can be extended to match a version less than or equal to x.z.y by enumerating all the patterns across the different fields. The following pattern matches any version less than or equal to 2.14.0: ^([0-1]\.\d+\.\d+)|(2\.[0-9]\.\d+|(2\.1[0-3]\.\d+))$
Basically, I wrote a Java method that would generate a regex with all the patterns to match a version greater than or equal to a lower bound, which was then fed to MongoDB queries to exclude documents too old to process based on the version field. It was a stupid solution to a dumb problem, but it worked flawlessly.
> e.g. This pattern ([0-9][0-9]?[0-9]][.])+ matches one, two or three digits followed by a . and also matches repeated patterns of this. This wold match an IP address (albeit not strictly).
I love regular expressions but one thing I've learned over the years is the syntax is dense enough that even people who are confident enough to start writing regex tutorials often can't write a regex that matches an IP address.