TL;DR

Match in three steps: anchor the position (^ $ \b), choose the character class (\d \w [0-9]), then control quantity (+ {n,m}). Top pitfalls: grep -E has no \d (POSIX ERE — use [0-9] or grep -P); JS lookbehind (?<=...) is only fully cross-platform since Safari 16.4 (March 2023); greedy matching swallows a whole line — use lazy ?.

Basic Syntax

SyntaxMeaningExample
.Any character (usually not newline)a.c matches abc, a1c
\d / \w / \sDigit / word char / whitespacesee engine differences below
[abc] / [^abc]Class / negated class[0-9]{3} = three digits
^ / $Start / end of line^https only matches line start
\bWord boundary\bcat\b does not match category
* / + / ?0+ / 1+ / 0 or 1colou?r matches color/colour
*? / +?Lazy (non-greedy) quantifiers.*? matches as little as possible
{n,m} / {n,} / {n}n to m / at least n / exactly n\d{4}-\d{2}-\d{2}
(...) / (?:...)Capturing / non-capturing groupcapturing groups support backrefs
(?<name>...)Named groupPython/PCRE/JS
`ab`Alternation`(catdog)s?`
(?=...) / (?!...)Positive / negative lookahead\d(?=px) matches the 2 in 2px
(?<=...) / (?<!...)Positive / negative lookbehindsee engine differences

Copy-Paste Validation Patterns

TargetPatternNote
Email (simplified)[\w.+-]+@[\w-]+\.[\w.-]+format check only; use a library in production
URLhttps?://[\w.-]+(/[\w./?%&=+-]*)?includes path & query
Date yyyy-mm-dd\d{4}-\d{2}-\d{2}format check; semantic validation is separate
IPv4 (strict 0-255)`((25[0-5]2[0-4]\d1\d\d[1-9]?\d)\.){3}(25[0-5]2[0-4]\d1\d\d[1-9]?\d)`the simple (\d{1,3}\.){3}\d{1,3} accepts 999
China mobile phone1[3-9]\d{9}11 digits, 2nd digit 3-9
Chinese characters[\u4e00-\u9fa5]CJK Unified Ideographs
Blank line^\s*$bulk cleanup
Hex color#[0-9a-fA-F]{6}\buse {3} for short form
24h time HH:MM`([01]\d2[0-3]):[0-5]\d`23:59 ok, 24:00 not

Engine Differences (where people get it wrong)

FeaturePCRE (grep -P / PHP / most languages)ERE (grep -E / sed -E)JavaScript
\d \w \s shorthandYesNo — use [0-9] etc.Yes
Named groups(?<name>)No(?<name>)
Lookbehind (?<=)YesNoSafari 16.4+ (cross-platform 2023-03)
Lazy quantifier *?YesGNU yes (not POSIX)Yes
Replacement refs$1 or \1\1$1

Command-Line & Code Examples

# Count occurrences of each IPv4 in access.log (grep -E: use [0-9], not \d)
grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log | sort | uniq -c | sort -rn | head

# Count 5xx errors
grep -cE 'HTTP/[0-9.]+" [5][0-9]{2}' app.log

# sed: convert 2026-08-07 to 2026/08/07 (ERE; group refs use \1)
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\1\/\2\/\3/g' data.txt

# Keep non-empty lines
grep -E '.+' file.txt
# Python re: extract all Chinese characters
import re
re.findall(r'[\u4e00-\u9fa5]+', 'hello 世界')   # ['世界']

# Greedy vs lazy when a line has two <a> tags
re.findall(r'<a>(.*?)</a>', '<a>1</a><a>2</a>')  # ['1','2'] (lazy .*?)
re.findall(r'<a>(.*)</a>', '<a>1</a><a>2</a>')   # ['1</a><a>2'] (greedy .*)

# Named groups
m = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})', '2026-08-07')
m.group('year')   # '2026'
// JS lookbehind to extract a price (Safari 16.4+)
"价格 299 元".match(/(?<=价格 )\d+/);   // ['299']

// Compatible with older Safari: capture group instead
"价格 299 元".match(/价格 (\d+)/)[1];   // '299'

Top Pitfalls

  1. \d does not work in grep -E: POSIX ERE has no \d — use [0-9], or switch to grep -P (PCRE).
  2. Greedy matching swallows the line: . matches as much as possible, so <a>(.)</a> runs to the last </a>; use .*?.
  3. Cannot match Chinese: \w does not include Chinese; use [\u4e00-\u9fa5] (Python/JS) or \p{Script=Han} in PCRE with UCP enabled.
  4. Shell escaping: inside double quotes $ is expanded by the shell — write \$; wrapping the whole regex in single quotes is safest.
  5. JS lookbehind errors on old Safari: lookbehind requires Safari 16.4+; rewrite with a capture group (see above).

FAQ

Why doesn't \d match digits in grep?

grep -E (POSIX ERE) has no \d shorthand — use [0-9]. GNU grep only supports \d with grep -P (PCRE). Example: grep -Eo '[0-9]{4}', not grep -Eo '\d{4}'.

How do I extract text between two tags?

Use the lazy .?: <a>(.?)</a>. A greedy .* matches up to the last closing tag and swallows everything in between — especially visible when a line contains multiple tags.

How do I match Chinese characters with regex?

Use [\u4e00-\u9fa5] (CJK Unified Ideographs; supported directly in Python/JS); in PCRE enable UCP and use \p{Script=Han}. Note \w only covers ASCII word characters and does not match Chinese.

Is JS lookbehind supported in all browsers?

Lookbehind (?<=...) has been supported since ES2018, fully cross-platform since Safari 16.4 (about March 2023). For older Safari, rewrite with a capture group: /价格 (\d+)/ instead of /(?<=价格 )\d+/.

Is a regex enough to validate email addresses?

No. Regex only does a loose format check ([\w.+-]+@[\w-]+\.[\w.-]+); it cannot verify that the domain exists or has valid MX records. Use a dedicated library in production (Python email-validator, JS validator.js).

Sources

最后更新:2026-08-07