0

I'm writing an auto linking extension for blogengine.net which auto links key phrases in the blog post to specific urls. The issue I'm finding is very often the list of phrases to auto link are sub sets of eachother, eg "bmw" is a subset of "bmw car leasing", so if I use a regex to autolink "bmw car leasing " first then auto link the phrase "bmw" next, the former is already auto linked. The precendence is important, the longer phrases must be auto linked first, then the smaller phrases which may be subsets of the longer ones.

What I need is a regex that will dismiss the match if it's already within an anchor tag, i.e my phrase should be dismissed.

I don't have to use regexes too often , so I'm not totally au fait with them, so far I've managed to put together a regex that will match on anchor tags, but not the reverse which is what I need. eg <a\b[^>]*>stuff(.*?)</a>

Any suggestions and advice would be very welcome.

Addition and hopefully the final solution .... only time will tell:- After a bit of trial and error, the final regex I used is below. This is based on the solution I marked as the answer:-

(?<!<a [^<]+)(?<!<img [^<]+)(?<=[ ,.;!]+)search phrase goes here(?=[ ,.;&!]+)(?!!.*<\\a>)

It allows the text being matched on to be preceded and succeeded by a space and basic punctuation as well as allowing for encoded characters like non breaking spaces &nbsp; etc. It also avoids anything in an img tag being matched. I realise it's still not 100% but as far as the requirements go it will suffice.

Thanks to all for your help and input.

Rich
  • 4,572
  • 3
  • 25
  • 31
  • 2
    http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454 – Jonas Elfström Mar 20 '11 at 23:47
  • Ahhhh come on Jonas, you telling me than in absolutely no cases can a regex work with html? I'm looking for what I think is just a simple case of ... match phrase so long as it's not preceded by an A tag, has no other A tags in between A tag and phrase, and is succeeded by a closing A tag with no other closing A tags in between. Is this really that difficult for regex? – Rich Mar 21 '11 at 00:04
  • hmmm ... 4352 up votes is the most extreme up voting I've seen here, damn I really didn't want to parse it manually! – Rich Mar 21 '11 at 00:12
  • 1
    Now I see that in this case you are actually only parsing a very small subset of HTML so I guess a regexp could work. At least if the text itself is free of other HTML elements. – Jonas Elfström Mar 21 '11 at 07:49

2 Answers2

1

Negative lookbehind and lookahead helps in such situations, the following matches something only if it's not preceded with (lookbehind):

(?<!<a>)something

However, as pointed out many times before here on SO, regular expressions are not the best tool to parse HTML, they are used for lexical analysis, not parsing. For more information check out the question linked to in the first comment to your question.

Community
  • 1
  • 1
steinar
  • 9,383
  • 1
  • 23
  • 37
1

The trick is to use a lookbehind that's non greedy and then add a lookahead for the ending of the anchor element. I find using tools like Expresso makes it much easier to create this kind of regular expressions.

var text = "Final report of the commercial starship Nostromo, third officer reporting. The other members of the crew, Kane, Lambert, Parker, Brett, Ash and Captain Dallas, are dead. Cargo and ship destroyed. I should reach the frontier in about six weeks. With a little luck, the network will pick me up. This is Ripley, last survivor of the Nostromo, signing off.";
var phrases = new List<KeyValuePair<string, string>> { 
    new KeyValuePair<string,string>("Nostromo", "http://www.imdb.com/media/rm3374159872/tt0078748"),
    new KeyValuePair<string,string>("starship Nostromo", "http://en.wikipedia.org/wiki/Alien_%28film%29#Spaceships_and_planets")};

foreach (var phrase in phrases.OrderByDescending(kv => kv.Key.Length))
{ 
    text = new Regex("(?<!<a [^<]+)" + phrase.Key + "(?!!.*<\\a>)").
                    Replace(text, "<a href=\"" + 
                                    phrase.Value + "\">" + 
                                    phrase.Key + "</a>");
}

Result:

Final report of the commercial starship Nostromo, third officer reporting. The other members of the crew, Kane, Lambert, Parker, Brett, Ash and Captain Dallas, are dead. Cargo and ship destroyed. I should reach the frontier in about six weeks. With a little luck, the network will pick me up. This is Ripley, last survivor of the Nostromo, signing off.

Jonas Elfström
  • 30,834
  • 6
  • 70
  • 106
  • +1 for Expresso, found it last night as part of http://www.codeproject.com/KB/dotnet/regextutorial.aspx , an invaluable tool for building and testing regular expressions. – Rich Mar 21 '11 at 14:23
  • Thanks Jonas, your regex works for the cases I've tested against. – Rich Mar 21 '11 at 14:29