Skip to content

\d, \w, \s — and \h, \v, \R, \N, which only some engines have

One line: \d, \w and \s exist everywhere and mean ASCII in four of these eight engines and Unicode in the other four; \h (horizontal space), \R (any line break) and \N (not a newline) are Perl/PCRE2 additions that Java and Ruby partly adopted, and that JavaScript accepts as the letters h, R and N.

Level: reference · one construct, eight engines

What it means

Shorthand Expands to ASCII by default in Unicode by default in
\d decimal digits JavaScript, Ruby, Java, Go, PCRE2 Python str, Perl, Rust regex
\w letters, digits, _ (and marks, in some) JavaScript, Ruby, Java, Go, PCRE2 Python str, Perl, Rust regex
\s whitespace Ruby, Java, Go, PCRE2 Python str, Perl, Rust regex, JavaScript
\h horizontal whitespace (space, tab, NBSP, …) — Perl, Ruby, Java, PCRE2 only
\v vertical whitespace (\n, \r, \f, …) — Perl, Java, PCRE2 only — a vertical tab character in Python, JavaScript, Ruby
\R any line break: \n, \r, \r\n, … as one unit — Perl, Ruby, Java, PCRE2 only
\N any character except newline, regardless of (?s) — Perl, PCRE2 only

\D, \W, \S, \H, \V are the complements, wherever the positive form exists.

Measured

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

construct                  python          perl            node    ruby    java    go   rust            fancy
\d on Arabic-Indic digits  \u{660}\u{661}  \u{660}\u{661}  no      no      no      no   \u{660}\u{661}  \u{660}\u{661}
\w on Latin-1              \u{E9}t\u{E9}   \u{E9}t\u{E9}   t       t       t       t    \u{E9}t\u{E9}   \u{E9}t\u{E9}
\w on Greek                \u{3B1}\u{3B2}  \u{3B1}\u{3B2}  no      no      no      no   \u{3B1}\u{3B2}  \u{3B1}\u{3B2}
\s on no-break space       \u{A0}          \u{A0}          \u{A0}  no      no      no   \u{A0}          \u{A0}
\h horizontal space        -               a\tb            no      no      a\tb    -    -               no
\v vertical space          no              a\nb            no      no      a\nb    no   no              no
\R any newline             -               a\r\nb          no      a\r\nb  a\r\nb  -    -               -
\N not a newline           -               axb             no      no      -       -    -               -
\D \W \S negations         a b             a b             a b     a b     a b     a b  a b             a b

Reading the table

  • Row 1, \d on U+0660 U+0661. Python, Perl and Rust match the Arabic-Indic digits; JavaScript, Ruby, Java and Go do not. An input field validated with \d+ in the first group accepts a "number" that Integer.parseInt in the second group would reject — and that int() in Python happily parses, which the lesson measures the other way round.
  • Rows 2–3, \w. The same split. Note the shape of the ASCII answer on été: t — the match starts at the first ASCII letter and stops at the next accent, so a \w+ "word" extractor returns fragments rather than nothing, which is harder to notice.
  • Row 4, \s on a no-break space. JavaScript moves columns: its \s has always been Unicode-aware (the ECMAScript definition includes every White_Space character) while its \w is ASCII. Ruby, Java and Go say no.
  • Row 5, \h. Perl and Java match the tab. Python, Go and Rust's regex refuse the escape; JavaScript, Ruby and fancy-regex say no — for JavaScript that is \h being the letter h, for Ruby it is a genuine miss on \t, because Onigmo's \h is a hex digit, not horizontal space. Same escape, three meanings and one refusal.
  • Row 6, \v. Perl and Java match the \n as vertical whitespace. Python, JavaScript, Ruby, Go and both Rust crates compiled the pattern and matched nothing: their \v is the single character U+000B, vertical tab. A pattern that used \v to mean "any line break" ports from Perl to Python and quietly matches only vertical tabs.
  • Row 7, \R. Perl, Ruby and Java match \r\n as one unit — the point of \R is that it never splits a CRLF. Python, Go and both Rust crates refuse. JavaScript reads \R as the letter R and finds none. Row 8, \N: Perl alone; Java refuses it.

The trap

Not \d being ASCII — that one is at least documented. The trap is the row of no cells that are not refusals: \v in Python, \h in Ruby, and every \h, \v, \R, \N in JavaScript compile fine and mean something else. A refused escape is a bug you find at the first test; a redefined one is a bug you find in production.

Where it is measured in depth

See also