How do inline flag groups like (?i:...) work in Java regex, and how do they differ from a leading (?i)? How do you turn a flag off for part of a pattern?
answer
- (?i) = switch on to end of group; (?i:...) = scoped group
- Scoped group is non-capturing (like (?:...))
- (?-i:...) turns the flag OFF in that region
- Mix on/off: (?i-x:...)
- (?i) can't reach text before it
basics
~20 sA leading (?i) turns a flag on for the rest of the pattern. The form (?i:...) is a non-capturing group that applies the flag only inside that group. You turn a flag off for a region with (?-i:...).
solid answer
~50 sInline flags come in two shapes. (?i) is a 'flag-setting' token: it switches the flag on from that point to the end of the current group (or pattern), with no parentheses of its own. (?i:...) is a 'scoped non-capturing group': the flag applies only to the sub-pattern inside the group and reverts afterwards, and the group does not capture text. You can list several letters together, e.g. (?ims:...). To disable a flag in a region you put a minus before the letters: (?-i:...) turns case-insensitivity off inside that group, and you can mix on/off as (?i-x:...) (turn i on, x off). Because (?i) only affects what follows, it cannot retroactively change earlier parts of the pattern. Scoped forms are the cleaner choice when only one segment needs the flag — for example matching a case-insensitive keyword followed by a case-sensitive identifier: (?i:select)\s+(?-i:[A-Z_]+).
code
java · 16 linesimport java.util.regex.*;
// Case-insensitive keyword, case-sensitive identifier
Pattern p = Pattern.compile("(?i:select)\\s+(?-i:[A-Za-z_]+)");
System.out.println(p.matcher("SELECT name").find()); // true (keyword folded)
System.out.println(p.matcher("select Name").find()); // true
// (?i) only affects what follows
Matcher m = Pattern.compile("a(?i)bc").matcher("aBC");
System.out.println(m.matches()); // true: a literal, bc folded
System.out.println(Pattern.compile("a(?i)bc").matcher("ABC").matches()); // false: 'A' not folded
// Scoped group is non-capturing; outer parens capture
Matcher c = Pattern.compile("((?i:foo))").matcher("FOO");
c.matches();
System.out.println(c.group(1)); // FOO (one capturing group)go deeper
Recognizes (?i) at the start of a pattern; may not know the scoped or off forms.
Uses (?i:...) to scope a flag and knows it is non-capturing; aware (?i) runs to end of group.
Fluently mixes on/off with (?-i:...) and (?i-x:...), explains scope-to-end-of-group and that compile flags are whole-pattern equivalents.
Designs maintainable patterns choosing scoped flags vs splitting into multiple compiled patterns, weighs readability, and reasons about flag interactions in generated or composed patterns.
## The two inline shapes Java (via `java.util.regex`) supports embedding flags directly in the pattern text in two distinct syntaxes: 1. **Flag-setting token `(?flags)`** — e.g. `(?i)`, `(?ms)`. This has *no* colon and *no* body. It turns the listed flags **on from this position to the end of the enclosing group** (or to the end of the whole pattern if it's at top level). It is purely a switch; it matches no characters. 2. **Scoped non-capturing group `(?flags:subpattern)`** — e.g. `(?i:abc)`. The flags apply **only to `subpattern`** inside the parentheses, and the previous flag state is restored after the closing `)`. Like `(?:...)`, it is **non-capturing**: it groups for the flag's sake without creating a numbered capture group. ## Scope precisely - `(?i)abc def` — case-insensitive for *both* `abc` and `def` (the switch runs to the end). - `(?i:abc)def` — case-insensitive for `abc` only; `def` is case-sensitive again. - `a(?i)bc` — `a` is case-sensitive, `bc` is case-insensitive (the token only affects what *follows* it). It cannot reach backward to `a`. ## Turning a flag OFF Prefix the letters with `-`: - `(?-i:...)` disables CASE_INSENSITIVE inside the group. - `(?-i)` disables it from here to the end of the enclosing group. You can combine on and off in one token: `(?i-x:...)` enables `i` and disables `x` for the group. The general form is `(?on-off:...)` or `(?on-off)`. ## Why scoped forms matter They let one pattern mix modes. Classic example: a SQL-ish keyword that should be case-insensitive followed by an identifier that must keep its case: ``` (?i:select)\s+(?-i:[A-Za-z_]+) ``` Here `SELECT`, `select`, `SeLeCt` all match the keyword, but the identifier portion is treated case-sensitively. Achieving this with a single compile-time flag is impossible because a compile flag is all-or-nothing for the whole pattern — which is exactly why scoped inline flags exist. ## Relationship to compile flags A compile-time flag (e.g. `Pattern.CASE_INSENSITIVE`) is equivalent to wrapping the entire pattern in `(?i)` at the front. Inline scoped groups give finer control than any compile flag can. When both are present, the inline scoped group still wins for its region (it explicitly sets/unsets the bit there). ## Gotchas - Not every flag is meaningfully scopable in practice, but the common ones (`i`, `m`, `s`, `x`, `u`, `U`, `d`) follow this syntax. - `(?i:...)` does **not** capture; if you also need to capture, you still wrap with normal `(...)` — e.g. `((?i:abc))` captures while applying the flag. - Forgetting the colon: `(?i)` vs `(?i:...)` are different tools — one is open-ended, one is bounded. ## Deriving the answer If only a slice of the pattern needs a mode, use the **scoped** `(?flags:...)`; to flip a mode off mid-pattern use the **minus** form `(?-flag...)`. Reserve the bare `(?flags)` token (or a compile flag) for 'whole pattern' cases.
- Write a pattern where the word 'http' is case-insensitive but the rest of a URL scheme check is case-sensitive.(?i:http)(?-i:S?)://... or more simply (?i:http)(?-i:[a-z]+) — the (?i:...) scopes folding to 'http' only.
- Does ((?i:foo)) capture anything, and how many groups are there?Yes — the outer (...) is group 1 capturing 'foo'; the inner (?i:...) is non-capturing and just scopes the flag. One capturing group total.
saying these in an interview costs you the question
- Thinking (?i:...) creates a capturing group (it's non-capturing)
- Believing (?i) affects characters earlier in the pattern
- Not knowing the minus form exists for disabling a flag locally
- Trying to get per-segment case rules from a single compile-time flag