2013-10-08 7 views
0

Что является разумным подходом к выполнению множества String.Replace без замены текста, который уже был заменен. Например, скажем, у меня есть эта строка:Несколько String.Replace без помех?

str = "Stacks be [img]http://example.com/overflowing.png[/img] :/"; 

А Regex я написал будет соответствовать [img]url[/img], и позвольте мне заменить его с правильным HTML <img> форматирования.

str = "Stacks be <img src=\"http://example.com/overflowing.png\"/> :/"; 

После я выполняю String.Replace заменить коды смайликов (:/, :(, :P, и т.д.) с <img> тегами. Однако, есть неожиданные результаты:

Предназначенный Результат

str = "Stacks be <img src=\"http://example.com/overflowing.png\"/> " + 
    "<img src=\"emote-sigh.png\"/>"; 

Actual (и очевидно) Результат

str = "Stacks be <img src=\"http<img src=\"emote-sigh.png"/> " + 
    "/example.com/overflowing.png\"/>" + 
    "<img src=\"emote-sigh.png\"/>"; 

Unfortunently, с числом замен я планирую сделать, кажется нецелесообразным пытаться сделать все это в одном выражении Regex (хотя я бы предположил, что это было бы самым результативным, так что lution). Что такое (более медленный, но) более удобный способ сделать это?

+0

пожалуйста показать код замены, который вы используете – Sayka

+1

Он чувствует ко мне, как переход к системе, где вы на самом деле разобрать различные части входного текста будет более полезным. Затем вы узнаете, что такое URL-адрес изображения и какой текст. Затем вы можете выполнять замены только на правильных битах ... –

+0

@JonSkeet Настоящим соглашайтесь, делая это таким же образом, также должен обеспечить самый удобный результат, все, что вам нужно сделать, это указать приоритет токенов, чтобы соответствовать, и что делать с ними, когда они найдены, а не беспокоиться о взаимодействиях между заменами – mlorbetske

ответ

2

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

Вот пример того, как вы могли бы сделать что:

Во-первых, мы создадим класс, который указывает, используется ли конкретная строка или не

public class UsageIndicator 
{ 
    public string Value { get; private set; } 

    public bool IsUsed { get; private set; } 

    public UsageIndicator(string value, bool isUsed) 
    { 
     Value = value; 
     IsUsed = isUsed; 
    } 

    public override string ToString() 
    { 
     return Value; 
    } 
} 

Тогда мы определим класс, представляющий как, как найти «маркер» в тексте и что делать, когда это было найдено

public class TokenOperation 
{ 
    public Regex Pattern { get; private set; } 

    public Func<string, string> Mutator { get; private set; } 

    public TokenOperation(string pattern, Func<string, string> mutator) 
    { 
     Pattern = new Regex(pattern); 
     Mutator = mutator; 
    } 

    private List<UsageIndicator> ExtractRegions(string source, int index, int length, out int matchedIndex) 
    { 
     var result = new List<UsageIndicator>(); 
     var head = source.Substring(0, index); 
     matchedIndex = 0; 

     if (head.Length > 0) 
     { 
      result.Add(new UsageIndicator(head, false)); 
      matchedIndex = 1; 
     } 

     var body = source.Substring(index, length); 
     body = Mutator(body); 
     result.Add(new UsageIndicator(body, true)); 

     var tail = source.Substring(index + length); 

     if (tail.Length > 0) 
     { 
      result.Add(new UsageIndicator(tail, false)); 
     } 

     return result; 
    } 

    public void Match(List<UsageIndicator> source) 
    { 
     for (var i = 0; i < source.Count; ++i) 
     { 
      if (source[i].IsUsed) 
      { 
       continue; 
      } 

      var value = source[i]; 
      var match = Pattern.Match(value.Value); 

      if (match.Success) 
      { 
       int modifyIBy; 
       source.RemoveAt(i); 
       var regions = ExtractRegions(value.Value, match.Index, match.Length, out modifyIBy); 

       for (var j = 0; j < regions.Count; ++j) 
       { 
        source.Insert(i + j, regions[j]); 
       } 

       i += modifyIBy; 
      } 
     } 
    } 
} 

После заботиться о тех вещах, положить что-то вместе, чтобы сделать замена довольно проста

public class Rewriter 
{ 
    private readonly List<TokenOperation> _definitions = new List<TokenOperation>(); 

    public void AddPattern(string pattern, Func<string, string> mutator) 
    { 
     _definitions.Add(new TokenOperation(pattern, mutator)); 
    } 

    public void AddLiteral(string pattern, string replacement) 
    { 
     AddPattern(Regex.Escape(pattern), x => replacement); 
    } 

    public string Rewrite(string value) 
    { 
     var workingValue = new List<UsageIndicator> { new UsageIndicator(value, false) }; 

     foreach (var definition in _definitions) 
     { 
      definition.Match(workingValue); 
     } 

     return string.Join("", workingValue); 
    } 
} 

в демо-код (ниже), имейте в виду, что порядок, в котором добавляются шаблон или буквенные выражения важно. Сначала добавляются то, что добавлено в первую очередь, поэтому, чтобы предотвратить появление в URL-адресе :// в качестве смайлика и косой черты, сначала обрабатываем блок изображения, так как он будет содержать URL-адрес между тегами и быть отмеченным как используется до того, как правило смайликов может попытаться его получить.

class Program 
{ 
    static void Main(string[] args) 
    { 
     var rewriter = new Rewriter(); 
     rewriter.AddPattern(@"\[img\].*?\[/img\]", x => x.Replace("[img]", "<img src=\"").Replace("[/img]", "\"/>")); 
     rewriter.AddLiteral(":/", "<img src=\"emote-sigh.png\"/>"); 
     rewriter.AddLiteral(":(", "<img src=\"emote-frown.png\"/>"); 
     rewriter.AddLiteral(":P", "<img src=\"emote-tongue.png\"/>"); 

     const string str = "Stacks be [img]http://example.com/overflowing.png[/img] :/"; 
     Console.WriteLine(rewriter.Rewrite(str)); 
    } 
} 

Пробные отпечатки:

Stacks be <img src="http://example.com/overflowing.png"/> <img src="emote-sigh.png"/> 
0

вы можете заменить, как показано ниже

string.replace(string.replace("[img]","<img src=\""),"[/img]","\"/>") 

он должен работать.

+3

Вы заменили одно выражение RegEx на OP двумя операциями 'String.Replace', и у него все еще будут проблемы с заменой смайликов. –

3

Unfortunently, с числом замен я планирую сделать, это кажется impracticle, чтобы попытаться сделать все это в одном Regex выражении (хотя я бы себе представить, что было бы самым производительным решением). Что такое (более медленный, но) более удобный способ сделать это?

Может показаться, но нет. Взгляните на this article.

tl; dr: Replace принимает делегата в качестве второго аргумента. Таким образом, сопоставление по шаблону, являющемуся дизъюнкцией всех разных вещей, которые вы хотите одновременно заменить, и в делетете использовать Dictionary или switch или аналогичную стратегию для выбора правильной замены текущего элемента.

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

3

Наиболее очевидным подходом было бы использовать регулярное выражение для замены любого текста, который вам нужен. Короче говоря, вы можете использовать регулярное выражение так: :/[^/] для соответствия :/, но не ://.

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

1

Если вы не хотите использовать какое-либо сложное Regex, чем вы можете, например.разделите текст на любой контейнер.

Вы должны разделить на основе токенов, найденных в тексте: в вашем случае токен представляет собой текст между [img] [/img] (включая теги [img]), то есть [img]http://example.com/overflowing.png[/img].

Затем вы можете применить метод замены [img] на эти жетоны и смайлики, заменив метод на остальные элементы в вышеупомянутом контейнере. Затем вы просто выводите строку, содержащую все элементы контейнера.

Ниже вы заполняете найти пример содержимого такого контейнера после процедуры разделенного:

1. "Stacks be " 
2. "[img]http://example.com/overflowing.png[/img]" 
3. " :/" 

Для элементов 1 & 3 применить смайлик заменить и в случае маркера элемента № 2 применить [img] заменить.

0

Вот фрагмент код из моего старого проекта:

private string Emoticonize(string originalStr) 
{ 
    StringBuilder RegExString = new StringBuilder(@"(?<=^|\s)(?:"); 
    foreach (KeyValuePair<string, string> e in Emoticons) 
    { 
     RegExString.Append(Regex.Escape(e.Key) + "|"); 
    } 
    RegExString.Replace("|", ")", RegExString.Length - 1, 1); 
    RegExString.Append(@"(?=$|\s)"); 
    MatchCollection EmoticonsMatches = Regex.Matches(originalStr, RegExString.ToString()); 

    RegExString.Clear(); 
    RegExString.Append(originalStr); 
    for (int i = EmoticonsMatches.Count - 1; i >= 0; i--) 
    { 
     RegExString.Replace(EmoticonsMatches[i].Value, Emoticons[EmoticonsMatches[i].Value], EmoticonsMatches[i].Index, EmoticonsMatches[i].Length); 
    } 

    return RegExString.ToString(); 
} 

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

0
 string[] emots = { ":/", ":(", ":)" }; 
     string[] emotFiles = { "emote-sigh", "emot-sad.png", "emot-happy.png" }; 

     string replaceEmots(string val) 
     { 
      string res = val; 
      for (int i = 0; i < emots.Length; i++) 
       res = res.Replace(emots[i], "<img src=\"" + emotFiles[i] + ".png\"/>"); 
      return res; 
     } 

     void button1_click() 
     { 
      string str = "Stacks be <img src=\"http://example.com/overflowing.png\"/> :/"; 
      str = replaceEmots(str); 
     } 
0

Вот код, который сделал замену в моем случае. И результат - именно то, что вы хотите.

str = "Stacks be <img src=\"http://example.com/overflowing.png\"/> :/"; 


     // check if the htmltemplate hold any template then set it or else hide the div data. 
     if (!String.IsNullOrEmpty(str)) 
     { 
      divStaticAsset.InnerHtml = str.Replace("[img]", "<img src=\'"). 
                Replace("[/img]", "\'/>") + "<img src=\'emote-sigh.png'/>"; 

     } 
+1

Это выведет 'Stacks be :/', отметив, что ': /' присутствует перед вторым '' тегом – mlorbetske

+0

Затем просто добавьте:/внутри этого Заменить ("[/ img]", "\ '/> «) –

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