0

String.Replace在不替换已被替换的文本的情况下执行多重操作是一种谨慎的方法。例如,假设我有这个字符串:

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

我编写的正则表达式将匹配, 并让我用正确的 HTML格式[img]url[/img]替换它。<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\"/>";

不幸的是,随着我计划进行的替换数量,尝试在单个正则表达式中完成所有操作似乎是不切实际的(尽管我认为这将是最高效的解决方案)。什么是(较慢但)更易于维护的方式来做到这一点?

4

8 回答 8

3

不幸的是,随着我计划进行的替换数量,尝试在单个正则表达式中完成所有操作似乎是不切实际的(尽管我认为这将是最高效的解决方案)。什么是(较慢但)更易于维护的方式来做到这一点?

可能看起来如此,但事实并非如此。看看这篇文章

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

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

于 2013-10-08T06:00:35.347 回答
3

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

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

于 2013-10-08T06:00:58.340 回答
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"/>
于 2013-10-08T06:26:37.060 回答
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 和 3,您应用表情符号替换,如果是标记元素编号 2,则应用[img]替换。

于 2013-10-08T06:03:22.543 回答
0

你可以像下面这样替换

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

它应该工作。

于 2013-10-08T06:00:16.787 回答
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();
}

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

于 2013-10-08T06:16:50.643 回答
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);
        }
于 2013-10-08T06:22:44.227 回答
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'/>";

        }
于 2013-10-08T06:40:13.380 回答