Skip to content

\K — keep everything before this point out of the match

One line: \K resets the start of the reported match to the current position, so foo\Kbar matches bar only after checking that foo came first — a lookbehind with no width limit — in Perl, PCRE2 and Ruby; Python, Java, Go and both Rust crates refuse it, and JavaScript reads it as the letter K.

Level: reference · one construct, eight engines

What it means

The pattern consumes foo; the match begins after it. That is different from (?<=foo)bar, which never consumes foo, in two ways that matter:

  • No width limit. (?<=\w+\s) is refused by every engine with a fixed-width or bounded rule; \w+\s\K is fine, because it is ordinary matching followed by a reset.
  • In a replacement, the consumed text is left alone. s/foo\Kbar/X/ produces fooX. The foo was part of what the engine walked over, and it is not part of what gets replaced.

The price is that the consumed part is really consumed: a global match continues after it, which is what you want for foo but not for a pattern that needs to re-examine that text.

Measured

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

construct         python  perl  node  ruby  java  go  rust  fancy
match after \K    -       bar   no    bar   -     -   -     bar
replace after \K  -       fooX  no    fooX  -     -   -     fooX
\K in JavaScript  -       no    aKb   no    -     -   -     no

Reading the table

  • Row 1. foo\Kbar on foobar reports bar in Perl, Ruby and fancy-regex. Python, Java, Go and Rust's regex refuse the escape — Rust's parser has no \K, and fancy-regex adds it on top.
  • Row 2. Replacing the match with X gives fooX in the same three engines: the reset also decides what a substitution touches.
  • Row 3. JavaScript compiled a\Kb and matched aKb. There is no \K there, and no error either.

The trap

Only JavaScript's row 3 — and the portability question. \K is a PCRE-family feature that Ruby adopted; if a pattern using it must also run in Python or Java, the equivalent is a capturing group and group(1), or a lookbehind where the width allows one.

Where it is idiomatic: PCRE2, so pcre2grep -o 'key=\K\w+' and ABAP's PCRE addition; and grep -oP, which is PCRE2 with GNU grep's face.

Where it is measured in depth

See also