Validating input¶
One line: A validator is a pattern that must match all of the input and only the input you intend, and the two places it fails are the anchors — ^…$ is a line test in disguise — and the shorthands — \d and (?i)[a-z] accept characters that are not the ones you pictured; the fix is \A…\z (or the language's whole-string call), explicit classes, and code for anything the pattern cannot see.
Level: 201 · you are checking a field before you trust it
The shape of a validator¶
Both anchors, the strict ones; a grammar that names every character it allows; and nothing optional that you did not mean to be optional. Then, in the code: the checks a regex cannot make — ranges, dates, existence. Common patterns and their traps has the six patterns that skip that last step.
Measured¶
Verified output of topic_validating_input_sh.sh — regenerated by tools/run_examples.py, never hand-typed.
construct python perl node ruby java go rust fancy
^\d+$ on a plain number 42 42 42 42 42 42 42 42
^\d+$ with a trailing \n 42 42 no 42 42 no no no
^\d+$ with a second line no no no 42 no no no no
\A\d+\z instead - no no no no no no no
\A\d+\Z, Python's spelling no 42 no 42 42 - - 42
\d+ with no anchors 42 42 42 42 42 42 42 42
\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}
[0-9]+ on the same no no no no no no no no
(?i)[a-z]+ on KELVIN SIGN \u{212A} \u{212A} - \u{212A} no \u{212A} \u{212A} \u{212A}
[a-z]+ on KELVIN, node i no no no no no no no no
Reading the table¶
- Row 1 is the happy path and it is unanimous, which is why validators pass their tests.
- Row 2, a trailing newline.
^\d+$accepts42\nin Python, Perl, Ruby and Java. JavaScript, Go and both Rust crates reject it. A form field, a line read from a file, a value pasted with its newline — half of these engines say "a number". - Row 3, a second line.
42\nrm -rf /gets through in Ruby only — its^and$are line anchors with no flag. The lesson runs the payload in every engine and explains why the other four stop at exactly one newline. - Rows 4–5, the strict anchors.
\A\d+\zrejects42\neverywhere it compiles — and Python 3.12 does not compile\z(3.14 does). Python's spelling is\Z, row 5, which rejects the newline there; the same\Zin Perl, Ruby, Java andfancy-regexaccepts it, because their\Zis the newline-tolerant one. Go and Rust refuse\Z. Two escapes, and no engine agrees with its neighbour about both. - Row 6, no anchors.
\d+finds42insideabc 42 def. An unanchored validator validates that a number appears somewhere. - Rows 7–8,
\d.^\d+$accepts Arabic-Indic digits in Python, Perl and Rust and rejects them in JavaScript, Ruby, Java and Go.^[0-9]+$rejects them in all eight. If "digit" means the ten ASCII digits, say so. - Rows 9–10,
(?i)[a-z]. With case-insensitivity on,[a-z]accepts U+212A KELVIN SIGN in Python, Perl, Ruby, Go and both Rust crates, because it folds tok. Java says no (ASCII folding withoutUNICODE_CASE); JavaScript with theiflag says no. A username validator with(?i)lets a KELVIN SIGN register ask, and a uniqueness check that folds the other way will not find the collision. Case-insensitive matching has the rest.
The whole-string call, per language¶
Every language has a call that means "the whole string, no anchors needed", and it sidesteps rows 2–5 entirely:
| Language | Whole-string match |
|---|---|
| Python | re.fullmatch(p, s) |
| Perl | \A…\z in the pattern; there is no call |
| JavaScript | ^…$ without m — the one engine where the plain anchors are strict |
| Ruby | \A…\z in the pattern; match? does not anchor |
| Java | String.matches(p) / Matcher.matches() |
| Go | \A…\z in the pattern; MatchString does not anchor |
| Rust | \A…\z, or is_match on an anchored pattern |
The rules¶
\A…\z, or the whole-string call. Never^…$in a validator, unless the engine is JavaScript andmis off.- Classes, not shorthands, when the set of allowed characters is the point:
[0-9],[A-Za-z], or a\p{…}property when the set is genuinely Unicode. - No
(?i)on a security decision. Fold in the host language with a documented function and match the folded form. - Reject, then explain. A validator's error message can say what was wrong only if the code, not the pattern, made the decision — which is the argument for pattern-finds-shape, code-checks-value.
Keywords on this page¶
^ · $ · \A \z \Z · Shorthand classes · Character class · Inline flags.
See also¶
$is not the end of the string · What\wand\bmatch · Case-insensitive matching — the three lessons this page compresses.- Security · Testing patterns · Common patterns and their traps.