在不替换已替换的文本的情况下,执行多个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表达式中完成所有操作似乎是不切实际的(尽管我认为这将是最高性能的解决方案)。什么是(较慢但更易于维护)的方法呢?
答案 0 :(得分:3)
不幸的是,由于我计划进行替换的数量,尝试在单个Regex表达式中完成所有操作似乎是不切实际的(尽管我认为这将是最高性能的解决方案)。什么是(较慢但更易于维护)的方法呢?
可能看起来如此,但事实并非如此。看看this article。
tl; dr:Replace
接受委托作为其第二个参数。因此匹配一个模式,该模式是您要同时替换的所有不同事物的分离,并且在委托中使用Dictionary
或switch
或类似策略来选择当前的正确替换元件。
本文中的策略取决于键是静态字符串;如果密钥中有正则表达式运算符,则概念失败。有一种更好的方法,通过将键包装在捕获括号中,您可以测试是否存在适当的捕获组以查看哪个匹配匹配。
答案 1 :(得分:3)
最明显的方法是使用正则表达式替换您需要的任何文本。简而言之,你可以像这样使用正则表达式::/[^/]
来匹配:/
而不是://
。
您还可以使用群组来了解您匹配的模式,从而让您知道要放置的内容。
答案 2 :(得分:2)
另一种方法是使用一种修改后的Lexer来隔离文本中保证某个替换的每个离散区域,并标记该块以便不再在其中运行替换3 p>
以下是您如何做到这一点的示例:
首先,我们将创建一个表示是否使用特定字符串的类
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'/>";
}