ChatGPT解决这个技术问题 Extra ChatGPT

String.Replace 忽略大小写

我有一个名为“hello world”的字符串

我需要将单词“world”替换为“csharp”

为此,我使用:

string.Replace("World", "csharp");

但结果,我没有替换字符串。原因是区分大小写。原始字符串包含“world”,而我试图替换“World”。

有什么办法可以避免在 string.Replace 方法中区分大小写?

很遗憾微软没有在框架中实现这样的基本功能!
对于 .NET 5 和最新版本的 .NET Core,只需使用 stackoverflow.com/a/64677285 中所述的采用 StringComparison 的 string.Replace 的重载

i
iliketocode

您可以使用 Regex 并执行不区分大小写的替换:

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

不适用于正则表达式语言元素,因此它不是通用方法。史蒂夫 B 的回答是正确的。
所以你最好不要写 hello. world? 或任何包含正则表达式运算符的东西。
以防万一有人不想进一步阅读,这是 2011 年公认的答案,并且拥有大量选票。如果您只需要替换字母数字,这可以正常工作。但是,如果您必须替换任何标点符号,您可能会遇到大麻烦。 Oleg Zarevennyi 的回答更胜一筹,但因为它是在 2017 年发布的,所以只有少量选票。
替换为 $$ 不起作用。如果您的替换可以包含 $$ 则使用 Oleg 的解决方案。
正则表达式太复杂了。这真的是最糟糕的。刚开始的学习曲线就很高,而真正擅长它的学习曲线要高得多。所以大多数人只是去互联网复制/粘贴经过良好测试的正则表达式。即便如此,它们通常也不完美。尝试为电子邮件寻找完美的正则表达式,您就会明白我的意思。它有它的用途,特别是在声明性上下文中,例如不能使用过程逻辑的 IIS 重写规则。但是我在 c# 中对它的使用绝对为零,在那里我可以编写完全可读和可维护的代码。
S
Steve B
var search = "world";
var replacement = "csharp";
string result = Regex.Replace(
    stringToLookInto,
    Regex.Escape(search), 
    replacement.Replace("$","$$"), 
    RegexOptions.IgnoreCase
);

如果您依赖可以包含 Regex language elements 的用户输入,则 Regex.Escape 很有用

更新

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

Here is a small fiddle that tests the code

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

它的输出是:

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 }

如果 replacement = "!@#$%^&*()" 你得到 "!@\#\$%\^&*()" 替换,则此方法失败。
第二个 Regex.Escape 不好,它会在特殊字符前加上反斜杠。似乎最好的方法是 .Replace("$", "$$"),这有点愚蠢(stackoverflow.com/a/10078353)。
@dannyTuppeny:你是对的......我相应地更新了答案
O
Oleg Zarevennyi

比其他正则表达式方法快 2.5 倍和最有效的方法:

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

}

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

这种方法的优点:

CPU和内存效率高;

它是最快的解决方案,比其他使用正则表达式的方法快2.5倍(最后证明);

适合从输入字符串中移除部分(设置 newValue 为 null),为此进行了优化;

与原始 .NET C# 字符串相同。替换行为,相同的异常;

评论很好,容易理解;

更简单——没有正则表达式。正则表达式总是比较慢,因为它们的多功能性(即使是编译的);

这种方法经过了很好的测试,并且在其他解决方案中没有像无限循环这样的隐藏缺陷,甚至得到了很高的评价:

@AsValeO:不适用于正则表达式语言元素,所以它不是通用方法 @Mike Stillion:这段代码有问题。如果新文本是旧文本的超集,则会产生无限循环。

基准测试:此解决方案比@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();

最初的想法——@Darky711;感谢@MinerR 的StringBuilder


我敢打赌,您可以使用 StringBuilder 而不是字符串来加快速度。
@MineR你是对的,我最初只是更新了没有无限循环的@Darky711解决方案,所以我使用了String。但是,StringBuilder 确实比 String30-40%。我已经更新了解决方案。谢谢 ;)
有趣的方法。当性能很重要时,可能是更好的(比我的更好:))。通常是一种添加到公共共享代码库的方法。
'nameof' 表达式的使用使得这仅对 C# 6.0 及更高版本有效。如果你在 VS2013 中,你可以通过简单地删除异常中的操作数来使用它。
调用 replace("abc","abc\u00AD","def",StringComparison.CurrentCulture) 时出现错误,预期结果为“def”(\u00AD 是软连字符 - 测试用例取自 .net 核心字符串替换github.com/dotnet/runtime/blob/… 处的测试用例)。解决方法是将“if (startSearchFromIndex == str.Length)”更改为“if (startSearchFromIndex >= str.Length)”
A
Adaptabi

