2009-06-25 2 views
1

Скажем, у меня есть эти две строки: «Some Text здесь» и «какой-то текст здесь»Вводят HTML разметки вокруг определенных слов в строке

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

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

Я играл с методом string.Replace(), но не уверен, как его можно совместить независимо от случая и как сохранить исходное слово неповрежденным (чтобы я не заменил слово «0» на <dfn title="">Word</dfn или наоборот).

+0

Вы создаете один из тех сайтов, на которых есть определенные слова, связанные с рекламой (например, ссылки браузера Word на IE8)? – Kredns

ответ

5

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

Вот эта функция.

Update:

  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)); 
} 

Выходной текст:

< Определение DFN название = " Определение 1 " > Foo </DFN >; Определение < dfn title = " Определение 2 " > бар </dfn >; Определение название < д.ф.н. = " Определение 3 " > Баз </д.ф.н. >

Надежда, что помогает.

+0

Причина голосования, пожалуйста? – Noldorin

+0

У меня возникли проблемы после изменения массива слов в коллекции словарей. Я получаю все, что работает, за исключением получения значения для отправки в качестве текста определения внутри метода string.format (выражение лямбда). Спасибо за помощь. – 2009-06-25 11:57:04

+0

@Frederik: Нет проблем ... Фактически вы могли бы просто использовать оператор switch в выражении лямбда из предыдущей версии, но я обновил сообщение, чтобы показать версию, которая использует словарь вместо этого. Возьмите в зависимости от того, что вы предпочитаете. – Noldorin

3

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

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 возвращает согласованную строку (правильно обведенную), окруженную тегом открытия и закрытия (обратите внимание, что вам все равно может понадобиться избежать внутренней строки).

0

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

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

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

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

0

Самый простой способ - использовать 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; 
} 

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

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

Regex код:

/// <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

Выполнение регулярного выражения заменяет это несколько раз (для каждой записи словаря) будет ужасно неэффективным. – Noldorin

+0

Да, вы правы. –

+0

Вы также рискуете, что ваше регулярное выражение ошибочно соответствует тексту, который был добавлен в строку более ранним Заменить(). Например, если одним из ключевых слов было «title», вы бы заменили имя атрибута «title» в любых уже существующих элементах dfn. –

1

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

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

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-кода, пока вы выполняете фактический поиск.

Смежные вопросы