1

I have a HTML fragment which contains two anchor tags in various parts of the HTML.

<span id="ctl00_PlaceHolderTitleBreadcrumb_ContentMap">
    <span><a class="ms-sitemapdirectional" href="/">My Site</a></span>
    <span> &gt; </span>
    <span><a class="ms-sitemapdirectional" href="/Lists/Announcements/AllItems.aspx">Announcements</a></span>
    <span> &gt; </span>
    <span class="ms-sitemapdirectional">Settings</span>
</span>

I'm looking to write a regular expression that will return the second anchor tag, which has 'Announcements' as it's text. In trying to write an expression, I keep getting both anchor tags returned - but I'm only interested in the second tag.

Is it possible to match the second tag only?

EDIT:

I will always know that I'm looking for an anchor tag which has 'Announcements' in it's text, if that helps.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Jason Evans
  • 28,906
  • 14
  • 90
  • 154
  • possible duplicate of [Can you provide some examples of why it is hard to parse XML and HTML with a regex?](http://stackoverflow.com/questions/701166/can-you-provide-some-examples-of-why-it-is-hard-to-parse-xml-and-html-with-a-rege) – Brad Mace Jul 09 '11 at 20:53
  • 2
    possible duplicate of [RegEx match open tags except XHTML self-contained tags](http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags) – Paŭlo Ebermann Sep 15 '11 at 14:17

6 Answers6

7

Parse the fragment into a DOM. Use XPath to issue:

(//a)[2]

Done.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
1

like

   /<a.+?>[^<>]*Announcements[^<>]*</a>/

PS. regular expression are the wrong tool for parsing html

user187291
  • 53,363
  • 19
  • 95
  • 127
1
/(<a.*?<\/a>).*?(<a.*?<\/a>)/

$1 matches the first tag, $2 matches the second

Rob
  • 8,042
  • 3
  • 35
  • 37
0

you don't have to use complicated regular expression for this if you don't want to. since you want to get anchors, and usually anchors has ending tags </a>, you can use your favourite language and do splits on </a> for each line. eg pseudocode

for each line in htmlfile
do
   var=split line on </a>
   for each item in var
   do
        if item has "Announcement" then
           print "found"
        end if
   done
done
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
0
<?php
$string = '<span id="ctl00_PlaceHolderTitleBreadcrumb_ContentMap"><span><a class="ms-sitemapdirectional" href="/">My Site</a></span><span> &gt; </span><span><a class="ms-sitemapdirectional" href="/Lists/Announcements/AllItems.aspx">Announcements</a></span><span> &gt; </span><span class="ms-sitemapdirectional">Settings</span></span>';

$dom = new DOMDocument();
$dom->loadHTML($string);
$anchors = $dom->getElementsByTagName('a');
if ( $anchors->length ) {
    $secondAnchor = $anchors->item(1);
    echo innerHTML($secondAnchor->parentNode);
}

function innerHTML($node){
    $doc = new DOMDocument();
    foreach ($node->childNodes as $child)
    $doc->appendChild($doc->importNode($child, true));

    return $doc->saveHTML();
}
meder omuraliev
  • 183,342
  • 71
  • 393
  • 434
0

If you know the exact text of the element, and you know it's the last element of its kind in the fragment, you have more than enough information to match it with a regex. I suspect you're using a regex like this:

/<a\s+.*>Announcements<\/a>/s

...and the .* is matching everything between the <a of the first anchor tag and the >Announcements</a> of the second one. Switching to a non-greedy quantifier:

/<a\s+.*?>Announcements<\/a>/s

...doesn't help; a reluctant quantifier stops matching as soon as possible, but the problem here is that it starts matching too soon. You need to replace the .* with something more specific, something that can only match whatever comes between the opening <a and closing > of a single tag:

/<a\s+[^<>]+>Announcements<\/a>/

Now, when it reaches the end of the first <a> tag and doesn't see Announcements</a> it will abort that match attempt, move along and start fresh at the second <a> tag.

Alan Moore
  • 73,866
  • 12
  • 100
  • 156