Внедрить HTML-разметку вокруг определенных слов в строке - PullRequest
1 голос
/ 25 июня 2009

Скажите, у меня есть две строки: «Здесь текст» и «Здесь текст»

И у меня есть коллекция, которая содержит слова, которые я хотел бы сопоставить с текстом в строках. «Некоторые», «Текст», «Здесь»

Если одно из слов соответствует определенному слову в строке (независимо от того, является ли оно прописным или строчным), я бы хотел взять исходное слово из строки и добавить вокруг него некоторую HTML-разметку, например <dfn title="Definition of word">Original word</dfn> ,

Я играл с методом string.Replace (), но не знал, как его сопоставить независимо от регистра и как сохранить исходное слово без изменений (чтобы я не заменял слово на * 1008). * или наоборот).

Ответы [ 6 ]

5 голосов
/ 25 июня 2009

Действительно, метод string.Replace недостаточно универсален для ваших требований в этом случае. Работа с текстом нижнего уровня должна делать эту работу. Альтернатива, конечно, регулярное выражение, но алгоритм, который я здесь представляю, будет наиболее эффективным методом, и я подумал, что было бы полезно написать его в любом случае, чтобы увидеть, как вы можете много манипулировать текстом без регулярное выражение для изменения.

Вот функция.

Обновление:

  1. Теперь работает с Dictionary<string, string> вместо string[], что позволяет передать определение в функцию вместе со словом.
  2. Теперь работает с произвольным порядком словаря определений.

...

public static string HtmlReplace(string value, Dictionary<string, string>
    definitions, Func<string, string, string> htmlWrapper)
{
    var sb = new StringBuilder(value.Length);

    int index = -1;
    int lastEndIndex = 0;
    KeyValuePair<string, string> def;
    while ((index = IndexOf(value, definitions, lastEndIndex,
        StringComparison.InvariantCultureIgnoreCase, out def)) != -1)
    {
        sb.Append(value.Substring(lastEndIndex, index - lastEndIndex));
        sb.Append(htmlWrapper(def.Key, def.Value));
        lastEndIndex = index + def.Key.Length;
    }
    sb.Append(value.Substring(lastEndIndex, value.Length - lastEndIndex));

    return sb.ToString();
}

private static int IndexOf(string text, Dictionary<string, string> values, int startIndex,
    StringComparison comparisonType, out KeyValuePair<string, string> foundEntry)
{
    var minEntry = default(KeyValuePair<string, string>);
    int minIndex = -1;
    int index;
    foreach (var entry in values)
    {
        if (((index = text.IndexOf(entry.Key, startIndex, comparisonType)) < minIndex
            && index != -1) || minIndex == -1)
        {
            minIndex = index;
            minEntry = entry;
        }
    }

    foundEntry = minEntry;
    return minIndex;
}

И небольшая тестовая программа. (Обратите внимание на использование лямбда-выражения для удобства.)

static void Main(string[] args)
{
    var str = "Definition foo; Definition bar; Definition baz";
    var definitions = new Dictionary<string, string>();
    definitions.Add("foo", "Definition 1");
    definitions.Add("bar", "Definition 2");
    definitions.Add("baz", "Definition 3");
    var output = HtmlReplace(str, definitions,
        (word, definition) => string.Format("<dfn title=\"{1}\">{0}</dfn>", 
            word, definition));
}

Вывод текста:

Definition foo ; Definition bar ; Определение baz

Надеюсь, это поможет.

3 голосов
/ 25 июня 2009

Вы можете использовать регулярное выражение:

class Program {

    static string ReplaceWord(Match m) {
        return string.Format("<dfn>{0}</dfn>",m.Value);
    }

    static void Main(string[] args) {

        Regex r = new Regex("some|text|here", RegexOptions.IgnoreCase);
        string input = "Some random text.";
        string replaced = r.Replace(input, ReplaceWord);
        Console.WriteLine(replaced);
    }
}

