tags:

views:

65

answers:

5

For an example input of:

<a href="abc" something=b foo="bar" baz=cool>

I am trying to match:

something=b
baz=cool

However, everything I end up with will only match the first one (something=b), even when using preg_match_all. The regular expression I am using is:

<\w+.*?(\w+=[^"|^'|^>]).*?>

Or:

<\w+ # Word starting with <
.*?  # Anything that comes in front of the matching attribute.
(
\w+  # The attribute
=
[^"|^'|^>]+? # Keep going until we find a ", ' or >
)
.*? # Anything that comes after the matching attribute.
> # Closing >

I'm probably doing something horribly wrong, pretty new to regular expressions. Please advise! :)

edit:

Revised regular expression:

<\w+.*?\w+=([^"\'\s>]+).*?>

I want it to match zzz=aaa there too ;)

A: 

In your regex <\w+.*?(\w+=[^"|^'|^>]).*?>, the \w+=[^"|^'|^>] part doesn't do what you think it does - you are mixing character classes and alternation with pipe character

Writing a regex that will catch all malformed attributes inside a given XMLish tag is tricky if the attribute value can have > or = characters.

For example:

<a href="asd" title=This page proves that e=MC^2>

Your regex tries to extract all attributes from the whole string in one step - it looks for <tag and then an unquoted attribute somewhere later. This way you'll match only one attribute, the first one.

You can extract the contents of the opening and closing angle brackets in one step and then look for attributes within that. The regex <\w+\s+([^>]+?)\s*> will give you the substring of attributes. Search within that string for unquoted attributes. If the attributes are simple (as in they don't contain spaces), you can use a simple

\w+=(?=[^"'])[^ ]+

If they can contain spaces too, you're gonna need some more lookahead:

\w+=(?=[^"']).+?(?=\w+=|$)
Amarghosh
That still only matches my first attribute, unfortunately. And that would only match something=b, even with something=bazzz
Daniel
@Daniel I wasn't suggesting any regex - I just quoted yours to point out why it wasn't working. See my update for a work around
Amarghosh
A: 

For starters, the caret "^" symbol negates the entire character class. The character class has implied or statements, that's the point of a character class, so your class can be shortened to [^'">]

Now as to why you're only getting the "something=b" tag, I believe you're missing a + after your character class.

So your regexp with my modifications would be:

<\w+.*?(\w+=[^"'>]+?) .*?>

Note the space after the end of the group

Josiah
You should escape the Regex with backticks. Otherwise the `*` get dropped and the Regex is italicized.
nikic
This helps somewhat (I had to add \s to the character class), but still only matches ONE instead of ALL the attributes inside my tag.
Daniel
With the space after the closing bracket this regex won't match `<div id=hi>`.
nikic
@nikic... escape with what? Backticks won't help you here.
salathe
@salathe: He meant that SO sees the `*`s as formatting chars. No matter; I edited the post to show the regex correctly.
cHao
No? I thought markdown formatting is diabled within backticks. But now the author has placed it in a code block, this solves the problem, too.
nikic
@cHao, oh. my mistake. :)
salathe
+3  A: 

Use a library like Tidy or HTMLPurifier to fix broken HTML for you.

Gordon
Daniel
A: 
<\w+
(?:
  \s+
  (?:
    \w+="[^"]*"
    |(\w+=[^\s>]+)
  )
)+
\s*/?>

You may try this with # delimiter and x modifier. I have formatted it so it is more readable.

nikic
A: 

If you know you don't have any = sign outside your tags, you can use this regex:

(?<=\=)([^"\'\s>]+)(?=[\s>])

In this example it matches all improper attributes

Edit:

(?<=\=)([^"\'\s/>]+)(?=[\s/?>])

this matches class2 in <div class=class2/> also.

True Soft