Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Positive lookbehind vs match reset (\K) regex feature

I just learned about the apparently undocumented \K behavior in Ruby regex (thanks to this answer by anubhava). This feature (possibly named Keep?) also exists in PHP, Perl, and Python regex flavors. It is described elsewhere as "drops what was matched so far from the match to be returned."

"abc".match(/ab\Kc/)     # matches "c"

Is this behavior identical to the positive lookbehind marker as used below?

"abc".match(/(?<=ab)c/)  # matches "c"

If not, what differences do the two exhibit?

like image 507
user513951 Avatar asked Jan 29 '16 19:01

user513951


People also ask

What is a positive Lookbehind in regex?

Positive lookbehind is syntaxed like (? <=a)something which can be used along with any regex parameter. The above phrase matches any "something" word that is preceded by an "a" word. Negative Lookbehind is syntaxed like (? <!a)something which is used to match a "something" word that is not preceded by an "a".

What does positive lookahead mean?

The positive lookahead construct is a pair of parentheses, with the opening parenthesis followed by a question mark and an equals sign. You can use any regular expression inside the lookahead (but not lookbehind, as explained below). Any valid regular expression can be used inside the lookahead.

What is ?! In regex?

The ?! n quantifier matches any string that is not followed by a specific string n.

What is a negative look ahead in regular expression?

In this type of lookahead the regex engine searches for a particular element which may be a character or characters or a group after the item matched. If that particular element is not present then the regex declares the match as a match otherwise it simply rejects that match.


1 Answers

It's easier to see the difference between \K and (?<=...) with the String#scan method.

A lookbehind is a zero-width assertion that doesn't consume characters and that is tested (backwards) from the current position:

> "abcdefg".scan(/(?<=.)./)
=> ["b", "c", "d", "e", "f", "g"]

The "keep" feature \K (that isn't an anchor) defines a position in the pattern where all that was matched so far by the pattern on the left is removed from the match result. But all characters matched before the \K are consumed, they just don't appear in the result:

> "abcdefg".scan(/.\K./)
=> ["b", "d", "f"]

The behaviour is the same as without \K:

> "abcdefg".scan(/../)
=> ["ab", "cd", "ef"]

except that the characters before the \K are removed from the result.

One interesting use of \K is to emulate a variable-length lookbehind, which is not allowed in Ruby (the same for PHP and Perl), or to avoid the creation of a unique capture group. For example (?<=a.*)f. can be implemented using \K:

> "abcdefg".match(/a.*\Kf./)
=> #<MatchData "fg">

An alternative way would be to write /a.*(f.)/, but the \K avoids the need to create a capture group.

Note that the \K feature also exists in the python regex module, even this one allows variable-length lookbehinds.

like image 97
Casimir et Hippolyte Avatar answered Sep 27 '22 20:09

Casimir et Hippolyte