String.Replace忽略大小写

San*_*eep 182 c# string

我有一个名为"你好世界"的字符串

我需要将"世界"一词改为"csharp"

为此,我使用:

string.Replace("World", "csharp");
Run Code Online (Sandbox Code Playgroud)

但结果,我没有更换字符串.原因是区分大小写.原始字符串包含"world",而我正在尝试替换"World".

有没有办法在string.Replace方法中避免这种区分大小写?

Dar*_*rov 276

您可以使用正则表达式并执行不区分大小写的替换:

class Program
{
    static void Main()
    {
        string input = "hello WoRlD";
        string result = 
           Regex.Replace(input, "world", "csharp", RegexOptions.IgnoreCase);
        Console.WriteLine(result); // prints "hello csharp"
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 不适用于**正则表达式语言元素**,因此它不是通用方法.Steve B的回答是正确的. (18认同)
  • 以防万一有人不愿意进一步阅读,这是 2011 年接受的答案,并且获得了大量选票。如果您只需要替换字母数字,这可以正常工作。但是,如果您必须替换任何标点符号,您可能会遇到大麻烦。Oleg Zarevennyi 的回答更胜一筹,但因为它是 2017 年发布的,所以只有很少的票数。 (10认同)

Ste*_*e B 98

var search = "world";
var replacement = "csharp";
string result = Regex.Replace(
    stringToLookInto,
    Regex.Escape(search), 
    replacement.Replace("$","$$"), 
    RegexOptions.IgnoreCase
);
Run Code Online (Sandbox Code Playgroud)

Regex.Escape如果依靠用户输入是有用的,其可以包含正则表达式语言元素

更新

感谢评论,您实际上不必转义替换字符串.

这是一个测试代码的小提琴:

using System;
using System.Text.RegularExpressions;           
public class Program
{
    public static void Main()
    {

        var tests = new[] {
            new { Input="abcdef", Search="abc", Replacement="xyz", Expected="xyzdef" },
            new { Input="ABCdef", Search="abc", Replacement="xyz", Expected="xyzdef" },
            new { Input="A*BCdef", Search="a*bc", Replacement="xyz", Expected="xyzdef" },
            new { Input="abcdef", Search="abc", Replacement="x*yz", Expected="x*yzdef" },       
            new { Input="abcdef", Search="abc", Replacement="$", Expected="$def" },
        };


        foreach(var test in tests){
            var result = ReplaceCaseInsensitive(test.Input, test.Search, test.Replacement);

            Console.WriteLine(
                "Success: {0}, Actual: {1}, {2}",
                result == test.Expected,
                result,
                test
            );

        }


    }

    private static string ReplaceCaseInsensitive(string input, string search, string replacement){
        string result = Regex.Replace(
            input,
            Regex.Escape(search), 
            replacement.Replace("$","$$"), 
            RegexOptions.IgnoreCase
        );
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

它的输出是:

Success: True, Actual: xyzdef, { Input = abcdef, Search = abc, Replacement = xyz, Expected = xyzdef } 
Success: True, Actual: xyzdef, { Input = ABCdef, Search = abc, Replacement = xyz, Expected = xyzdef }
Success: True, Actual: xyzdef, { Input = A*BCdef, Search = a*bc, Replacement = xyz, Expected = xyzdef } 
Success: True, Actual: x*yzdef, { Input = abcdef, Search = abc, Replacement = x*yz, Expected = x*yzdef} 
Success: True, Actual: $def, { Input = abcdef, Search = abc, Replacement = $, Expected = $def }
Run Code Online (Sandbox Code Playgroud)

  • 如果替换=“!@#$%^&*()”,此方法将失败,而将替换为“!@ \#\ $%\ ^&\ * \(\)”。 (2认同)
  • 第二个 `Regex.Escape` 不好,它会用反斜杠作为特殊字符的前缀。似乎最好的方法是 .Replace("$", "$$"),这有点愚蠢(http://stackoverflow.com/a/10078353)。 (2认同)
  • @dannyTuppeny:你是对的......我相应地更新了答案 (2认同)

Ole*_*nyi 35

2.5X比其他正则表达式方法更快,更有效的方法:

/// <summary>
/// Returns a new string in which all occurrences of a specified string in the current instance are replaced with another 
/// specified string according the type of search to use for the specified string.
/// </summary>
/// <param name="str">The string performing the replace method.</param>
/// <param name="oldValue">The string to be replaced.</param>
/// <param name="newValue">The string replace all occurrences of <paramref name="oldValue"/>. 
/// If value is equal to <c>null</c>, than all occurrences of <paramref name="oldValue"/> will be removed from the <paramref name="str"/>.</param>
/// <param name="comparisonType">One of the enumeration values that specifies the rules for the search.</param>
/// <returns>A string that is equivalent to the current string except that all instances of <paramref name="oldValue"/> are replaced with <paramref name="newValue"/>. 
/// If <paramref name="oldValue"/> is not found in the current instance, the method returns the current instance unchanged.</returns>
[DebuggerStepThrough]
public static string Replace(this string str,
    string oldValue, string @newValue,
    StringComparison comparisonType)
{

    // Check inputs.
    if (str == null)
    {
        // Same as original .NET C# string.Replace behavior.
        throw new ArgumentNullException(nameof(str));
    }
    if (str.Length == 0)
    {
        // Same as original .NET C# string.Replace behavior.
        return str;
    }
    if (oldValue == null)
    {
        // Same as original .NET C# string.Replace behavior.
        throw new ArgumentNullException(nameof(oldValue));
    }
    if (oldValue.Length == 0)
    {
        // Same as original .NET C# string.Replace behavior.
        throw new ArgumentException("String cannot be of zero length.");
    }


    //if (oldValue.Equals(newValue, comparisonType))
    //{
    //This condition has no sense
    //It will prevent method from replacesing: "Example", "ExAmPlE", "EXAMPLE" to "example"
    //return str;
    //}



    // Prepare string builder for storing the processed string.
    // Note: StringBuilder has a better performance than String by 30-40%.
    StringBuilder resultStringBuilder = new StringBuilder(str.Length);



    // Analyze the replacement: replace or remove.
    bool isReplacementNullOrEmpty = string.IsNullOrEmpty(@newValue);



    // Replace all values.
    const int valueNotFound = -1;
    int foundAt;
    int startSearchFromIndex = 0;
    while ((foundAt = str.IndexOf(oldValue, startSearchFromIndex, comparisonType)) != valueNotFound)
    {

        // Append all characters until the found replacement.
        int @charsUntilReplacment = foundAt - startSearchFromIndex;
        bool isNothingToAppend = @charsUntilReplacment == 0;
        if (!isNothingToAppend)
        {
            resultStringBuilder.Append(str, startSearchFromIndex, @charsUntilReplacment);
        }



        // Process the replacement.
        if (!isReplacementNullOrEmpty)
        {
            resultStringBuilder.Append(@newValue);
        }


        // Prepare start index for the next search.
        // This needed to prevent infinite loop, otherwise method always start search 
        // from the start of the string. For example: if an oldValue == "EXAMPLE", newValue == "example"
        // and comparisonType == "any ignore case" will conquer to replacing:
        // "EXAMPLE" to "example" to "example" to "example" … infinite loop.
        startSearchFromIndex = foundAt + oldValue.Length;
        if (startSearchFromIndex == str.Length)
        {
            // It is end of the input string: no more space for the next search.
            // The input string ends with a value that has already been replaced. 
            // Therefore, the string builder with the result is complete and no further action is required.
            return resultStringBuilder.ToString();
        }
    }


    // Append the last part to the result.
    int @charsUntilStringEnd = str.Length - startSearchFromIndex;
    resultStringBuilder.Append(str, startSearchFromIndex, @charsUntilStringEnd);


    return resultStringBuilder.ToString();

}
Run Code Online (Sandbox Code Playgroud)

注意:忽略case == StringComparison.OrdinalIgnoreCase作为参数StringComparison comparisonType.它是替换所有值的最快,不区分大小写的方法.


这种方法的优点:

  • 高CPU和MEMORY效率;
  • 它是最快的解决方案,比使用正则表达式的其他方法快2.5倍(最后证明);
  • 适合从输入字符串中删除部分(设置newValuenull),为此进行优化;
  • 与原始.NET C# string.Replace行为相同,相同的异常;
  • 评论很好,易于理解;
  • 更简单 - 没有正则表达式.正则表达式总是较慢,因为它们的多功能性(甚至编译);
  • 这种方法经过了充分测试,在其他解决方案中没有像无限循环这样的隐藏缺陷,甚至评价很高:

@AsValeO:不适用于Regex语言元素,因此它不是通用方法

@Mike Stillion:此代码存在问题.如果new中的文本是旧文本的超集,则会产生无限循环.


基准测试:此解决方案比@Steve B的正则表达式快2.59倍.代码:

// Results:
// 1/2. Regular expression solution: 4486 milliseconds
// 2/2. Current solution: 1727 milliseconds — 2.59X times FASTER! than regex!

// Notes: the test was started 5 times, the result is an average; release build.

const int benchmarkIterations = 1000000;
const string sourceString = "aaaaddsdsdsdsdsd";
const string oldValue = "D";
const string newValue = "Fod";
long totalLenght = 0;

Stopwatch regexStopwatch = Stopwatch.StartNew();
string tempString1;
for (int i = 0; i < benchmarkIterations; i++)
{
    tempString1 = sourceString;
    tempString1 = ReplaceCaseInsensitive(tempString1, oldValue, newValue);

    totalLenght = totalLenght + tempString1.Length;
}
regexStopwatch.Stop();



Stopwatch currentSolutionStopwatch = Stopwatch.StartNew();
string tempString2;
for (int i = 0; i < benchmarkIterations; i++)
{
    tempString2 = sourceString;
    tempString2 = tempString2.Replace(oldValue, newValue,
        StringComparison.OrdinalIgnoreCase);

    totalLenght = totalLenght + tempString2.Length;
}
currentSolutionStopwatch.Stop();
Run Code Online (Sandbox Code Playgroud)

最初的想法 - @ Darky711; 谢谢@MinerR StringBuilder.

  • 我敢打赌,你可以使用StringBuilder而不是字符串来更快地实现这一点. (5认同)
  • @MineR你是对的,我最初只是更新了@Darky711解决方案,没有无限循环,所以我使用了`String`。然而,“StringBuilder”确实比“String”快**30-40%**。我已经更新了解决方案。谢谢 ;) (2认同)
  • 有趣的方法。当性能很重要时,可能是更好的(比我的好:))。通常是一种添加到公共共享代码库的方法。 (2认同)
  • 使用'nameof'表达式仅对C#6.0及更高版本有效。如果您使用的是VS2013,则可以通过简单地删除异常中的操作数来使用它。 (2认同)

Pet*_*cio 28

扩展使我们的生活更轻松:

static public class StringExtensions
{
    static public string ReplaceInsensitive(this string str, string from, string to)
    {
        str = Regex.Replace(str, from, to, RegexOptions.IgnoreCase);
        return str;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 逃避使我们的生活变得越来越少:-) return Regex.Replace(input,Regex.Escape(search),replacement.Replace("$","$$"),RegexOptions.IgnoreCase); (9认同)

Dar*_*711 28

很多使用正则表达式的建议.没有它的这种扩展方法怎么样:

public static string Replace(this string str, string old, string @new, StringComparison comparison)
{
    @new = @new ?? "";
    if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(old) || old.Equals(@new, comparison))
        return str;
    int foundAt = 0;
    while ((foundAt = str.IndexOf(old, foundAt, comparison)) != -1)
    {
        str = str.Remove(foundAt, old.Length).Insert(foundAt, @new);
        foundAt += @new.Length;
    }
    return str;
}
Run Code Online (Sandbox Code Playgroud)

  • 写完唯一的非正则表达式答案做得好. (8认同)
  • 这段代码有问题.如果**new**中的文本是**old**中文本的超集,则可以产生无限循环.一旦在**FoundAt**中插入**new**,**FoundAt**的值需要按**new**的长度提前. (2认同)
  • 我也将这种条件分开以返回新字符串:`if(old.Equals(@new,comparison))return @new;`,因为新字符串的大小写可能不同。 (2认同)

小智 13

您可以使用Microsoft.VisualBasic命名空间来查找此帮助程序函数:

Replace(sourceString, "replacethis", "withthis", , , CompareMethod.Text)
Run Code Online (Sandbox Code Playgroud)

  • 在 .Net 4.7.2 中,您需要添加对 Microsoft.VisualBasic 的引用才能使其正常工作。在.Net Core中,Microsoft.VisualBasic.Strings类(无论如何在版本10.3.0中)似乎没有实现Replace函数。如果您首先添加类 -AssemblyName Microsoft.VisualBasic,这也适用于 Powershell。 (3认同)
  • 我对我的答案感到自豪,直到我看到这是一个更好的答案,因为它是内置的。例如: Strings.Replace("TeStInG123", "t", "z", 1, -1, CompareMethod.Text) returns "泽斯因G123" (2认同)
  • 警告,如果搜索的字符串是空字符串,则 Strings.Replace 返回 null。 (2认同)

ZZY*_*ZZY 6

.Net Core 内置了这种方法: Replace(String, String, StringComparison) Doc。现在我们可以简单地写: "...".Replace("oldValue", "newValue", StringComparison.OrdinalIgnoreCase)


Nic*_*ick 5

编辑:不知道“裸链接”问题,对此感到抱歉)

这里拍摄:

string myString = "find Me and replace ME";
string strReplace = "me";
myString = Regex.Replace(myString, "me", strReplace, RegexOptions.IgnoreCase);
Run Code Online (Sandbox Code Playgroud)

似乎您不是第一个抱怨缺少区分大小写的字符串的人。


Bol*_*olo 5

修改了@ Darky711的答案,使用传入的比较类型并尽可能地匹配框架替换命名和xml注释.

/// <summary>
/// Returns a new string in which all occurrences of a specified string in the current instance are replaced with another specified string.
/// </summary>
/// <param name="str">The string performing the replace method.</param>
/// <param name="oldValue">The string to be replaced.</param>
/// <param name="newValue">The string replace all occurrances of oldValue.</param>
/// <param name="comparisonType">Type of the comparison.</param>
/// <returns></returns>
public static string Replace(this string str, string oldValue, string @newValue, StringComparison comparisonType)
{
    @newValue = @newValue ?? string.Empty;
    if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(oldValue) || oldValue.Equals(@newValue, comparisonType))
    {
        return str;
    }
    int foundAt;
    while ((foundAt = str.IndexOf(oldValue, 0, comparisonType)) != -1)
    {
        str = str.Remove(foundAt, oldValue.Length).Insert(foundAt, @newValue);
    }
    return str;
}
Run Code Online (Sandbox Code Playgroud)