有没有字符串的替代品。替换为不区分大小写?

我需要搜索一个字符串,并将所有出现的%FirstName%%PolicyAmount%替换为从数据库中提取的值。问题是FirstName的大小写不同。这阻止了我使用String.Replace()方法。我看过相关网页,上面写着

Regex.Replace(strInput, strToken, strReplaceWith, RegexOptions.IgnoreCase);

然而,由于某种原因,当我尝试用$0替换%PolicyAmount%时,替换从未发生。我假设这与美元符号在正则表达式中是保留字符有关。

是否有另一种方法,我可以使用,不涉及消毒输入处理正则表达式特殊字符?

127323 次浏览
Regex.Replace(strInput, strToken.Replace("$", "[$]"), strReplaceWith, RegexOptions.IgnoreCase);

正则表达式方法应该可以工作。但是,您还可以将数据库中的字符串小写,将您拥有的%variables%小写,然后从数据库中定位小写字符串中的位置和长度。记住,字符串中的位置不会因为小写而改变。

然后使用一个反向循环(它更容易,如果你不这样做,你将不得不保持一个运行的计数,后来的点移动到哪里)从你的非小写字符串从数据库中删除%变量%的位置和长度,并插入替换值。

< p > 从MSDN
$0 - "替换组号number(十进制)匹配的最后一个子字符串。"< / p >

在。net正则表达式中,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();
}

似乎最简单的方法就是使用。net中附带的Replace方法,它从。net 1.0开始就存在了:

string res = Microsoft.VisualBasic.Strings.Replace(res,
"%PolicyAmount%",
"$0",
Compare: Microsoft.VisualBasic.CompareMethod.Text);

为了使用此方法,您必须添加一个Reference到Microsoft。VisualBasic组装。这个程序集是. net运行时的标准部分,它不是额外下载的,也不是被标记为过时的。

一个类似C. Dragon的版本,但如果你只需要一个替换:

int n = myText.IndexOf(oldValue, System.StringComparison.InvariantCultureIgnoreCase);
if (n >= 0)
{
myText = myText.Substring(0, n)
+ newValue
+ myText.Substring(n + oldValue.Length);
}

这是一个扩展方法。不知道在哪里找到的。

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;
}


}
    /// <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);
}

这是一组令人困惑的答案,部分原因是问题的标题实际上比被问到的具体问题大。在读完之后,我不确定是否有任何答案与吸收这里所有的好东西有几次编辑之差,所以我想我应该试着总结一下。

下面是一种扩展方法,我认为它避免了这里提到的陷阱,并提供了最广泛适用的解决方案。

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);
}

所以…

不幸的是,@HA的评论,你必须Escape这三个是不正确的。初始值和newValue不需要是。

然而,你必须在你插入的新值中转义$s。因此正则表达式中有三个美元符号。在正则表达式中替换。取代(原文如此)。没有它,像这样的东西就会碎…

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

告诉你什么,我知道那些喜欢Regex的人觉得他们的使用避免了错误,但我仍然经常倾向于字节嗅探字符串(但只有在阅读编码的Spolsky之后),以绝对确保你得到了你想要的重要用例。让我想起了《不安全的正则表达式》里的Crockford。我们经常编写允许我们想要的(如果我们幸运的话)的regexp,但无意中允许更多的(例如,$10在我的newValue regexp中真的是一个有效的“捕获值”字符串吗?),因为我们不够考虑。这两种方法都有价值,并且都鼓励不同类型的无意错误。我们很容易低估复杂性。

奇怪的$转义(以及Regex.Escape没有像我在替换值中所期望的那样转义$0这样的捕获值模式)让我抓狂了一段时间。编程难(c) 1842年

下面是执行Regex替换的另一个选项,因为很多人似乎没有注意到匹配包含字符串中的位置:

    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();
}

受到cfeduke答案的启发,我做了这个函数,它使用IndexOf来查找字符串中的旧值,然后用新值替换它。我在处理数百万行的SSIS脚本中使用了这个方法,regex方法要比这个慢得多。

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;
}

基于Jeff Reddy的回答,并进行了一些优化和验证:

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.龙76的流行答案,将他的代码变成一个扩展,重载默认的Replace方法。

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();
}
}

(因为每个人都在尝试这一点)。这是我的版本(null检查,正确的输入和替换转义)**灵感来自互联网和其他版本:

using System;
using System.Text.RegularExpressions;


public static class MyExtensions {
public static string ReplaceIgnoreCase(this string search, string find, string replace) {
return Regex.Replace(search ?? "", Regex.Escape(find ?? ""), (replace ?? "").Replace("$", "$$"), RegexOptions.IgnoreCase);
}
}

用法:

var result = "This is a test".ReplaceIgnoreCase("IS", "was");

先让我解释清楚,然后你可以把我撕成碎片。

Regex并不是这个问题的答案——相对来说,它太慢,内存太大。

StringBuilder比string mangling好得多。

由于这将是一个补充string.Replace的扩展方法,我认为匹配其工作方式很重要——因此,对于相同的参数问题抛出异常非常重要,如果没有进行替换则返回原始字符串。

我认为有一个StringComparison参数不是一个好主意。 我确实尝试过,但是michael-liu最初提到的测试用例显示了一个问题:-

[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]
虽然IndexOf将匹配,但源字符串(1)和oldValue中的匹配长度不匹配。长度(2).这通过在一些其他解决方案中导致IndexOutOfRange来体现。长度被添加到当前的匹配位置,我找不到绕过这个方法。 Regex无论如何都无法匹配这种情况,所以我采取了实用的解决方案,只使用StringComparison.OrdinalIgnoreCase作为我的解决方案

我的代码类似于其他答案,但我的扭曲是,我在创建StringBuilder之前寻找匹配。如果没有发现,则避免潜在的大分配。这样代码就变成了do{...}while而不是while{...}

我已经针对其他答案做了一些广泛的测试,这个答案的速度略快,使用的内存也略少。

    public static string ReplaceCaseInsensitive(this string str, string oldValue, string newValue)
{
if (str == null) throw new ArgumentNullException(nameof(str));
if (oldValue == null) throw new ArgumentNullException(nameof(oldValue));
if (oldValue.Length == 0) throw new ArgumentException("String cannot be of zero length.", nameof(oldValue));


var position = str.IndexOf(oldValue, 0, StringComparison.OrdinalIgnoreCase);
if (position == -1) return str;


var sb = new StringBuilder(str.Length);


var lastPosition = 0;


do
{
sb.Append(str, lastPosition, position - lastPosition);


sb.Append(newValue);


} while ((position = str.IndexOf(oldValue, lastPosition = position + oldValue.Length, StringComparison.OrdinalIgnoreCase)) != -1);


sb.Append(str, lastPosition, str.Length - lastPosition);


return sb.ToString();
}

从。net Core 2.0或。net Standard 2.1开始,这被烘焙到。net运行时[1]中:

"hello world".Replace("World", "csharp", StringComparison.CurrentCultureIgnoreCase); // "hello csharp"

[1] https://learn.microsoft.com/en-us/dotnet/api/system.string.replace#System_String_Replace_System_String_System_String_System_StringComparison_