Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In regex, match either the end of the string or a specific character

I have a string. The end is different, such as index.php?test=1&list=UL or index.php?list=UL&more=1. The one thing I'm looking for is &list=.

How can I match it, whether it's in the middle of the string or it's at the end? So far I've got [&|\?]list=.*?([&|$]), but the ([&|$]) part doesn't actually work; I'm trying to use that to match either & or the end of the string, but the end of the string part doesn't work, so this pattern matches the second example but not the first.

like image 563
Gary Avatar asked Aug 23 '12 00:08

Gary


People also ask

How do you specify the end of a string in regex?

End of String or Line: $ The $ anchor specifies that the preceding pattern must occur at the end of the input string, or before \n at the end of the input string. If you use $ with the RegexOptions. Multiline option, the match can also occur at the end of a line.

Which symbol is used to indicate the end of string regex?

The caret ^ and dollar $ characters have special meaning in a regexp. They are called “anchors”. The caret ^ matches at the beginning of the text, and the dollar $ – at the end. The pattern ^Mary means: “string start and then Mary”.

Which matches the end of the string?

\z is used to match end of the entire string in regular expression in java.

What does '$' mean in regex?

$ means "Match the end of the string" (the position after the last character in the string).


2 Answers

Use:

/(&|\?)list=.*?(&|$)/ 

Note that when you use a bracket expression, every character within it (with some exceptions) is going to be interpreted literally. In other words, [&|$] matches the characters &, |, and $.

like image 86
João Silva Avatar answered Sep 28 '22 06:09

João Silva


In short

Any zero-width assertions inside [...] lose their meaning of a zero-width assertion. [\b] does not match a word boundary (it matches a backspace, or, in POSIX, \ or b), [$] matches a literal $ char, [^] is either an error or, as in ECMAScript regex flavor, any char. Same with \z, \Z, \A anchors.

You may solve the problem using any of the below patterns:

[&?]list=([^&]*) [&?]list=(.*?)(?=&|$) [&?]list=(.*?)(?![^&]) 

If you need to check for the "absolute", unambiguous string end anchor, you need to remember that is various regex flavors, it is expressed with different constructs:

[&?]list=(.*?)(?=&|$)  - OK for ECMA regex (JavaScript, default C++ `std::regex`) [&?]list=(.*?)(?=&|\z) - OK for .NET, Go, Onigmo (Ruby), Perl, PCRE (PHP, base R), Boost, ICU (R `stringr`), Java/Andorid [&?]list=(.*?)(?=&|\Z) - OK for Python 

Matching between a char sequence and a single char or end of string (current scenario)

The .*?([YOUR_SINGLE_CHAR_DELIMITER(S)]|$) pattern (suggested by João Silva) is rather inefficient since the regex engine checks for the patterns that appear to the right of the lazy dot pattern first, and only if they do not match does it "expand" the lazy dot pattern.

In these cases it is recommended to use negated character class (or bracket expression in the POSIX talk):

[&?]list=([^&]*) 

See demo. Details

  • [&?] - a positive character class matching either & or ? (note the relationships between chars/char ranges in a character class are OR relationships)
  • list= - a substring, char sequence
  • ([^&]*) - Capturing group #1: zero or more (*) chars other than & ([^&]), as many as possible

Checking for the trailing single char delimiter presence without returning it or end of string

Most regex flavors (including JavaScript beginning with ECMAScript 2018) support lookarounds, constructs that only return true or false if there patterns match or not. They are crucial in case consecutive matches that may start and end with the same char are expected (see the original pattern, it may match a string starting and ending with &). Although it is not expected in a query string, it is a common scenario.

In that case, you can use two approaches:

  • A positive lookahead with an alternation containing positive character class: (?=[SINGLE_CHAR_DELIMITER(S)]|$)
  • A negative lookahead with just a negative character class: (?![^SINGLE_CHAR_DELIMITER(S)])

The negative lookahead solution is a bit more efficient because it does not contain an alternation group that adds complexity to matching procedure. The OP solution would look like

[&?]list=(.*?)(?=&|$) 

or

[&?]list=(.*?)(?![^&]) 

See this regex demo and another one here.

Certainly, in case the trailing delimiters are multichar sequences, only a positive lookahead solution will work since [^yes] does not negate a sequence of chars, but the chars inside the class (i.e. [^yes] matches any char but y, e and s).

like image 25
Wiktor Stribiżew Avatar answered Sep 28 '22 05:09

Wiktor Stribiżew