tags:

views:

92

answers:

4

I need to fetch a particular element in HTML that has the following pattern: (C#)

<td class="blah" ...........>Some text blah: page x of xx<br>

I need to get the value of xx.

The only thing that is constant in the above pattern is:

  1. its a TD element
  2. it has class="blah" in it
  3. it has the text pattern ": page x of xx

You can assume there is only 1 occurance of the above pattern.

+7  A: 

Please don't use regexes to parse HTML!

Grab a copy of the HTML agility pack and your life will be much simpler, and your application much less brittle.

jvenema
+1, enough said
just somebody
A: 

Is it arbitrary HTML? Can it have CDATA blocks, comments, external character entities?

If any the above is true, then you should forget about regex for this purpose, and use something like HTML Agility Pack to properly parse it to DOM, and then work with that.

Pavel Minaev
A: 

Use a parser to get the content from the particular TD you care about, then use a regex along the lines of \d of (\d{2})$ which should get the value of xx in the first capture group.

I'm specifically not trying to write a regex that will handle the HTML part of this question; see also the <center> cannot hold.

Hank Gay
+1  A: 

Using regex is not the correct way to do this. As others have pointed out, use an HTML parser. If you have HTML Agility Pack, you can do this:

using System;
using System.Linq;
using System.Text.RegularExpressions;
using HtmlAgilityPack;

class Program
{
    static void Main(string[] args)
    {
        string html = @"<html><body><td class=""blah"" ...........>Some text blah: page 13 of 99<br> more stuff</td></body></html>";
        HtmlDocument doc = new HtmlDocument();
        doc.LoadHtml(html);
        var nodes = doc.DocumentNode.SelectNodes("//td[@class='blah']");
        if (nodes != null)
        {
            var td = nodes.FirstOrDefault();
            if (td != null)
            {
                Match match = Regex.Match(td.InnerText, @"page \d+ of (\d+)");
                if (match.Success)
                {
                    Console.WriteLine(match.Groups[1].Value);
                }
            }
        }
    }
}

Output:

99

However, it can be done with regex, as long as you accept that it's not going to be a perfect solution. It's fragile, and can easily be tricked, but here it is:

class Program
{
    static void Main(string[] args)
    {
        string s = @"stuff <td class=""blah"" ...........>Some text blah: page 13 of 99<br> more stuff";
        Match match = Regex.Match(s, @"<td[^>]*\sclass=""blah""[^>]*>[^<]*page \d+ of (\d+)<br>");

        if (match.Success)
        {
            Console.WriteLine(match.Groups[1].Value);
        }
    }
}

Output:

99

Just make sure no-one ever sees you do this.

Mark Byers
where is the first match?
mrblah
I'm sorry, I don't understand your question. Can you rephrase it? Which implementation are you referring to: the HTML parsing or the pure regex?
Mark Byers
could (\d+) be converted into a named match? so I could do match.Groups["somename"].Value ?
mrblah
Yes. Change `(\d+)` to `(?<somename>\d+)` in either version.
Mark Byers