Since, I'm new in regular expressions; I want to make a regular expression to select two consecutive words.
For example when i give this phrase: "Hello people #RegularExpression sucks!"
It has to return t开发者_运维技巧hese couple words:
-Hello people
-people #RegularExpression
-#RegularExpression sucks!
I tried this /\w\s\w/i
but it did not work :(
$s = "Hello people #RegularExpression sucks!";
preg_match_all('~(?=(\S+\s+\S+))\S+\s+~', $s, $matches);
print_r($matches[1]);
output:
Array
(
[0] => Hello people
[1] => people #RegularExpression
[2] => #RegularExpression sucks!
)
explanation:
\S+
matches one or more non-whitespace characters. Your \w
was incorrect for two reasons: it only only matches one character; and it only matches a so-called word character (equivalent to [A-Za-z0-9_]
). Adding the +
to your \s
wasn't necessary in this test case, but there's no reason not to add it, and extra whitespace does have a way of sneaking into text in the real world. (But be sure and add +
, not *
; there must be at least one whitespace character in there.)
(?=...)
is a positive lookahead. You use them to check whether it's possible to match the enclosed subexpression at the current match position, without advancing the match position. Then, typically, you go ahead and match a different subexpression, not in a lookahead.
Here's the tricky bit: Although the characters matched by the lookahead subexpression are not consumed, any capturing groups in the subexpression work as usual. The lookahead in my regex, (?=(\S+\s+\S+))
matches and captures the next two-word sequence. Then (assuming the lookahead succeeded) \S+\s+
matches in the normal way, setting the match position correctly for the next attempt.
This technique should work in any regex flavor that supports capturing groups and lookaheads. That includes PHP as well as all the other major languages (Perl, JavaScript, .NET, Python, Java...). The technique for accessing only the contents of the first capturing group from each match varies wildly from one language to the next, but PHP makes it easy, with $matches[1]
.
Your regular expression would actually match two letters separated by a space. So with your input you'd get o p
and n s
. The other issue with doing this is doing a global regular expression search on a string returns non-overlapping instances. So a proper regular expression could return Hello people
, #RegularExpression sucks!
, but it wouldn't return people #RegularExpression
as that overlaps with Hello people
. A third question is how do you define word? The classical definition, and the one used by the \w
atom, is alphanumeric or underscore. As such, #RegularExpression
wouldn't match because #
isn't a word character.
In all, it sounds like what you really want to do is just split your string on spaces, and then you can collect all the word pairs yourself. You can do the split with something like preg_split('/\s+/', $str)
to return an array of all whitespace-separated words, and then you can iterate over the array however you want.
I'm pretty sure it is possible to do with regex, but the pickle here is regexes consume watch they match, so "going back" to get your overlapping matches is a tricky thing to do. Regex is not the right tool for this; a hammer does not suck because it can't (properly) handle screws.
If I were you I'd just do:
$str = "Hello people #RegularExpression does not suck!";
$arr = explode(' ', $str);
for ($i=0; $i<count($arr) - 1; $i++) {
echo implode(' ', array_slice($arr, $i, 2)) . "\n";
}
Outputs:
Hello people
people #RegularExpression
#RegularExpression does
does not
not suck!
Like others said, that seems not possible (EDIT: Oops, that's wrong, see Alan's answer) in standard pcre regex, and you better choose an another strategy.
Let me just add that it seems to exist an experimental and tricky solution : the backtracking verbs.
See the section "BACKTRACKING CONTROL" in the document pcre.org/pcre.txt
This pattern should work:
/[^\s]+\s[^\s]+/i
Matches every non whitespace followed by a single whitespace char and other non whitespace chars.
精彩评论