多个String.Replace没有干扰?

时间:2013-10-08 05:53:56

标签: c# .net regex replace

在不替换已替换的文本的情况下,执行多个String.Replace的谨慎方法是什么?例如,假设我有这个字符串:

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

我写的正则表达式将匹配[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\"/>";

实际(并且显而易见)结果

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

不幸的是,由于我计划进行替换的数量,尝试在单个Regex表达式中完成所有操作似乎是不切实际的(尽管我认为这将是最高性能的解决方案)。什么是(较慢但更易于维护)的方法呢?

8 个答案:

答案 0 :(得分:3)

  

不幸的是,由于我计划进行替换的数量,尝试在单个Regex表达式中完成所有操作似乎是不切实际的(尽管我认为这将是最高性能的解决方案)。什么是(较慢但更易于维护)的方法呢?

可能看起来如此,但事实并非如此。看看this article

tl; dr:Replace接受委托作为其第二个参数。因此匹配一个模式,该模式是您要同时替换的所有不同事物的分离,并且在委托中使用Dictionaryswitch或类似策略来选择当前的正确替换元件。

本文中的策略取决于键是静态字符串;如果密钥中有正则表达式运算符,则概念失败。有一种更好的方法,通过将键包装在捕获括号中,您可以测试是否存在适当的捕获组以查看哪个匹配匹配。

答案 1 :(得分:3)

最明显的方法是使用正则表达式替换您需要的任何文本。简而言之,你可以像这样使用正则表达式::/[^/]来匹配:/而不是://

您还可以使用群组来了解您匹配的模式,从而让您知道要放置的内容。

答案 2 :(得分: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。标记并在表情符号规则尝试获取之前标记为已使用。

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

答案 3 :(得分:1)

如果您不想使用任何复杂的正则表达式,例如:将文本分成任何类型的容器。

您应该根据文本中的标记进行拆分:在您的情况下,标记是[img] [/img](包括那些[img]标记)之间的文本,即[img]http://example.com/overflowing.png[/img]

然后,您可以对这些令牌应用[img]替换方法,并在上述容器中的其余元素上替换方法。然后你只输出一个包含所有容器元素的字符串。

在下面填写拆分程序后,查找此类容器的示例内容:

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

元素1&amp; 3您应用表情符号替换,如果是2号令牌元素,则应用[img]替换。

答案 4 :(得分:0)

你可以像下面那样替换

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

它应该有用。

答案 5 :(得分: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();
}

表情符号是一个字典,我将表情符号代码作为键存储,并将相应的图像存储为值。

答案 6 :(得分: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);
        }

答案 7 :(得分: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'/>";

        }