如何在 String.replace 中忽略大小写

发布于 2024-11-08 11:13:27 字数 334 浏览 0 评论 0原文

string sentence = "We know it contains 'camel' word.";
// Camel can be in different cases:
string s1 = "CAMEL";
string s2 = "CaMEL";
string s3 = "CAMeL";
// ...
string s4 = "Camel";
// ...
string s5 = "camel";

尽管 string.Replace 不支持左侧字符串的 ignoreCase ,如何将句子中的“camel”替换为“horse”?

string sentence = "We know it contains 'camel' word.";
// Camel can be in different cases:
string s1 = "CAMEL";
string s2 = "CaMEL";
string s3 = "CAMeL";
// ...
string s4 = "Camel";
// ...
string s5 = "camel";

How to replace 'camel' in sentence with 'horse' despite of string.Replace doesn't support ignoreCase on left string?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

桃酥萝莉 2024-11-15 11:13:27

使用正则表达式:

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

当然,这也会匹配包含camel的单词,但不清楚您是否想要这样。

如果您需要精确匹配,您可以使用自定义 MatchEvaluator。

public static class Evaluators
{
    public static string Wrap( Match m, string original, string format )
    {
        // doesn't match the entire string, otherwise it is a match
        if (m.Length != original.Length)
        {
            // has a preceding letter or digit (i.e., not a real match).
            if (m.Index != 0 && char.IsLetterOrDigit( original[m.Index - 1] ))
            {
                return m.Value;
            }
            // has a trailing letter or digit (i.e., not a real match).
            if (m.Index + m.Length != original.Length && char.IsLetterOrDigit( original[m.Index + m.Length] ))
            {
                return m.Value;
            }
        }
        // it is a match, apply the format
        return string.Format( format, m.Value );
    }
} 

与前面的示例一起使用,将匹配项包装在跨度中,如下所示:

var regex = new Regex( highlightedWord, RegexOptions.IgnoreCase );
foreach (var sentence in sentences)
{
    var evaluator = new MatchEvaluator( match => Evaluators.Wrap( match, sentence, "<span class='red'>{0}</span>" ) );
    Console.WriteLine( regex.Replace( sentence, evaluator ) );
}

Use a regular expression:

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

Of course, this will also match words containing camel, but it's not clear if you want that or not.

If you need exact matches you can use a custom MatchEvaluator.

public static class Evaluators
{
    public static string Wrap( Match m, string original, string format )
    {
        // doesn't match the entire string, otherwise it is a match
        if (m.Length != original.Length)
        {
            // has a preceding letter or digit (i.e., not a real match).
            if (m.Index != 0 && char.IsLetterOrDigit( original[m.Index - 1] ))
            {
                return m.Value;
            }
            // has a trailing letter or digit (i.e., not a real match).
            if (m.Index + m.Length != original.Length && char.IsLetterOrDigit( original[m.Index + m.Length] ))
            {
                return m.Value;
            }
        }
        // it is a match, apply the format
        return string.Format( format, m.Value );
    }
} 

Used with the previous example to wrap the match in a span as:

var regex = new Regex( highlightedWord, RegexOptions.IgnoreCase );
foreach (var sentence in sentences)
{
    var evaluator = new MatchEvaluator( match => Evaluators.Wrap( match, sentence, "<span class='red'>{0}</span>" ) );
    Console.WriteLine( regex.Replace( sentence, evaluator ) );
}
情栀口红 2024-11-15 11:13:27

为字符串添加一个扩展方法来实现这个目的:

用法:

string yourString = "TEXTTOREPLACE";
yourString.Replace("texttoreplace", "Look, I Got Replaced!", StringComparison.OrdinalIgnoreCase);

代码:

using System;
using System.Collections.Generic;
using System.IO;

public static class Extensions
{       
    public static string Replace(this string source, string oldString, string newString, StringComparison comp)
    {
        int index = source.IndexOf(oldString, comp);

        // Determine if we found a match
        bool MatchFound = index >= 0;

        if (MatchFound)
        {
            // Remove the old text
            source = source.Remove(index, oldString.Length);

            // Add the replacemenet text
            source = source.Insert(index, newString);
        }

        // recurse for multiple instances of the name
        if (source.IndexOf(oldString, comp) != -1)
        {
            source = Replace(source, oldString, newString, comp);
        }

        return source;
    }
}

Add an extension method for string to do the trick:

Usage:

string yourString = "TEXTTOREPLACE";
yourString.Replace("texttoreplace", "Look, I Got Replaced!", StringComparison.OrdinalIgnoreCase);

Code:

using System;
using System.Collections.Generic;
using System.IO;

