views:

61

answers:

5

I'm trying to write a regular expression for matching the following HTML.

<span class="hidden_text">Some text here.</span>

I'm struggling to write out the condition to match it and have tried the following, but in some cases it selects everything after the span as well.

$condition = "/<span class=\"hidden_text\">(.*)<\/span>/";

If anyone could highlight what I'm doing wrong that would be great.

+1  A: 
$condition = "/<span class=\"hidden_text\">(?<=^|>)[^><]+?(?=<|$)<\/span>/";

I got it. ;)

diggersworld
Yes, that makes perfect sense :P
Skilldrick
Well, that won't work for simple input like <span class=\"hidden_text\"><b>Bold</b></span>
HoLyVieR
+6  A: 

You need to use a non-greedy selection by adding ? after .* :

$condition = "/<span class=\"hidden_text\">(.*?)<\/span>/";

Note : If you need to match generic HTML, you should use a XML parser like DOM.

HoLyVieR
+4  A: 

You shouldn’t try to use regular expressions on a non-regular language like HTML. Better use a proper HTML parser to parse the document.

See the following questions for further information on how to do that with PHP:

Gumbo
A: 

Chances are that you have multiple spans, and the regexp you're using will default to greedy mode

It's a lot easier using PHP's DOM Parser to extract content from HTML

Mark Baker
A: 

I think this is what they call a teachable moment. :P Let us now compare and contrast the regex in your self-answer:

"/<span class=\"hidden_text\">(?<=^|>)[^><]+?(?=<|$)<\/span>/"

...and this one:

'~<span class="hidden_text">[^><]++</span>~'
  • PHP's double-quoted strings are subject to interpolation of embedded variables ($my_var) and evaluation of source code wrapped in braces ({return "foo"}). If you aren't using those features, it's best to use single-quoted strings to avoid surprises. As a bonus, you don't have to escape those double-quotes any more.

  • PHP allows you to use almost any ASCII punctuation character for the regex delimiters. By replacing your slashes with ~ I eliminated the need to escape the slash in the closing tag.

  • The lookbehind - (?<=^|>) - was not doing anything useful. It would only ever be evaluated immediately after the opening tag had been matched, so the previous character was always >.

  • [^><]+? is good (assuming you don't want to allow other tags in the content), but the quantifier doesn't need to be reluctant. [^><]+ can't possibly overrun the closing </span> tag, so there's point sneaking up on it. In fact, go ahead and kick the door in with a possessive quantifier: [^><]++.

  • Like the lookbehind before it, (?=<|$) was only taking up space. If [^><]+ consumes everything it can and the next character not <, you don't need a lookahead to tell you the match is going to fail.

Note that I'm just critiquing your regex, not fixing it; your regex and mine would probably yield the same results every time. There are many ways both of them can go wrong, even if the HTML you're working with is perfectly valid. Matching HTML with regexes is like trying to catch a greased pig.

Alan Moore