Skip to content

(?#…) and # — comments inside a pattern

One line: (?#text) is a comment group, ignored by the engine, in Python, Perl, PCRE2, Ruby and fancy-regex; under free-spacing mode (?x), everything from # to the end of the line is a comment in Python, Perl, Ruby, Java and Rust; and JavaScript, Java and Go have no (?#…) at all.

Level: reference · one construct, eight engines

What it means

Two mechanisms, and the second is the useful one:

  • (?#…) — an inline comment anywhere, no flag needed, cannot contain ).
  • # under (?x) — a line comment, which is how a forty-character pattern becomes a readable block:
(?x)
^ (?<year>  \d{4} ) -   # ISO date
  (?<month> \d{2} ) -
  (?<day>   \d{2} ) $

The x mode also ignores whitespace, so a literal space has to be \ or [ ].

Measured

Verified output of kw_comment_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

construct          python  perl  node  ruby  java  go   rust  fancy
(?#...)            ab      ab    -     ab    -     -    -     ab
hash under (?x)    a       a     -     a     a     -    a     a
hash without (?x)  a#b     a#b   a#b   a#b   a#b   a#b  a#b   a#b

Reading the table

  • Row 1, a(?#note)b. Python, Perl, Ruby and fancy-regex skip the comment and match ab. JavaScript, Java, Go and Rust's regex refuse it.
  • Row 2, (?x)a#comment. Under free-spacing, #comment is ignored to the end of the row, so the pattern is a and the match is a — in Python, Perl, Ruby, Java and Rust. JavaScript has no x; Go has no x.
  • Row 3, a#b with no flag. A # is an ordinary character everywhere: all eight match a#b. Turning on (?x) later would turn that # into a comment and silently truncate the pattern — the one way this feature bites.

The trap

Adding (?x) to an existing pattern. Every space in it disappears and every # starts a comment, so [a-z ]+#tag becomes [a-z]+. The mode is worth using from the start of a long pattern; it is dangerous to add afterwards.

Where it is measured in depth

See also