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

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

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

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


当前回答

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

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

StringBuilder比string mangling好得多。

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

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

[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]

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

我的代码类似于其他答案,但我的扭曲是,我在创建StringBuilder之前寻找匹配。如果没有发现,则避免潜在的大分配。然后代码就变成了do{…}while而不是a 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();
    }

其他回答

看起来像绳子。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();
}

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

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

}

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

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

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

StringBuilder比string mangling好得多。

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

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

[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]

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

我的代码类似于其他答案,但我的扭曲是,我在创建StringBuilder之前寻找匹配。如果没有发现,则避免潜在的大分配。然后代码就变成了do{…}while而不是a 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_Sys tem_StringComparison_