Найти ключевое слово в тексте, когда ключевое слово соответствует определенным условиям — C#

Я ищу хороший способ сделать следующее:

У меня есть статья, в которой есть HTML-теги, такие как якоря, абзацы и т. д.
У меня также есть ключевое слово, которое мне нужно найти в статье и установить его как якорь (у меня есть некоторый URL-адрес для установки там). < br /> Если ключевое слово существует в статье, оно должно соответствовать следующим ДВУМ условиям, ПРЕЖДЕ чем сделать его якорем:

  1. Он не может быть внутри какого-либо тега. Например, что-то вроде

    <img alt="keyword"> 
    

    не будет действительным/сопоставленным.

  2. Ключевое слово не может уже находиться внутри привязки. Например, что-то вроде

    <a>keyword</a>
    

    не будет действительным/сопоставленным.


    Будем признательны за любую помощь. Спасибо


person YanivHer    schedule 29.01.2013    source источник
comment
Пожалуйста, объясните, где вы пытаетесь создать это ограничение. В функции JavaScript? В веб-фреймворке?   -  person isherwood    schedule 29.01.2013
comment
Я пытаюсь сделать это на С#   -  person YanivHer    schedule 29.01.2013
comment
Нет, это был мой плохой. Я добавил это в заголовок после того, как вы упомянули об этом :)   -  person YanivHer    schedule 29.01.2013
comment
Я не понимаю, что вы пытаетесь сделать. Просто пытаюсь уточнить. Вам нужно вставить ссылку, но вы не можете использовать тег <a>?   -  person EJC    schedule 29.01.2013
comment
Нет, мне нужно вставить ссылку на какое-то существующее слово в статье. Но я должен убедиться, что я не сделаю это слово ссылкой, когда оно является частью атрибута какого-либо элемента или уже является ссылкой (внутри элемента привязки). Надеюсь, что прояснение вещей немного больше.   -  person YanivHer    schedule 29.01.2013
comment
Я упорядочил и изменил вопрос выше, чтобы сделать его более понятным. Надеюсь поможет помощникам.   -  person YanivHer    schedule 29.01.2013
comment
Ах я вижу. Хм, это сложно... Откуда статья? Пользовательский ввод? Вы можете создать свою собственную разметку, как на этом сайте. Или вы можете найти предыдущий и следующий символ в строке и убедиться, что это пробел, точка или точка с запятой. (Используйте регулярное выражение) Но это кажется немного хрупким. Хм...   -  person EJC    schedule 29.01.2013
comment
Моей первой мыслью было поступить как искатель. Например, когда вы загружаете файл и идете по символам, ищете «‹» и проверяете, является ли следующий «a», если это так, то ищите его закрывающий тег. Но я подумал, почему бы сначала не попросить лучшее решение :) Кстати, статья является пользовательским вводом. это происходит из элемента управления форматированным текстом.   -  person YanivHer    schedule 29.01.2013
comment
О, я вижу, что ты делаешь. Вы связываете слова в пользовательском вводе на основе некоторых критериев, которые ВЫ имеете. Я не уверен в лучшем способе. Я пытаюсь думать, я не думаю, что вы захотите искать каждый символ, но я не уверен, что еще делать.   -  person EJC    schedule 29.01.2013


Ответы (1)


Мне удалось это сделать!

Большое спасибо этому сообщению, которое очень помогло мне с выражением xpath: http://social.msdn.microsoft.com/Forums/en-US/regexp/thread/beae72d6-844f-4a9b-ad56-82869d685037/

Моя задача состояла в том, чтобы добавить X ключевых слов в статью, используя таблицу ключевых слов и URL-адресов в моей базе данных.
После совпадения ключевого слова - он не будет искать его снова, а попытается найти следующее ключевое слово в тексте.
Ключевое слово могло состоять из нескольких слов. Вот почему я добавил Replace(" ", "\s+").
Кроме того, сначала мне пришлось отдать предпочтение самым длинным ключевым словам. То есть, если бы у меня были:
"добрый день" и "хороший" как два разных ключевых слова - "добрый день" всегда побеждает.

Это мое решение:

static public string AddLinksToArticle(string article, int linksToAdd)
    {
        try
        {
            //load keywords and urls
            var dt = new DAL().GetArticleLinks();

            //sort the it
            IEnumerable<ArticlesRow> sortedArticles = dt.OrderBy(row => row.keyword, new StringLengthComparer());

            // iterate the dictionary to get keyword to replace with anchor
            foreach (var item in sortedArticles)
            {
                article = FindAndReplaceKeywordWithAnchor(article, item.keyword, item.url, ref linksToAdd);
                if (linksToAdd == 0)
                {
                    break;
                }
            }

            return article;
        }
        catch (Exception ex)
        {
            Utils.LogErrorAdmin(ex);
            return null;
        }
    }

    private static string FindAndReplaceKeywordWithAnchor(string article, string keyword, string url, ref int linksToAdd)
    {
        //convert text to html
        HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
        doc.LoadHtml(article);

        // \w* - means it can start with any alphanumeric charactar
        // \s+ - was placed to replace all white spaces (when there is more than one word).
        // \b - set bounderies for the keyword
        string pattern = @"\b" + keyword.Trim().Insert(0, "\\w*").Replace(" ", "\\s+") + @"\b";

        //get all elements text propery except for anchor element 
        var nodes = doc.DocumentNode.SelectNodes("//text()[not(ancestor::a)]") ?? new HtmlAgilityPack.HtmlNodeCollection(null);
        foreach (var node in nodes)
        {
            if (node.InnerHtml.Contains(keyword))
            {
                Regex regex = new Regex(pattern);
                node.InnerHtml = regex.Replace(node.InnerHtml, "<a href=\"" + url + "\">" + keyword + "</a>", 1);//match only first occurrence
                linksToAdd--;
                break;
            }
        }

        return doc.DocumentNode.OuterHtml;
    }
}

public class StringLengthComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        return y.Length.CompareTo(x.Length);
    }
}

Надеюсь, это поможет кому-то в будущем.

person YanivHer    schedule 04.02.2013
comment
Этот код не будет работать должным образом, так как в методе FindAndReplaceKeywordWithAnchor есть критический дефект. node.InnerHtml.Contains(keyword) вернет true, если ваш тег находится в тексте даже как часть некоторого слова. Так что, если вам нужен сын, но есть Джейсон, он вернет true. И тогда ваш перерыв отменит цикл, так что сына не найдут. Вам нужно изменить node.InnerHtml.Contains (ключевое слово) на regex.IsMatch (node.InnerHtml), где регулярное выражение — это новое регулярное выражение (шаблон). - person Zoltan Kochan; 06.05.2013
comment
Ты прав. Я исправил это. Спасибо! Я не буду редактировать свой ответ, чтобы будущие люди увидели ваш вклад в него. - person YanivHer; 09.05.2013