RegexOptions.IgnoreCase используется для сопоставления слов в списке независимо от их регистра.
Функция ReplaceWord возвращает совпадающую строку (в правильном регистре), окруженную открывающим и закрывающим тегом (обратите внимание, что вам все еще может потребоваться экранировать внутреннюю строку).

1 голос
/ 25 июня 2009

Во-первых, я буду иметь в виду и предоставлю анти-ответ: контрольный пример для вас, который является баггером для кода.

Что произойдет, если у меня есть условия:

Web Browser
Browser History

И я запускаю это против фразы:

Now, clean the web browser history by ...

Вы получаете

Now, clean the <dfn title="Definition of word">web <dfn title="Definition of word">browser</dfn> history</dfn> by ...

Я недавно боролся с той же проблемой, но не думаю, что мое решение поможет вам - http://github.com/jarofgreen/TaggedWiki/blob/d002997444c35cafecd85316280a896484a06511/taggedwikitest/taggedwiki/views.py строка 47 и далее. В итоге я поставил маркер напротив тега, а не завернул текст.

Однако у меня может быть одна часть ответа для вас: во избежание ловли слов в HTML (проблема того, что происходит, если у вас есть тег «title», который вы определили в своем последнем абзаце), я сделал 2 прохода , В первом проходе поиска я сохранил расположение фраз для переноса, затем во втором проходе без поиска я вставил фактический HTML. Таким образом, в тексте нет HTML-кода, пока вы выполняете фактический поиск.

0 голосов
/ 25 июня 2009

Код регулярного выражения:

/// <summary>
/// Converts the input string by formatting the words in the dict with their meanings
/// </summary>
/// <param name="input">Input string</param>
/// <param name="dict">Dictionary contains words as keys and meanings as values</param>
/// <returns>Formatted string</returns>
public static string FormatForDefns(string input, Dictionary<string,string> dict )
{
    string formatted = input;
    foreach (KeyValuePair<string, string> kv in dict)
    {
        string definition = "<dfn title=\"" + kv.Value + "\">" + kv.Key + "</dfn>.";
        string pattern = "(?<word>" + kv.Key + ")";
        formatted = Regex.Replace(formatted, pattern, definition, RegexOptions.IgnoreCase);
    }
    return formatted;
}

Это телефонный код

Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("word", "meaning");
dict.Add("taciturn ", "Habitually silent; not inclined to talk");

string s = "word abase";
string formattedString = MyRegEx.FormatForDefns(s, dict);
0 голосов
/ 25 июня 2009

Самый простой способ - использовать String.Replace, как вы сказали.

Я был удивлен, что не было возможности указать StringComparisonOptions в String.Replace.

Я написал для вас "не очень оптимизированный", но очень простой IgnoreCaseReplace:

static string IgnoreCaseReplace(string text, string oldValue, string newValue)
{
    int index = 0;
    while ((index = text.IndexOf(oldValue,
        index,
        StringComparison.InvariantCultureIgnoreCase)) >= 0)
    {
        text = text.Substring(0, index)
            + newValue
            + text.Substring(index + oldValue.Length);

        index += newValue.Length;
    }

    return text;
}

Чтобы сделать его более красивым, вы можете обернуть его в статический класс и сделать его методом расширения String:

static class MyStringUtilities
{
    public static string IgnoreCaseReplace(this string text, string oldValue, string newValue)
    {
        int index = 0;
        while ((index = text.IndexOf(oldValue,
            index,
            StringComparison.InvariantCultureIgnoreCase)) >= 0)
        {
            text = text.Substring(0, index)
                + newValue
                + text.Substring(index + oldValue.Length);

            index += newValue.Length;
        }

        return text;
    }
}
0 голосов
/ 25 июня 2009

Может быть, я неправильно понял ваш вопрос. Но почему бы просто не использовать регулярные выражения?

Если вы правильно настроили регулярное выражение, то они быстрее, надежнее и обеспечивают индексацию исходной строки, которая даст вам точную позицию сопоставленного слова, так что вы сможете вставить разметку точно в нужное место. *

Но учтите, что вам придется использовать String.Insert () с позициями совпадений, а string .replace () не поможет.

Надеюсь, что ответит на ваш вопрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...