public static class Extensions
{       
    public static string Replace(this string source, string oldString, string newString, StringComparison comp)
    {
        int index = source.IndexOf(oldString, comp);

        // Determine if we found a match
        bool MatchFound = index >= 0;

        if (MatchFound)
        {
            // Remove the old text
            source = source.Remove(index, oldString.Length);

            // Add the replacemenet text
            source = source.Insert(index, newString);
        }

        // recurse for multiple instances of the name
        if (source.IndexOf(oldString, comp) != -1)
        {
            source = Replace(source, oldString, newString, comp);
        }

        return source;
    }
}
情话墙 2024-11-15 11:13:27

这是一个采用 StringComparison 的扩展方法,使用 string.IndexOf:

    [Pure]
    public static string Replace(this string source, string oldValue, string newValue, StringComparison comparisonType)
    {
        if (source.Length == 0 || oldValue.Length == 0)
            return source;

        var result = new System.Text.StringBuilder();
        int startingPos = 0;
        int nextMatch;
        while ((nextMatch = source.IndexOf(oldValue, startingPos, comparisonType)) > -1)
        {
            result.Append(source, startingPos, nextMatch - startingPos);
            result.Append(newValue);
            startingPos = nextMatch + oldValue.Length;
        }
        result.Append(source, startingPos, source.Length - startingPos);

        return result.ToString();
    }

顺便说一句,这里还有一个类似的 Contains 方法也采用 StringComparison:

    [Pure]
    public static bool Contains(this string source, string value, StringComparison comparisonType)
    {
        return source.IndexOf(value, comparisonType) >= 0;
    }

一些测试:

[TestFixture]
public class ExternalTests
{
    private static string[] TestReplace_args =
        {
            "ab/B/c/ac",
            "HELLO World/Hello/Goodbye/Goodbye World",
            "Hello World/world/there!/Hello there!",
            "hello WoRlD/world/there!/hello there!",
            "///",
            "ab///ab",
            "/ab/cd/",
            "a|b|c|d|e|f/|//abcdef",
            "a|b|c|d|e|f|/|/:/a:b:c:d:e:f:",
        };

    [Test, TestCaseSource("TestReplace_args")]
    public void TestReplace(string teststring)
    {
        var split = teststring.Split("/");
        var source = split[0];
        var oldValue = split[1];
        var newValue = split[2];
        var result = split[3];
        Assert.That(source.Replace(oldValue, newValue, StringComparison.OrdinalIgnoreCase), Is.EqualTo(result));
    }
}

Here's an extension method taking a StringComparison, using string.IndexOf:

    [Pure]
    public static string Replace(this string source, string oldValue, string newValue, StringComparison comparisonType)
    {
        if (source.Length == 0 || oldValue.Length == 0)
            return source;

        var result = new System.Text.StringBuilder();
        int startingPos = 0;
        int nextMatch;
        while ((nextMatch = source.IndexOf(oldValue, startingPos, comparisonType)) > -1)
        {
            result.Append(source, startingPos, nextMatch - startingPos);
            result.Append(newValue);
            startingPos = nextMatch + oldValue.Length;
        }
        result.Append(source, startingPos, source.Length - startingPos);

        return result.ToString();
    }

Btw, here's also a similar Contains-method also taking a StringComparison:

    [Pure]
    public static bool Contains(this string source, string value, StringComparison comparisonType)
    {
        return source.IndexOf(value, comparisonType) >= 0;
    }

Some tests:

[TestFixture]
public class ExternalTests
{
    private static string[] TestReplace_args =
        {
            "ab/B/c/ac",
            "HELLO World/Hello/Goodbye/Goodbye World",
            "Hello World/world/there!/Hello there!",
            "hello WoRlD/world/there!/hello there!",
            "///",
            "ab///ab",
            "/ab/cd/",
            "a|b|c|d|e|f/|//abcdef",
            "a|b|c|d|e|f|/|/:/a:b:c:d:e:f:",
        };

    [Test, TestCaseSource("TestReplace_args")]
    public void TestReplace(string teststring)
    {
        var split = teststring.Split("/");
        var source = split[0];
        var oldValue = split[1];
        var newValue = split[2];
        var result = split[3];
        Assert.That(source.Replace(oldValue, newValue, StringComparison.OrdinalIgnoreCase), Is.EqualTo(result));
    }
}
流殇 2024-11-15 11:13:27

这是我的扩展方法,它将 Tom Beech 的Tom Beech 的递归性相结合。 com/a/19734023/403404">sntbob,以及对 ksun 指出的错误的更清晰的修复。

代码:

