skip to content

How do ranges [a-z] and negation [^abc] work in a Java character class, and how do you include a literal hyphen or caret?

level: middleimportance: must knowfreq 74%

answer

  1. Hyphen between two chars = range by code point
  2. Leading caret = negate the whole set
  3. Hyphen first/last/escaped = literal dash
  4. Caret is literal unless it is first
  5. [A-z] trap includes [ \ ] ^ _ `; negated class matches newlines

basics

~20 s

[a-z] matches any one character whose code falls between 'a' and 'z'. [^abc] matches any one character that is NOT a, b, or c. Put a hyphen first or last (or escape it) to mean a literal dash.

solid answer

~40 s

A range like [a-z] matches one character whose code point lies between the two endpoints inclusively, ordered by Unicode value, so [0-9] is digits and [A-Za-z] is ASCII letters. A leading caret negates the whole class: [^abc] matches any single character except a, b, or c, including newlines unless you constrain it. The hyphen is only a range operator between two characters; placed first ([-abc]), last ([abc-]), or escaped (\-) it is a literal dash. The caret is only special as the very first character; elsewhere ([a^]) it is a literal caret. Be careful with ranges that span case or punctuation, e.g. [A-z] accidentally includes the six characters between 'Z' and 'a' ([, \, ], ^, _, `).

go deeper

for a junior

Knows [a-z] is a range and [^...] negates; can match digits with [0-9].

for a middle

Correctly places a literal hyphen/caret, knows ranges order by code point, and avoids the [A-z] trap.

for a senior

Explains negation includes line terminators, validates endpoints, and chooses ranges vs predefined classes deliberately.

for a principal

Considers Unicode-correctness of ASCII ranges, input-validation security implications of overly broad negated classes, and readability/maintenance trade-offs.

## Recap: a class matches one character A character class `[...]` matches exactly one input character that is a member of the described set. Ranges and negation are two ways to describe that set more compactly. ## Ranges with the hyphen Writing `[a-z]` means "any one character whose **code point** is between `a` and `z` inclusive". A **code point** is the numeric value Unicode assigns to a character; characters are ordered by that number. So: - `[0-9]` = the ten ASCII digits. - `[a-z]` = lowercase ASCII letters; `[A-Z]` = uppercase. - `[A-Za-z]` = combine two ranges in one class (you can list several ranges and loose characters together, e.g. `[A-Za-z0-9_]`). The hyphen `-` is the **range operator** only when it sits **between two characters**. A reversed range like `[z-a]` is illegal and throws `PatternSyntaxException`. ### Range pitfalls Because ordering is by code point, a range can include surprises. `[A-z]` spans from `A` (65) to `z` (122) and therefore also matches the six punctuation characters between `Z` (90) and `a` (97): `[ \ ] ^ _ \``. Always pick endpoints in the same contiguous block, e.g. `[A-Za-z]` not `[A-z]`. ## Negation with the caret A caret `^` as the **very first** character inside the brackets **negates** the class: `[^abc]` matches any one character that is **not** `a`, `b`, or `c`. Negation also applies to ranges: `[^0-9]` is any non-digit. Important subtlety: a negated class matches **any** other character, **including line terminators** like `\n`, unless you add flags or further restrict the set. (The dot `.`, by contrast, excludes line terminators by default — see the dot question.) ## Including literal hyphen and caret - **Literal hyphen**: place it where it cannot form a range — first `[-abc]`, last `[abc-]`, or escape it `[a\-c]`. Otherwise `[a-c]` is read as a range. - **Literal caret**: it is only special as the first character. Anywhere else it is literal: `[a^b]` matches `a`, `^`, or `b`. To put a caret first as a literal, escape it: `[\^a]`. - **Literal closing bracket**: escape it `[a\]b]` (or place it first in some flavors; in Java escape it). ## Java source escaping Remember regex backslashes double in Java string literals: the regex `[a\-c]` is the Java literal `"[a\\-c]"`. Compile with `Pattern.compile(...)`. ## Deriving the answer Know three rules — (1) hyphen between two chars = range by code point, (2) leading caret = negate, (3) a metachar placed where it can't act becomes literal — and you can explain every example, including the `[A-z]` trap and negation matching newlines.

  • Where can you place a hyphen so it is treated as a literal dash?
    First inside the class ([-abc]), last ([abc-]), or escaped ([a\-c]). In any of those positions it cannot form a range, so it matches a literal hyphen.
  • Does [^abc] match a newline character?
    Yes. A negated character class matches any character not listed, including line terminators like \n. This differs from the dot, which excludes line terminators by default.

saying these in an interview costs you the question

  • Using [A-z] expecting only letters (it spans punctuation)
  • Thinking the caret negates anywhere in the class, not just at the start
  • Assuming a negated class excludes newlines like the dot does
  • Writing a reversed range like [z-a] (throws PatternSyntaxException)

context