Extracting fields¶
One line: Pulling parts out of text is what capturing groups are for, and the three things that go wrong are the same in every engine — a repeated group keeps only its last iteration, an optional group on the branch not taken is unset, and the way you refer to a group differs by language — so the shape that survives is one group per field, named, with the iteration done in the host language.
Level: 201 · you have a line of text and want the pieces
The shape¶
A date, a log line, a key-value pair: the pattern's job is to find the whole thing once and hand back its parts. Two decisions, and both matter more than the pattern:
- Name the groups.
(?<year>\d{4})survives an edit that inserts a group before it;(\d{4})referred to as group 3 does not. The spelling is(?<name>…)in JavaScript, Ruby, Java, Go and Rust,(?P<name>…)in Python — and Perl, PCRE2, Go and Rust take both. See named group. - Match one record, and loop in the language.
findall/finditer/scan/FindAllStringSubmatch/captures_iter— not a quantifier around the group, which keeps only the last hit.
Measured¶
Verified output of topic_extracting_fields_sh.sh — regenerated by tools/run_examples.py, never hand-typed.
construct python perl node ruby java go rust fancy
whole match 2026-09-22 2026-09-22 2026-09-22 2026-09-22 2026-09-22 2026-09-22 2026-09-22 2026-09-22
year via $1 $1 2026 2026 $1 2026 2026 2026 2026
year via \1 2026 \u{1} \\1 2026 1 \\1 \\1 \\1
named, (?<y>…) and ${y} - n/a ${d}/${m}/${y} ${d}/${m}/${y} 22/09/2026 22/09/2026 22/09/2026 22/09/2026
named, (?P<y>…) and \g<y> 22/09/2026 g<d>/g<m>/g<y> - - - \\g<d>/\\g<m>/\\g<y> \\g<d>/\\g<m>/\\g<y> \\g<d>/\\g<m>/\\g<y>
last iteration of a group [$1] [3,] [3,] [$1] [3,] [3,] [3,] [3,]
optional field, unset int=$1 frac=[$2] int=42 frac=[] int=42 frac=[] int=$1 frac=[$2] int=42 frac=[] int=42 frac=[] int=42 frac=[] int=42 frac=[]
Reading the table¶
- Row 1. The whole match is the same in all eight. The disagreements are all about getting at the parts.
- Rows 2–3,
$1against\1. Asking for the year through a replacement shows the replacement spelling war:$1is text in Python and Ruby;\1is a control character in Perl and the digit1in Java. In code you would callm.group(1)/$1/m[1]/m.group(1)/m[1]/m[1]/caps[1]— the API names differ less than the template does. - Rows 4–5, named.
${d}/${m}/${y}rearranges the date in Java, Go and Rust;\g<d>/\g<m>/\g<y>does it in Python. JavaScript's spelling is$<d>, Ruby's\k<d>, Perl's$+{d}. Python's-on row 4 is(?<y>being refused, not the extraction. - Row 6, the last iteration.
(\d+,)+over1,2,3,and then$1:3,everywhere the reference works. No engine keeps the earlier iterations in group 1. This is the row that decides the shape above — the loop belongs outside the pattern. - Row 7, the optional field.
(\d+)(?:\.(\d+))?on42: group 2 is unset, and every engine inserts nothing for it in a replacement —frac=[]. In code, unset isNonein Python,undefinedin JavaScript,nilin Ruby,nullin Java, an empty string in Go,Nonein Rust — six languages, five different values for "this group did not match", and the empty-string one (Go) cannot tell "matched nothing" from "did not match".
How the loop looks, per language¶
| Language | One match with groups | Every match |
|---|---|---|
| Python | m = re.search(p, s); m.group("y") / m["y"] |
for m in re.finditer(p, s) |
| Perl | if ($s =~ /$p/) { $+{y} } |
while ($s =~ /$p/g) { … } |
| JavaScript | const m = s.match(re); m.groups.y |
for (const m of s.matchAll(re)) — needs the g flag |
| Ruby | m = s.match(re); m[:y] |
s.scan(re) — returns the groups, not the matches, when there are groups |
| Java | Matcher m = p.matcher(s); m.find(); m.group("y") |
while (m.find()) |
| Go | m := re.FindStringSubmatch(s); m[re.SubexpIndex("y")] |
re.FindAllStringSubmatch(s, -1) |
| Rust | let c = re.captures(s)?; &c["y"] |
re.captures_iter(s) |
Two of those have a trap of their own: JavaScript's matchAll throws without the g flag, and Ruby's scan changes what it returns depending on whether the pattern has groups.
Keywords on this page¶
Capturing group · Named group · Non-capturing group · Replacement references · \G — for a tokenizer, where the next field must start where the last one ended.
See also¶
- What a backreference is — capturing in order to match again, which is a different job.
- Search and replace · Splitting text — the two neighbouring jobs.
- Common patterns and their traps.