public static string Replace(this string source, string oldString, 
                             string newString, StringComparison comparison)
{
    int index = source.IndexOf(oldString, comparison);

    while (index > -1)
    {
        source = source.Remove(index, oldString.Length);
        source = source.Insert(index, newString);

        index = source.IndexOf(oldString, index + newString.Length, comparison);
    }

    return source;
}

用法:

string source = "banana";
Console.WriteLine(source.Replace("AN", "banana", StringComparison.OrdinalIgnoreCase));

结果:

bbananabananaa

并且,如果您仍然希望递归性质是可选的:

代码:

public static string Replace(this string source, string oldString, 
                             string newString, StringComparison comparison,
                             bool recursive = true)
{
    int index = source.IndexOf(oldString, comparison);

    while (index > -1)
    {
        source = source.Remove(index, oldString.Length);
        source = source.Insert(index, newString);

        if (!recursive)
        {
            return source;
        }
        index = source.IndexOf(oldString, index + newString.Length, comparison);
    }

    return source;
}

用法:

string source = "banana";
Console.WriteLine(source.Replace("AN", "banana", StringComparison.OrdinalIgnoreCase, false));

结果:

bbananaana

Here is my extension method, which combines Tom Beech's, with the recursiveness of sntbob's, and a cleaner fix to the bug that ksun pointed out.

Code:

public static string Replace(this string source, string oldString, 
                             string newString, StringComparison comparison)
{
    int index = source.IndexOf(oldString, comparison);

    while (index > -1)
    {
        source = source.Remove(index, oldString.Length);
        source = source.Insert(index, newString);

        index = source.IndexOf(oldString, index + newString.Length, comparison);
    }

    return source;
}

Usage:

string source = "banana";
Console.WriteLine(source.Replace("AN", "banana", StringComparison.OrdinalIgnoreCase));

Result:

bbananabananaa

And, if you still want the recursive nature to be optional:

Code:

public static string Replace(this string source, string oldString, 
                             string newString, StringComparison comparison,
                             bool recursive = true)
{
    int index = source.IndexOf(oldString, comparison);

    while (index > -1)
    {
        source = source.Remove(index, oldString.Length);
        source = source.Insert(index, newString);

        if (!recursive)
        {
            return source;
        }
        index = source.IndexOf(oldString, index + newString.Length, comparison);
    }

    return source;
}

Usage:

string source = "banana";
Console.WriteLine(source.Replace("AN", "banana", StringComparison.OrdinalIgnoreCase, false));

Result:

bbananaana

停滞 2024-11-15 11:13:27

使用 StringComparison,因为它有方便的 OrdinalIgnoreCase

    string sentence = "We know it contains 'camel' word."; 
    string wordToFind = "camel";
    string replacementWord = "horse";

    int index = sentence.IndexOf(wordToFind , StringComparison.OrdinalIgnoreCase)
    // Did we match the word regardless of case
    bool match = index >= 0;

    // perform the replace on the matched word
    if(match) {
        sentence = sentence.Remove(index, wordToFind.Length)
        sentence = sentence.Insert(index, replacementWord)
    }

如果 C# String 类有一个像 Java 一样的 ignoreCase() 方法,那就太好了。

Utiltize StringComparison because of its handy OrdinalIgnoreCase

    string sentence = "We know it contains 'camel' word."; 
    string wordToFind = "camel";
    string replacementWord = "horse";

    int index = sentence.IndexOf(wordToFind , StringComparison.OrdinalIgnoreCase)
    // Did we match the word regardless of case
    bool match = index >= 0;

    // perform the replace on the matched word
    if(match) {
        sentence = sentence.Remove(index, wordToFind.Length)
        sentence = sentence.Insert(index, replacementWord)
    }

Sure would be nice if the C# String class had an ignoreCase() method like Java.

弥枳 2024-11-15 11:13:27

您还可以使用 String.IndexOf

http://msdn.microsoft。 com/en-us/library/system.string.indexof.aspx

与使用 RegExpressions 相比,您可能会获得稍好的性能(我讨厌它们,因为它们不直观且容易搞砸,尽管这简单的.Net 函数调用抽象了实际混乱的正则表达式,并且没有提供太多的错误空间),但这可能不是您关心的问题;现在的电脑真的很快,对吧? :) 采用 StringComparison 对象的 IndexOf 重载允许您选择忽略大小写,并且由于 IndexOf 返回指定位置的第一个匹配项,因此您必须编写一个循环来处理多次出现的字符串。

You could also use String.IndexOf

http://msdn.microsoft.com/en-us/library/system.string.indexof.aspx

