Regular Expression (RegEx) Syntax Reference Markdown Table Template
A regex reference table earns its place in a README or wiki when it documents the patterns a team actually relies on: what each token means, which input it accepts, and which near-miss it rejects. Developers consult it while writing validation code, reviewers use it to check that a pattern in a pull request does what its comment claims, and support or QA staff read it to understand why an input was refused.
Edit This Template
Edit cells, add rows, or sort — the Markdown updates livePaste cells from Excel or Google Sheets into the first header to replace the table · Tab / Enter / arrows move · Shift+F10 opens row & column actions
When to Use This Table
The trap is that “regex” is not one language. JavaScript, Python’s re, PCRE2 (PHP, and grep -P in GNU grep), Java, .NET, and Go’s RE2-based regexp agree on the basics and diverge on details that matter: whether \d matches Arabic-Indic digits, whether lookbehind exists, whether $ matches before a trailing newline. A cheat table that does not name its flavor is quietly wrong for some of its readers, so state the engine and flags at the top.
A good table pairs every token with a tested example that shows the exact substring matched and a counter-example chosen to fail for the reason the token exists. A poor one shows \d+ matching 2025 and calls it done, never mentions anchors or flags, and breaks in the renderer because an alternation pipe split the row in two.
Formatting This Table in Markdown
A regular expression (regex) syntax reference table puts a few things in cells that Markdown treats as syntax. These are the ones to watch so it renders correctly on GitHub, GitLab, Obsidian and docs sites.
Put every token in a code span, even simple ones
Outside backticks, Markdown consumes regex characters: a bare
a\.bloses its backslash, a barex*y*zitalicizes the y, a bare[a-z](foo)becomes a link, and a bare<div>is read as an HTML tag and can disappear. Inside a code span all of these stay literal.Escape alternation pipes, or move the pattern out
Cells are split before code spans are parsed, so alternation is written
(jpg\|png)in the source; see escaping pipes. A regex that matches a literal pipe is\|, and on cmark-gfm (the parser behind GitHub’s renderer) its table source is\\|; other renderers may differ, so show patterns with several pipes in a fenced block under the table.Sample inputs turn into links outside code spans
A table about matching emails and URLs often lists
user@example.comorwww.example.comas input; left bare, GFM autolinks both, so the cell shows a link instead of the string. Wrap each input in a code span and quote it so spaces and empty strings stay visible; on GitHub, two bare$anchors in one cell may also pair up as math delimiters.
What Goes in Each Column
| Column | Alignment | What to put here |
|---|---|---|
| RegEx Token | left :--- | The token as typed inside a pattern, in backticks, without /…/ delimiters or string-literal doubling: \d, not "\\d". Group complementary tokens in one row (\d and \D). Any alternation bar must be written as \|, even inside the code span, or the row splits. |
| Pattern Category | center :---: | One label from a short, fixed list (Anchor, Character class, Quantifier, Group, Lookaround, Flag), centered and always singular. Consistent labels let readers sort the table and stop it collecting near-duplicates such as “Anchors” and “Anchor”. |
| Matching Behavior | left :--- | What the token matches and what changes that behaviour: the m flag for ^ and $, the s flag for ., Unicode mode for \w and \d. When flavors differ, say which one the sentence describes rather than giving the ASCII definition as universal. |
| Example Match | left :--- | Pattern, input, and the exact substring matched: \d+ on "Order 2025" matches 2025. Showing the substring reveals whether the token matched the whole input or only part of it, which is the distinction most validation bugs come from. |
| Non-Matching Example | left :--- | A near miss that fails for the reason this token exists, with that reason in parentheses: "12a4" against ^\d{4}$ fails at a. Unrelated strings such as "abc" against a digit pattern teach nothing that the behaviour column does not already say. |
How to Fill It In
- 1Write the target flavor and default flags above the table, for example “JavaScript (ECMAScript 2018+), no flags unless shown”. If the team uses two engines, such as a JavaScript front end and a Python back end, add a flavor-difference table rather than averaging them.
- 2Collect the tokens from patterns in use: search the codebase for
new RegExp(, regex literals,re.compile(, and validation schemas. Document those first; a token nobody uses can wait. - 3Test every example in the real engine. Use regex101 with the matching flavor selected, or one-liners such as
node -eandpython3 -c, and confirm both the match and the non-match. Copy the matched substring from the tool output. - 4Escape for Markdown last: replace every
|in a cell with\|(see escaping pipes), keep single backslashes, and preview the rendered table on the platform where it will live. - 5Move the examples into unit tests next to the code that uses the patterns, so a change in a validator fails CI instead of silently contradicting the documentation.
Ready-to-Paste Variations
Flavor differences that bite
For teams running the same pattern in more than one engine. Check your exact runtime version before relying on a newer feature.
| Feature | JavaScript | Python `re` | PCRE2 | Go `regexp` |
| :--- | :---: | :---: | :---: | :---: |
| Lookahead `(?=…)` `(?!…)` | Yes | Yes | Yes | No |
| Lookbehind `(?<=…)` `(?<!…)` | Yes (ES2018) | Fixed width only | Yes, bounded length | No |
| `\d` matches non-ASCII digits | No | Yes, for `str` patterns | Only with UCP | No |
| POSIX class `[[:alpha:]]` | No | No | Yes | Yes |
| Named group | `(?<name>…)` | `(?P<name>…)` | Both forms | `(?P<name>…)` |
| Backreference `\1` | Yes | Yes | Yes | No |
| Atomic group `(?>…)` | No | Yes (3.11+) | Yes | No |Lookaround assertions
All four zero-width assertions, each with an input and the exact text matched. The assertion checks its context without including it in the match.
| Assertion | Syntax | Pattern | Input | Matched text |
| :--- | :---: | :--- | :--- | :--- |
| Positive lookahead | `(?=…)` | `\w+(?=@)` | `user@site` | `user` |
| Negative lookahead | `(?!…)` | `q(?!u)` | `Iraq` | `q` (no match in `queen`) |
| Positive lookbehind | `(?<=…)` | `(?<=#)\d+` | `issue #42` | `42` |
| Negative lookbehind | `(?<!…)` | `(?<!-)\b\d+` | `-5 and 7` | `7` |Alternation with escaped pipes
Every | in these cells is written as \| in the source and renders as a plain bar. Note the anchor row: alternation has the lowest precedence of any operator.
| Pattern | Meaning | Matches | Does not match |
| :--- | :--- | :--- | :--- |
| `cat\|dog` | Either word, anywhere | `hotdog` (finds `dog`) | `cow` |
| `^(cat\|dog)$` | Exactly one of the words | `cat` | `cats` |
| `^cat\|dog$` | Starts with `cat`, or ends with `dog` | `catalog` | `hotdogs` |
| `\.(?:jpe?g\|png)$` | Extension, without capturing | `photo.jpeg` | `photo.gif` |
| `\x7C` | A literal pipe, no bar in the pattern | `a\|b` | `a/b` |Common Mistakes
Leaving
|unescaped in an alternation such as(jpg|png).GFM splits cells on every unescaped pipe, including inside backticks, so the row gains a column and the pattern is cut in half. Write
(jpg\|png); GitHub removes the backslash when rendering the code span.Documenting
\das[0-9]and\was[A-Za-z0-9_]for every language.That holds in JavaScript and Go, but Python 3
strpatterns match any Unicode digit and word character unless you passre.ASCII. If only ASCII digits are valid, write[0-9]explicitly and say why.Validating whole inputs with
^…$in Python.Python’s
$also matches just before a trailing newline, sore.match(r"^\d+$", "123\n")succeeds. Usere.fullmatch()or end the pattern with\Z.Assuming
.matches everything.By default
.does not match line terminators. Enable thesflag (dotAll in JavaScript since ES2018,re.DOTALLin Python) or use[\s\S]when a pattern must cross lines, and note the flag in the behaviour column.Greedy quantifiers in examples that only work on tidy input.
<.+>on<b>bold</b>matches the whole string, not the first tag. Show the lazy form<.+?>or a negated class<[^>]+>, and pick example inputs that contain more than one candidate match.
Related Markdown Table Templates
Browse all 50 templatesMarkdown Table Delimiter Mechanics
Visual reference detailing delimiter row colon placement, column text alignment rules, minimum hyphens, and behavioral differences.
Application Keyboard Shortcuts
Quick reference cheat sheet for application hotkeys, key combinations, and the scope each works in, shown with this site's grid editor shortcuts.
Status Indicators & Markdown Badges
Cheat sheet of copy-pasteable Unicode symbols, emojis, and shields.io badge syntax for table cells.
Regular Expression (RegEx) Syntax Reference: Frequently Asked Questions
Does JavaScript support lookbehind?
Yes, since ES2018, with no fixed-width restriction, and all current major browsers support it (Safari since 16.4). In an engine without support, a regex literal using it is a syntax error that stops the whole script from parsing, so check your oldest supported browser.
Why does \w not match accented letters in JavaScript?
In JavaScript, \w is ASCII-only even with the u flag. To match letters in any script, use a Unicode property escape with the u flag, such as /[\p{L}\p{N}_]+/u, which matches é, ß, and letters from non-Latin alphabets.
What is the difference between * and *?
Both repeat zero or more times. * is greedy: it takes as much as possible and then backtracks. *? is lazy: it takes as little as possible and extends only when the rest of the pattern fails. The same applies to +?, ??, and {n,m}?.
Which flavor does grep use?
Plain grep uses POSIX basic syntax, grep -E uses extended syntax, and GNU grep -P uses PCRE. POSIX syntax has no \d, so write [0-9] or [[:digit:]] in shell scripts that must run with any grep.