很多使用正则表达式的建议。如果没有它,这个扩展方法怎么样:

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

请注意,比较参数不用于进行实际替换(它始终不区分大小写)
这段代码有问题。如果新文本是旧文本的超集,则会产生无限循环。在 FoundAt 插入 new 后,FoundAt 的值需要增加 new 的长度。
comparison 参数应在 IndexOf 中使用,而不是 StringComparison.CurrentCultureIgnoreCase
@Bolo 我已经对其进行了编辑以使用比较参数(可能需要一些时间进行同行评审)。
我还将这个条件分开以返回新字符串:if(old.Equals(@new, comparison)) return @new;,因为新字符串的大写/小写可能不同。
P
Petrucio

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

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

转义让我们的生活少了些麻烦 :-) return Regex.Replace(input, Regex.Escape(search), replacement.Replace("$", "$$"), RegexOptions.IgnoreCase);
i
iliketocode

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

Replace(sourceString, "replacethis", "withthis", , , CompareMethod.Text)

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

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


B
Bolo

修改@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;
}

N
Nick

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

取自 here

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

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


G
Georgy Batalov

我写了扩展方法:

public static string ReplaceIgnoreCase(this string source, string oldVale, string newVale)
    {
        if (source.IsNullOrEmpty() || oldVale.IsNullOrEmpty())
            return source;

        var stringBuilder = new StringBuilder();
        string result = source;

        int index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);

        while (index >= 0)
        {
            if (index > 0)
                stringBuilder.Append(result.Substring(0, index));

            if (newVale.IsNullOrEmpty().IsNot())
                stringBuilder.Append(newVale);

            stringBuilder.Append(result.Substring(index + oldVale.Length));

            result = stringBuilder.ToString();

            index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);
        }

        return result;
    }

我对以前的扩展方法使用了两种额外的扩展方法:

    public static bool IsNullOrEmpty(this string value)
    {
        return string.IsNullOrEmpty(value);
    }

    public static bool IsNot(this bool val)
    {
        return val == false;
    }

赞成。但是 IsNot 太认真地对待扩展了 :)
令人失望的是,这并不适用于所有情况。我正在传递一个专有名称,它会附加到字符串长度为一百万个字符,然后内存不足
下面提供的替代方案解决了我的问题
我真的很喜欢.IsNot
T
Tom Robson

这不起作用:我无法更快或更容易地成像其他任何东西。

public static class ExtensionMethodsString
{
    public static string Replace(this String thisString, string oldValue, string newValue, StringComparison stringComparison)
    {
        string working = thisString;
        int index = working.IndexOf(oldValue, stringComparison);
        while (index != -1)
        {
            working = working.Remove(index, oldValue.Length);
            working = working.Insert(index, newValue);
            index = index + newValue.Length;
            index = working.IndexOf(oldValue, index, stringComparison);
        }
        return working;
    }
}

我不知道它是否更快,但它很简洁,不使用正则表达式开销和潜在问题,并使用内置的 StringComparison。
C
Community

在搜索字符串上使用 Regex.Escape 扩展 Petrucio 的答案,并按照 Steve B 的答案中的建议转义匹配的组(以及对我的口味的一些小改动):

public static class StringExtensions
{
    public static string ReplaceIgnoreCase(this string str, string from, string to)
    {
        return Regex.Replace(str, Regex.Escape(from), to.Replace("$", "$$"), RegexOptions.IgnoreCase);
    }
}

这将产生以下预期结果:

Console.WriteLine("(heLLo) wOrld".ReplaceIgnoreCase("(hello) world", "Hi $1 Universe")); // Hi $1 Universe
Console.WriteLine("heLLo wOrld".ReplaceIgnoreCase("(hello) world", "Hi $1 Universe"));   // heLLo wOrld

但是,如果不执行转义,您会得到以下结果,这不是 String.Replace 的预期行为,它只是不区分大小写:

Console.WriteLine("(heLLo) wOrld".ReplaceIgnoreCase_NoEscaping("(hello) world", "Hi $1 Universe")); // (heLLo) wOrld
Console.WriteLine("heLLo wOrld".ReplaceIgnoreCase_NoEscaping("(hello) world", "Hi $1 Universe"));   // Hi heLLo Universe

B
Bbb

使用@Georgy Batalov 解决方案我在使用以下示例时遇到了问题

字符串原始 = "blah,DC=bleh,DC=blih,DC=bloh,DC=com";字符串替换 = original.ReplaceIgnoreCase(",DC=", ".")

下面是我如何重写他的扩展