You may get slightly better performance doing it this way than with RegExpressions (I abhor them because they're not intuitive and easy to screw up, although this simple .Net function call abstracts the actual messy RegEx, and doesn't provide much room for error), but that's probably not a concern for you; computers are REALLY fast these days, right? :) The overload for IndexOf that takes a StringComparison object allows you to optionally ignore case, and because IndexOf returns the first occurrence from at a specified position, you'll have to code a loop to process a string having multiple occurrences.

不语却知心 2024-11-15 11:13:27
    public static string CustomReplace(string srcText, string toFind, string toReplace, bool matchCase, bool replace0nce)
    {
        StringComparison sc = StringComparison.OrdinalIgnoreCase;
        if (matchCase)
            sc = StringComparison.Ordinal;

        int pos;
        while ((pos = srcText.IndexOf(toFind, sc)) > -1)
        {
            srcText = srcText.Remove(pos, toFind.Length);
            srcText = srcText.Insert(pos, toReplace);

            if (replace0nce)
                break;
        }

        return srcText;
    }
    public static string CustomReplace(string srcText, string toFind, string toReplace, bool matchCase, bool replace0nce)
    {
        StringComparison sc = StringComparison.OrdinalIgnoreCase;
        if (matchCase)
            sc = StringComparison.Ordinal;

        int pos;
        while ((pos = srcText.IndexOf(toFind, sc)) > -1)
        {
            srcText = srcText.Remove(pos, toFind.Length);
            srcText = srcText.Insert(pos, toReplace);

            if (replace0nce)
                break;
        }

        return srcText;
    }
感性不性感 2024-11-15 11:13:27

它可能不如其他一些答案那么有效,但我有点喜欢 sntbob 编写的 CustomReplace 函数。

然而,它有一个缺陷。如果文本替换是递归的,则会导致无限循环。例如,CustomReplace("I eatbananas!","an","banana",false,false) 将导致无限循环,并且字符串将继续变大。
例如,在第四次迭代之后,字符串将是“I eat bbbbbananaanaanaanaanas!”

如果您只想替换“banana”中“an”的两个实例,那么您将不得不采取另一种方法。我修改了 sntbob 的代码来解决这种情况。我承认它要复杂得多,但它可以处理递归替换。

public static string CustomReplace(string srcText, string toFind, string toReplace, bool matchCase, bool replaceOnce)
    {
        StringComparison sc = StringComparison.OrdinalIgnoreCase;
        if (matchCase)
            sc = StringComparison.Ordinal;

        int pos;
        int previousProcessedLength = 0;
        string alreadyProcessedTxt = "";
        string remainingToProcessTxt = srcText;
        while ((pos = remainingToProcessTxt.IndexOf(toFind, sc)) > -1)
        {
            previousProcessedLength = alreadyProcessedTxt.Length;
            //Append processed text up until the end of the found string and perform replacement
            alreadyProcessedTxt += remainingToProcessTxt.Substring(0, pos + toFind.Length);
            alreadyProcessedTxt = alreadyProcessedTxt.Remove(previousProcessedLength + pos, toFind.Length);
            alreadyProcessedTxt = alreadyProcessedTxt.Insert(previousProcessedLength + pos, toReplace);

            //Remove processed text from remaining
            remainingToProcessTxt = remainingToProcessTxt.Substring(pos + toFind.Length);                

            if (replaceOnce)
                break;
        }

        return alreadyProcessedTxt + remainingToProcessTxt;
    }

It may not be as efficient as some of the other answers, but I kind of like the CustomReplace function written by sntbob.

However, there is a flaw in it. If the text replacement is recursive it will cause an infinite loop. For example, CustomReplace("I eat bananas!","an","banana",false,false) would cause an infinite loop and the string would continue growing larger.
For example, after the 4th iteration the string would be "I eat bbbbbananaanaanaanaanas!"

If you want to only replace the two instances of "an" inside "banana" then you'll have to take another approach. I modified sntbob's code to account for this case. I admit that it's much more convoluted, but it handles recursive replacements.

public static string CustomReplace(string srcText, string toFind, string toReplace, bool matchCase, bool replaceOnce)
    {
        StringComparison sc = StringComparison.OrdinalIgnoreCase;
        if (matchCase)
            sc = StringComparison.Ordinal;

        int pos;
        int previousProcessedLength = 0;
        string alreadyProcessedTxt = "";
        string remainingToProcessTxt = srcText;
        while ((pos = remainingToProcessTxt.IndexOf(toFind, sc)) > -1)
        {
            previousProcessedLength = alreadyProcessedTxt.Length;
            //Append processed text up until the end of the found string and perform replacement
            alreadyProcessedTxt += remainingToProcessTxt.Substring(0, pos + toFind.Length);
            alreadyProcessedTxt = alreadyProcessedTxt.Remove(previousProcessedLength + pos, toFind.Length);
            alreadyProcessedTxt = alreadyProcessedTxt.Insert(previousProcessedLength + pos, toReplace);

            //Remove processed text from remaining
            remainingToProcessTxt = remainingToProcessTxt.Substring(pos + toFind.Length);                

            if (replaceOnce)
                break;
        }

        return alreadyProcessedTxt + remainingToProcessTxt;
    }
