有没有替代string。replace是不区分大小写?
我需要search一个string,并用从数据库中提取的值replace所有出现的%FirstName%
和%PolicyAmount%
。 问题是名字的大小不一。 这阻止了我使用String.Replace()
方法。 我已经看到有关这个主题的网页
Regex.Replace(strInput, strToken, strReplaceWith, RegexOptions.IgnoreCase);
但是,由于某种原因,当我尝试用$0
replace%PolicyAmount%
,replace永远不会发生。 我认为这与美元符号是正则expression式中的保留字符有关。
有没有另一种方法,我可以使用,不涉及消毒input处理正则expression式特殊字符?
来自MSDN
$ 0 – “replace组号码(十进制)匹配的最后一个子string。”
在.NET正则expression式组0始终是整个匹配。 对于文字$你需要
string value = Regex.Replace("%PolicyAmount%", "%PolicyAmount%", @"$$0", RegexOptions.IgnoreCase);
看起来像string.Replace 应该有一个重载,需要一个StringComparison参数。 既然没有,你可以尝试这样的事情:
public static string ReplaceString(string str, string oldValue, string newValue, StringComparison comparison) { StringBuilder sb = new StringBuilder(); int previousIndex = 0; int index = str.IndexOf(oldValue, comparison); while (index != -1) { sb.Append(str.Substring(previousIndex, index - previousIndex)); sb.Append(newValue); index += oldValue.Length; previousIndex = index; index = str.IndexOf(oldValue, index, comparison); } sb.Append(str.Substring(previousIndex)); return sb.ToString(); }
这是一个扩展方法。 不知道我在哪里find它。
public static class StringExtensions { public static string Replace(this string originalString, string oldValue, string newValue, StringComparison comparisonType) { int startIndex = 0; while (true) { startIndex = originalString.IndexOf(oldValue, startIndex, comparisonType); if (startIndex == -1) break; originalString = originalString.Substring(0, startIndex) + newValue + originalString.Substring(startIndex + oldValue.Length); startIndex += newValue.Length; } return originalString; } }
似乎最简单的方法就是简单地使用.Net自带的Replace方法,自从.Net 1.0开始,
string res = Microsoft.VisualBasic.Strings.Replace(res, "%PolicyAmount%", "$0", Compare: Microsoft.VisualBasic.CompareMethod.Text);
为了使用这种方法,你必须添加一个引用到Microsoft.VisualBasic集合。 这个程序集是.Net运行时的标准部分,它不是一个额外的下载或标记为过时的。
这种混淆的答案组,部分原因在于问题的标题实际上比被问到的具体问题要大得多。 读过之后,我不确定任何答案是从这里吸收所有好东西的一些编辑,所以我想我会试着总结一下。
这里有一个扩展方法,我认为这个方法避免了这里提到的陷阱,并提供了最广泛适用的解决scheme。
public static string ReplaceCaseInsensitiveFind(this string str, string findMe, string newValue) { return Regex.Replace(str, Regex.Escape(findMe), Regex.Replace(newValue, "\\$[0-9]+", @"$$$0"), RegexOptions.IgnoreCase); }
所以…
- 这是@MarkRobinson 的扩展方法
- 这不会试图跳过正则expression式 @Helge(如果你想在正则expression式之外进行string嗅探,你必须逐字节地执行)
- 通过@MichaelLiu的优秀testing用例
"œ".ReplaceCaseInsensitiveFind("oe", "")
,虽然他可能有一个稍微不同的行为。
不幸的是, @HA评论说你必须Escape
所有三个是不正确的 。 初始值和newValue
不需要。
注意:但是, 如果它们是“捕获值”标记的一部分,则必须在要插入的新值中转义$
s。 这样Regex中的三个美元符号就代替了Regex.Replace [原文如此]。 没有这个,这样的事情打破了…
"This is HIS fork, hIs spoon, hissssssss knife.".ReplaceCaseInsensitiveFind("his", @"he$0r")
这是错误:
An unhandled exception of type 'System.ArgumentException' occurred in System.dll Additional information: parsing "The\hisr\ is\ he\HISr\ fork,\ he\hIsr\ spoon,\ he\hisrsssssss\ knife\." - Unrecognized escape sequence \h.
告诉你什么,我知道对正则expression式感到舒适的人觉得他们的使用避免了错误,但我仍然偏向于字节嗅探string(但只有在阅读Spolsky编码之后 )才能确定你得到了你用于重要的用例。 让我想起克罗克福德一点“ 不安全的正则expression式 ”。 我们经常写正则expression式来允许我们想要的东西(如果我们是幸运的),但是无意中允许更多的东西(例如, $10
真的是我的newValue正则expression式中有效的“捕获值”string?),因为我们没有体贴足够。 两种方法都有价值,并且都鼓励不同types的无意的错误。 低估复杂性往往是容易的。
这怪异的$
逃避(和Regex.Escape
没有逃脱捕获的价值模式,如$0
如我所期望的替代值)使我疯狂了一段时间。 编程很难(c)1842年
/// <summary> /// A case insenstive replace function. /// </summary> /// <param name="originalString">The string to examine.(HayStack)</param> /// <param name="oldValue">The value to replace.(Needle)</param> /// <param name="newValue">The new value to be inserted</param> /// <returns>A string</returns> public static string CaseInsenstiveReplace(string originalString, string oldValue, string newValue) { Regex regEx = new Regex(oldValue, RegexOptions.IgnoreCase | RegexOptions.Multiline); return regEx.Replace(originalString, newValue); }
受到cfeduke的回答的启发,我做了使用IndexOf的函数来查找string中的旧值,然后用新值replace它。 我在一个处理数百万行的SSIS脚本中使用了这个,而正则expression式方法比这个慢得多。
public static string ReplaceCaseInsensitive(this string str, string oldValue, string newValue) { int prevPos = 0; string retval = str; // find the first occurence of oldValue int pos = retval.IndexOf(oldValue, StringComparison.InvariantCultureIgnoreCase); while (pos > -1) { // remove oldValue from the string retval = retval.Remove(pos, oldValue.Length); // insert newValue in it's place retval = retval.Insert(pos, newValue); // check if oldValue is found further down prevPos = pos + newValue.Length; pos = retval.IndexOf(oldValue, prevPos, StringComparison.InvariantCultureIgnoreCase); } return retval; }
通过将他的代码变成一个扩展默认的Replace
方法的扩展,扩展了C. Dragon 76的stream行答案。
public static class StringExtensions { public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison) { StringBuilder sb = new StringBuilder(); int previousIndex = 0; int index = str.IndexOf(oldValue, comparison); while (index != -1) { sb.Append(str.Substring(previousIndex, index - previousIndex)); sb.Append(newValue); index += oldValue.Length; previousIndex = index; index = str.IndexOf(oldValue, index, comparison); } sb.Append(str.Substring(previousIndex)); return sb.ToString(); } }
根据Jeff Reddy的回答,经过一些优化和validation:
public static string Replace(string str, string oldValue, string newValue, StringComparison comparison) { if (oldValue == null) throw new ArgumentNullException("oldValue"); if (oldValue.Length == 0) throw new ArgumentException("String cannot be of zero length.", "oldValue"); StringBuilder sb = null; int startIndex = 0; int foundIndex = str.IndexOf(oldValue, comparison); while (foundIndex != -1) { if (sb == null) sb = new StringBuilder(str.Length + (newValue != null ? Math.Max(0, 5 * (newValue.Length - oldValue.Length)) : 0)); sb.Append(str, startIndex, foundIndex - startIndex); sb.Append(newValue); startIndex = foundIndex + oldValue.Length; foundIndex = str.IndexOf(oldValue, startIndex, comparison); } if (startIndex == 0) return str; sb.Append(str, startIndex, str.Length - startIndex); return sb.ToString(); }
一个类似于C. Dragon的版本,但是如果你只需要一个replace:
int n = myText.IndexOf(oldValue, System.StringComparison.InvariantCultureIgnoreCase); if (n >= 0) { myText = myText.Substring(0, n) + newValue + myText.Substring(n + oldValue.Length); }
这是执行Regexreplace的另一个选项,因为没有多less人似乎注意到匹配包含string中的位置:
public static string ReplaceCaseInsensative( this string s, string oldValue, string newValue ) { var sb = new StringBuilder(s); int offset = oldValue.Length - newValue.Length; int matchNo = 0; foreach (Match match in Regex.Matches(s, Regex.Escape(oldValue), RegexOptions.IgnoreCase)) { sb.Remove(match.Index - (offset * matchNo), match.Length).Insert(match.Index - (offset * matchNo), newValue); matchNo++; } return sb.ToString(); }
Regex.Replace(strInput, strToken.Replace("$", "[$]"), strReplaceWith, RegexOptions.IgnoreCase);
正则expression式方法应该工作。 然而,你也可以做的是从数据库中小写string,小写你的%variables%,然后从数据库中find下面的string中的位置和长度。 请记住,string中的位置不会因为下方的位置而改变。
然后使用一个反向的循环(如果你不这样做,你将不得不保留一个运行的数据),从数据库中的非小写string中移除%variables%长度并插入replace值。