Regex tester
Runs in your browser. No upload, no account.
Runs in your browser0 bytes uploadedMatches will appear here, with the exact position of each one and every capture group listed by number and by name.
Regular expressions look like one language. They are not. What a pattern means depends on the engine reading it, and the differences between those engines are not exotic corner cases — they sit in the first line of an enormous number of patterns published online. The overwhelming majority of regex examples on the internet were written for PCRE, Python or Java. If your code runs in a browser or in Node, that is a different engine with different rules.
The failure this causes is quiet, which is what makes it expensive. Paste a pattern written for another engine into JavaScript and it very often compiles without complaint. No error, no warning, a result on screen. It simply matches something other than what the author meant. This page hands your pattern to the same engine your program will use, and then says out loud when a piece of it means one thing here and something else where it was probably copied from.
The clearest example is anchoring. In PCRE, Python, Java and .NET, \A means "the very beginning of the subject" and \z means "the very end". They are the strict siblings of ^ and $, and they are extremely common in validation patterns, because they keep matching honest when the input contains line breaks.
JavaScript has neither. It also does not reject them. The specification says that a backslash followed by a character with no defined meaning is an identity escape — the backslash is discarded and the character stands for itself. So \A is the letter A. \z is the letter z. A pattern written as\A\d{3}\z does not mean "exactly three digits". It means "the letter A, three digits, the letter z", and it will happily report a match onA300z while ignoring the numbers you actually wanted.
That is not a rare accident. The same rule swallows \Z, \h and\H for horizontal whitespace, \R for any line break,\K for resetting the match start, \e for the escape character, and the \Q…\E literal-quoting pair. Every one of them compiles. Every one of them silently becomes a letter. The panel above names each occurrence, says what it does here, says what it does in the dialects where it is an operator, and offers the construction that expresses the same intent in JavaScript.
\p{L} is the standard way to say "any letter, in any script". It works in JavaScript — but only when the u flag is on. Without it,\p is another identity escape, and the whole thing collapses into five literal characters: p, an opening brace, L, a closing brace. A pattern meant to accept every alphabet on earth ends up matching the exact text p{L} and nothing else.
The u flag cuts the other way too, which is why this tool tests your specific pattern with the flag and without it rather than consulting a list. Unicode mode outlaws escapes that have no meaning, so patterns that are perfectly legal today stop compiling entirely when it is switched on. \- outside a character class, a lone {}, a range like [a-\w] — all fine without the flag, all hard syntax errors with it. If that applies to what you have typed, it is reported as it happens, with the engine's own wording.
There is a third category with no error at all: the flag changes results. Withoutu, a single dot matches one UTF-16 code unit, so an emoji outside the basic plane counts as two characters and ^.$ fails against it. Withu, the same pattern succeeds. Nothing warns you, because nothing is wrong — the pattern simply means something different.
Some borrowed constructions do not compile, and there the difficulty is the message. Atomic groups (?>…), possessive quantifiers like a++, recursion, conditionals, inline flag switches such as (?i), inline comments and the Python spelling of named groups (?P<name>…) all produce a terse complaint about an invalid group. That wording is perfectly clear if you already know what went wrong and completely opaque otherwise. The diagnosis above the raw message names the construct, says which engines have it, and where a JavaScript equivalent exists, gives it.
Every match is listed with its start and end index into the string, not just its text, because a pattern that matches the right characters in the wrong place is still wrong. Capture groups are listed by number, with their name when they have one, and with their own positions.
A group that did not take part in the match is reported as such rather than as an empty string. Those are genuinely different outcomes — (a)|(b) againstb leaves the first group undefined, not blank — and code that treats them alike is a common source of bugs that only appear on the branch nobody tested.
Flags are yours to set, and nothing is added behind your back. Without gthe engine stops after the first match, and so does this page, because showing you every match while your code will only ever see one would be a comfortable lie. If you want to see what replace() produces, the replacement field runs the real thing, so$1, $<name> and $& behave exactly as they will in your source.
Catastrophic backtracking is the one way a regular expression does real damage. A shape like (a+)+$ makes the engine try an exponentially growing number of ways to divide the input, and the time doubles with every character added. Measured here while building this page: twenty-seven characters took about two seconds, and twenty-nine took close to nine.
Note the sizes. This is why matching runs on a separate thread rather than switching to one above some threshold of input length. For most work, the amount of text predicts the amount of effort; for regular expressions it predicts nothing at all, and a rule based on size would wave through exactly the case that causes harm. Every run here happens off the main thread with a one-second budget, and a run that exceeds it has its thread stopped so the page stays usable.
You will be told that the run did not finish. You will not be told that your pattern is "safe", now or ever. Proving that in general is genuinely hard, and a green badge on something that later takes down a request handler is worse than saying nothing. What gets reported is what happened, on this input, on this machine.
One caution that applies to every tool with a text box, this one included. A<textarea> rewrites carriage-return-and-line-feed pairs into bare line feeds before your script ever sees the value. That is required behavior in the HTML specification, and it cannot be worked around.
So if your pattern is about line endings — matching \r\n, counting them, stripping them — pasting the sample will quietly give you the wrong answer, because the carriage returns were removed on the way in. Open the file instead. The file path reads bytes and leaves them alone. If you are chasing characters that look like nothing at all, the invisible character inspector names them individually, and the text comparison tool reports line endings as a document-level fact.
It does not generate patterns from a description. That would mean sending your requirement to a model somewhere else, and a generated expression that looks correct without being correct is precisely the failure this page exists to catch.
It does not offer other dialects. A tester covering six engines is useful for translation work and useless for the question most people actually have, which is whether this pattern will behave in the place they are about to paste it. Doing one engine properly, and being explicit about where the others differ, answers that better.
It has no save button and no shareable link, and that is deliberate rather than unfinished. Test strings are rarely toy data — they are log lines, exported customer records, addresses pulled from a real table. A link that restores your session has to carry that content somewhere, which in practice means your browser history, your clipboard, and whatever channel you paste it into. Nothing typed here is written to the address bar or to browser storage. Matching is a call into the regular expression engine already sitting in your browser, which has nowhere to send anything even in principle. Should you need to pull structured values out of a document rather than test a pattern against it, the email extractor andthe JSON formatter do that directly.
JavaScript, and not a reimplementation of it — the pattern is handed to the same RegExp engine your page or your Node process will use. That matters because most patterns published online are written for PCRE, Python or Java, and several of their constructs compile in JavaScript while meaning something else entirely.
Because JavaScript has no \A or \z. In PCRE, Python and Java they anchor to the start and end of the string. In JavaScript an unknown escape is an identity escape, so \A is simply the letter A and \z is the letter z. The pattern compiles, reports a match, and matches text you never intended. Use ^ and $ instead.
Unicode property escapes only exist when the u flag is on. Without it, \p is an identity escape and \p{L} is the five literal characters p, {, L and }. Turning on u makes it a property class — and can also turn a previously valid pattern into a syntax error, which is flagged here as it happens.
A badly shaped one can take a very long time on a short string — a nested quantifier against 29 characters was measured at nearly nine seconds here. Matching therefore runs on a separate thread with a one-second budget; past that the thread is stopped and you get told the run did not finish. The tool never labels a pattern safe, because a false reassurance would be worse than none.
No. Matching is a call into the regular expression engine already built into your browser, and there is nowhere for it to send anything. Nothing is written to the address bar or to browser storage either, which is deliberate: test strings are frequently real log lines and real customer records, and a shareable link would put them in your history and in whatever you pasted it into.