tags:

views:

54

answers:

4

I've got the following string:

response: id="1" message="whatever" attribute="none" world="hello"

The order of the attributes is random. There might be any number of other attributes.

Is there a way to get the id, message and world attribute in one regular expression instead of applying the following three one after another?

/ message="(.*?)"/
/ world="(.*?)"/
/ id="(.*?)"/
A: 

I'm not a regex guy, so this might be wrong. If it is, don't downvote =) just let me know and I'll delete it! :-D

Could you or the phrases together and match that?

/ (message|world|id)="(.*?)"/

glowcoder
No, this will match only the first attribute it finds and not result in three matches.
christian studer
that's a meaningless. you meant `()` and not `[]`. but it wouldn't be possible which attribute value was referring to.
SilentGhost
This will only match the first property.
Svend
A: 

If you are using preg_match, then there's an optional third argument which will store all the regular expression groups (anything inside parentheses, basically) as well as the full match.

So use the whole thing as one regular expression and then the 2nd, 3rd, and 4th elements will be the message, world, and id respectively (the 1st will be the whole string that matched.

Daniel DiPaolo
I know about preg_match() and the matching array, but I'm looking for the right regular expression.
christian studer
+2  A: 

You can achieve this by using three positive lookahead assertions with .* at the front of them:

<?php
$re = '/(?=.* message="(.*?)")(?=.* world="(.*?)")(?=.* id="(.*?)")/';

$string = '<response id="1" message="whatever" attribute="none" world="hello" />';

preg_match($re, $string, $matches);
var_dump($matches);

Output:

array(4) {
  [0]=>
  string(0) ""
  [1]=>
  string(8) "whatever"
  [2]=>
  string(5) "hello"
  [3]=>
  string(1) "1"
}

Of course, this pattern will fail if any of those 3 parameters are missing (which might be helpful to you too...). If you want them to be optional, you can further wrap the inside of the lookahead into a non-capture group and make it optional (?:....)? (this example makes the "world" parameter optional)

/(?=.* message="(.*?)")(?=(?:.* world="(.*?)")?)(?=.* id="(.*?)")/
gnarf
Thank you, perfect. I didn't knew about lookaheads and will read up on them.
christian studer
A: 

The below is the best one I can come up with. It should capture the actual attributes matched at each point, their values and also allow anything other inbetween. It's not strictly correct though, and you would do well to use a "real" parser.

/((message|attribute|world)="([^"]*)").*?((message|attribute|world)="([^"]*)").*?((?:message|attribute|world)="([^"]*)")/
Svend