Skip to content

[[:alpha:]] — POSIX bracket classes, which four engines have and four read as garbage

One line: [[:digit:]], [[:alpha:]], [[:space:]] and the rest are the POSIX spelling of character categories; Perl, Ruby, Go and Rust accept them (Perl and Ruby with Unicode meaning, Go and Rust ASCII-only), and Python, JavaScript and Java compile the same text as a class containing the characters [, :, d, i, g, t — with no error.

Level: reference · one construct, eight engines

What it means

The syntax comes from POSIX regex(7), which is what grep, sed, awk and C's <regex.h> implement. The double brackets are not a typo: [:digit:] is the class name, and it is only valid inside an outer [...], so it can be combined — [[:alpha:]_] is letters or underscore.

Engine [[:name:]] Meaning
Perl, PCRE2 (so ABAP) yes Unicode-aware on decoded text; [[:^name:]] negates
Ruby yes Unicode-aware
Go regexp, Rust regex yes ASCII only — RE2's documented rule
POSIX grep/sed/awk, C yes locale-dependent
Python re, JavaScript, Java no — silently a class of six characters —

Java's alternative is \p{Alpha} and friends, which are ASCII by default; Python and JavaScript have no equivalent and use \p{…} (JavaScript with u) or \w-style shorthands.

Measured

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

construct              python  perl      node  ruby      java  go  rust      fancy
[[:digit:]]            no      12        no    12        no    12  12        12
[[:alpha:]] on Greek   no      \u{3B1}b  no    \u{3B1}b  no    b   b         b
[[:word:]] on Latin-1  no      \u{E9}a   no    \u{E9}a   no    a   a         a
[[:^digit:]] negation  no      ab        no    ab        no    ab  ab        ab
\p{Alpha} on Greek     -       \u{3B1}b  no    \u{3B1}b  b     -   \u{3B1}b  \u{3B1}b
outside brackets       :       :         :     :         :     :   :         :

Reading the table

  • Row 1, [[:digit:]]+ on a12. Perl, Ruby, Go, Rust and fancy-regex return 12. Python, JavaScript and Java return no — not a refusal. They parsed [[:digit:]] as the class [[:digt] followed by a literal ], and a12 contains none of those characters. Python prints a FutureWarning about a "possible nested set" on stderr; JavaScript and Java say nothing at all.
  • Rows 2–3, on αb and éa. Perl and Ruby take the whole word — POSIX classes there are Unicode-aware. Go and Rust return b and a: the classes are ASCII by definition in RE2 syntax, and the match starts at the first ASCII letter. This is the row that makes "does it support POSIX classes" two questions rather than one.
  • Row 4, [[:^digit:]]. The negated form, a Perl invention that Ruby, Go and Rust also accept.
  • Row 5, \p{Alpha}. The Java spelling — and in Java it means ASCII (b), while in Perl, Ruby and Rust it means Unicode (αb). Python refuses \p entirely; Go refuses this name (it knows categories and scripts, not POSIX names); JavaScript without u reads it as the letters p{Alpha}.
  • Row 6, [:digit:]+ with one pair of brackets. All eight matched the colon, because outside an enclosing class the text is an ordinary set of the characters :, d, i, g and t. Perl warns about it. The others do not.

The trap

A pattern copied from a shell script into Python. grep -E '[[:space:]]+' is correct and idiomatic; the same string handed to re.compile is a class of five characters, and a test that happens to contain a colon or a t will pass. Translate to \s+ — and then check which \s you now have, per shorthand classes.

Where it is measured in depth

See also