public static string ReplaceIgnoreCase(this string source, string oldVale, 
string newVale)
    {
        if (source.IsNullOrEmpty() || oldVale.IsNullOrEmpty())
            return source;

        var stringBuilder = new StringBuilder();
        string result = source;

        int index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);
        bool initialRun = true;

        while (index >= 0)
        {
            string substr = result.Substring(0, index);
            substr = substr + newVale;
            result = result.Remove(0, index);
            result = result.Remove(0, oldVale.Length);

            stringBuilder.Append(substr);

            index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);
        }

        if (result.Length > 0)
        {
            stringBuilder.Append(result);
        }

        return stringBuilder.ToString();
    }

H
Honey22Sharp

我的这个方法可以忽略大小写以及只选择整个单词

public static string Replace(this string s, string word, string by, StringComparison stringComparison, bool WholeWord)
{
    s = s + " ";
    int wordSt;
    StringBuilder sb = new StringBuilder();
    while (s.IndexOf(word, stringComparison) > -1)
    {
        wordSt = s.IndexOf(word, stringComparison);
        if (!WholeWord || ((wordSt == 0 || !Char.IsLetterOrDigit(char.Parse(s.Substring(wordSt - 1, 1)))) && !Char.IsLetterOrDigit(char.Parse(s.Substring(wordSt + word.Length, 1)))))
        {
            sb.Append(s.Substring(0, wordSt) + by);
        }
        else
        {
            sb.Append(s.Substring(0, wordSt + word.Length));
        }
        s = s.Substring(wordSt + word.Length);
    }
    sb.Append(s);
    return sb.ToString().Substring(0, sb.Length - 1);
}

R
Ravikant Sonare

下面的函数是从字符串集中删除所有匹配词,如 (this)。作者:拉维坎特·索纳雷。

private static void myfun()
{
    string mystring = "thiTHISThiss This THIS THis tThishiThiss. Box";
    var regex = new Regex("this", RegexOptions.IgnoreCase);
    mystring = regex.Replace(mystring, "");
    string[] str = mystring.Split(' ');
    for (int i = 0; i < str.Length; i++)
    {
        if (regex.IsMatch(str[i].ToString()))
        {
            mystring = mystring.Replace(str[i].ToString(), string.Empty);

        }
    }
    Console.WriteLine(mystring);
}

这个函数是用 Ravikant Sonare 替换字符串集合中的所有字符串,
s
sud007

您也可以尝试 Regex 类。

var regex = new Regex( "camel", RegexOptions.IgnoreCase ); var newSentence = regex.Replace( sentence, "horse" );


F
Fabio Mendes Soares

使用这个,经过测试并且 100% 有效!

对于 VB.NET

Dim myString As String
Dim oldValue As String
Dim newValue As String

myString = Form1.TextBox1.Text
oldValue = TextBox1.Text
newValue = TextBox2.Text

Dim working As String = myString
Dim index As Integer = working.IndexOf(oldValue, StringComparison.CurrentCultureIgnoreCase)

While index <> -1
    working = working.Remove(index, oldValue.Length)
    working = working.Insert(index, newValue)
    index = index + newValue.Length
    index = working.IndexOf(oldValue, index, StringComparison.CurrentCultureIgnoreCase)
    Form1.TextBox1.Text = working
End While

对于 C#

private void Button2_Click(System.Object sender, System.EventArgs e)
{
    string myString;
    string oldValue;
    string newValue;

    myString = Form1.TextBox1.Text;
    oldValue = TextBox1.Text;
    newValue = TextBox2.Text;

    string working = myString;
    int index = working.IndexOf(oldValue, StringComparison.CurrentCultureIgnoreCase);

    while (index != -1)
    {
        working = working.Remove(index, oldValue.Length);
        working = working.Insert(index, newValue);
        index = index + newValue.Length;
        index = working.IndexOf(oldValue, index, StringComparison.CurrentCultureIgnoreCase);
        Form1.TextBox1.Text = working;
    }
}

G
Gama Sharma

另一种方法是使用选项 StringComparison.CurrentCultureIgnoreCase 忽略 String.Replace() 中的大小写敏感性

string.Replace("World", "csharp", StringComparison.CurrentCultureIgnoreCase)

H
Harshal

我更喜欢这个 - "Hello World".ToLower().Replace( "world", "csharp" );


这将小写所有内容,甚至是不应该替换的单词。
显然,只有在您不关心此案的情况下,您才能使用它。
我相信这是在大多数情况下简单使用 Replace 方法的最佳答案,尽管所有反对票。如果您正在做一个更复杂的边缘情况,那么您可能不应该从使用 Replace() 开始。