放飞的风筝 2024-11-15 11:13:27

为什么不直接导入 Microsoft.VisualBasic 命名空间并使用 VB Strings.Replace 方法?

https://msdn .microsoft.com/en-us/library/microsoft.visualbasic.strings.replace(v=vs.110).aspx

例如,

var newString = Strings.Replace(SourceString, FindTextValue, ReplacementTextValue, 1, -1, Constants.vbTextCompare);

vbTextCompare 强制进行不区分大小写的替换。工作完成了。

好吧,它不是“纯”C#,但它可以让您以更少的复杂性和更少的混乱到达您想要的地方。

Why not just import the Microsoft.VisualBasic namespace and use the VB Strings.Replace method?

https://msdn.microsoft.com/en-us/library/microsoft.visualbasic.strings.replace(v=vs.110).aspx

eg

var newString = Strings.Replace(SourceString, FindTextValue, ReplacementTextValue, 1, -1, Constants.vbTextCompare);

vbTextCompare forces a case-insensitive replacement. Job done.

Okay, it's not 'pure' C#, but it gets you to where you want to go with much less complexity and messing around.

‖放下 2024-11-15 11:13:27

这是使用 StringComparison 作为扩展方法的另一种替代方法。在 StringBuilder 对象上。我读过一些文章,表明 StringBuilder 使用内存可能比使用字符串更有效。如果您需要的话,您可以轻松地更改它以使用字符串。

/// <summary>
/// Extension method to find/replace replaces text in a StringBuilder object
/// </summary>
/// <param name="original">Source StringBuilder object</param>
/// <param name="oldString">String to search for</param>
/// <param name="newString">String to replace each occurrance of oldString</param>
/// <param name="stringComparison">String comparison to use</param>
/// <returns>Original Stringbuilder with replacements made</returns>
public static StringBuilder Replace(this StringBuilder original,
                    string oldString, string newString, StringComparison stringComparison)
    {
        //If anything is null, or oldString is blank, exit with original value
        if ( newString == null || original == null || string.IsNullOrEmpty(oldString))
            return original;

        //Convert to a string and get starting position using
        //IndexOf which allows us to use StringComparison.
        int pos = original.ToString().IndexOf(oldString, 0, stringComparison);

        //Loop through until we find and replace all matches
        while ( pos >= 0 )
        {
            //Remove the old string and insert the new one.
            original.Remove(pos, oldString.Length).Insert(pos, newString);

            //Get the next match starting 1 character after last replacement (to avoid a possible infinite loop)
            pos = original.ToString().IndexOf(oldString, pos + newString.Length + 1, stringComparison);
        }
        return original;
    }

Here's one more alternative that uses StringComparison as an extension method. on a StringBuilder object. I've read some articles indicating that a StringBuilder might be a little more efficient with memory than using strings. You can easily alter this to work with strings if that's what you need.

/// <summary>
/// Extension method to find/replace replaces text in a StringBuilder object
/// </summary>
/// <param name="original">Source StringBuilder object</param>
/// <param name="oldString">String to search for</param>
/// <param name="newString">String to replace each occurrance of oldString</param>
/// <param name="stringComparison">String comparison to use</param>
/// <returns>Original Stringbuilder with replacements made</returns>
public static StringBuilder Replace(this StringBuilder original,
                    string oldString, string newString, StringComparison stringComparison)
    {
        //If anything is null, or oldString is blank, exit with original value
        if ( newString == null || original == null || string.IsNullOrEmpty(oldString))
            return original;

        //Convert to a string and get starting position using
        //IndexOf which allows us to use StringComparison.
        int pos = original.ToString().IndexOf(oldString, 0, stringComparison);

        //Loop through until we find and replace all matches
        while ( pos >= 0 )
        {
            //Remove the old string and insert the new one.
            original.Remove(pos, oldString.Length).Insert(pos, newString);

            //Get the next match starting 1 character after last replacement (to avoid a possible infinite loop)
            pos = original.ToString().IndexOf(oldString, pos + newString.Length + 1, stringComparison);
        }
        return original;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文