大文字小文字を区別しないString.Replace


214

「hello world」という文字列があります

「world」を「csharp」に置き換える必要がある

このために私は使用します:

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

しかし、その結果、文字列が置換されません。理由は大文字と小文字が区別されるためです。元の文字列には「world」が含まれていますが、「World」を置き換えようとしています。

string.Replaceメソッドでこの大文字と小文字の区別を回避する方法はありますか?


5
ここに同様の問題があり
のMichałKuliński

回答:


309

正規表現を使用して、大文字と小文字を区別しない置換を実行できます。

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

19
Regex言語要素では機能しないため、普遍的な方法ではありません。スティーブBの答えは正しいです。
AsValeO 2015

1
ですから、hello. world?正規表現演算子を含むものを書かないでください。
セバスチャンマッハ

さらに読みたくない人のために、これは2011年に承認された回答であり、投票数は膨大です。英数字のみを置き換える必要がある場合、これは正常に機能します。ただし、句読点文字を置き換える必要がある場合、大きな問題が発生する可能性があります。オレグZarevennyiの答えは優れているが、それは2017年に投稿されましたので、投票のほんの数を持っている
トニーPulokas

115
var search = "world";
var replacement = "csharp";
string result = Regex.Replace(
    stringToLookInto,
    Regex.Escape(search), 
    replacement.Replace("$","$$"), 
    RegexOptions.IgnoreCase
);

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

その出力は次のとおりです。

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 }

2
このメソッドは、replacement = "!@#$%^&*()"の場合は失敗します。代わりに "!@ \#\ $%\ ^&*()"が置き換えられます。
Kcoder、2014年

2
2番目Regex.Escapeは悪いです。特殊文字の前にバックスラッシュを付けます。最善の方法のようですが、.Replace( "$"、 "$$")は、ちょっとばかげています(stackoverflow.com/a/10078353)。
ダニーTuppeny 2015

1
@dannyTuppeny:あなたは正しいです...私はそれに応じて答えを更新しました
Steve B

54

2.5X FASTER最も効果的なの方法の他の正規表現の方法より:

/// <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倍の速さです(最終的に証明)。
  • (設定された入力文字列から一部を除去するのに適しnewValuenull、このために最適化されました)。
  • 元の.NET C#の string.Replace動作と同じですが、例外があります。
  • よくコメントされ、理解しやすい。
  • よりシンプル–正規表現なし。正規表現は汎用性があるため(コンパイルされていても)、常に遅くなります。
  • この方法は十分にテストされており、他のソリューションでは無限ループのような隠れた欠陥はありません。

@AsValeO:Regex言語要素では機能しないため、普遍的な方法ではありません

@Mike Stillion:このコードに問題があります。newのテキストがoldのテキストのスーパーセットである場合、これは無限ループを生成する可能性があります。


ベンチマーク証明:このソリューションは、@ 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ます。


5
文字列ではなくStringBuilderを使用すると、これをさらに高速化できると思います。
MineR 2018年

1
@MineR正解です。元々は無限ループのない@ Darky711ソリューションを更新しただけなので、を使用しましたString。ただし、はより30〜40%StringBuilder高速です。ソリューションを更新しました。ありがとう;)String
Oleg Zarevennyi 2018年

2
興味深いアプローチ。おそらく、パフォーマンスが重要な場合は、より優れたもの(私のものより:))です。通常は、共通の共有コードライブラリに追加するメソッドです。
スティーブB

2
'nameof'式を使用すると、C#6.0以降でのみ有効になります。VS2013を使用している場合は、例外のオペランドを削除するだけで使用できます。
LanchPad

コメントアウトされた「// if(oldValue.Equals(newValue、ComparisonType))」について、comparisonTypeをStringComparison.Ordinal?に置き換えますか?
ロジャーウィルコックス

31

拡張機能は私たちの生活を容易にします。

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

10
そしてエスケープすることで私たちの生活のバグが少なくなります:-) return Regex.Replace(input、Regex.Escape(search)、replacement.Replace( "$"、 "$$")、RegexOptions.IgnoreCase);
Vman

29

Regexを使用した多くの提案。それなしでこの拡張メソッドはどうですか:

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

比較引数は実際の置換を行うために使用されていないことに注意してください(常に大文字と小文字は区別されません)
Bolo

2
このコードに問題があります。newのテキストがoldのテキストのスーパーセットである場合、これは無限ループを生成する可能性があります。一旦新たにに挿入されFoundAtの値FoundAtは長さだけ前進する必要が新しいです
Mike Stillion

comparisonパラメータはIndexOfStringComparison.CurrentCultureIgnoreCase
Maxence

@Bolo比較引数を使用するように編集しました(ピアレビューには少し時間がかかる場合があります)。
bradlis7

2
新しい文字列を返す場合も、この条件を分離if(old.Equals(@new, comparison)) return @new;します。新しい文字列は大文字と小文字が異なる場合があるためです。
sɐunıɔןɐqɐp

13

Microsoft.VisualBasic名前空間を使用して、このヘルパー関数を見つけることができます。

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

これが組み込まれているため、より良い答えになるまで、私は自分の答えを誇りに思いました。例:Strings.Replace( "TeStInG123"、 "t"、 "z"、1、-1、CompareMethod.Text)は " zeSzInG123 "
Bolo

警告、検索中の文字列が空の文字列の場合、Strings.Replaceはnullを返します。
Mafu Josh

1
.Net 4.7.2では、これを機能させるためにMicrosoft.VisualBasicへの参照を追加する必要があります。.Net Coreでは、Microsoft.VisualBasic.Stringsクラス(とにかくバージョン10.3.0)はReplace関数を実装していないようです。これは、最初にAdd-Class -AssemblyName Microsoft.VisualBasicを実行した場合にも、Powershellで機能します。
Von Lemongargle教授、

6

編集:申し訳ありませんが、「ネイキッドリンク」の問題を認識していませんでした)

ここから撮影:

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

大文字と小文字を区別しないstring.Replaceの欠如について不平を言うのはあなたが最初ではないようです。


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

2

私は拡張メソッドを書きました:

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

以前の拡張メソッドに2つの追加の拡張メソッドを使用します。

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

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

2
賛成。しかしIsNot、拡張機能を真剣に受け止めすぎています:)
nawfal 2017

残念なことに、これはすべての状況で機能するわけではありません。識別名を渡していたところ、文字列が100万文字になるまで追加され、メモリが不足しました
Bbb

私の問題を修正した以下の代替案
Bbb

私は本当に好きです.IsNot
19

1

検索文字列でPetrucioの回答を拡張Regex.Escapeし、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

1

これは機能しません。他のイメージを作成することはできません。

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を使用しています。
fvlinden

0

以下の関数は、(this)のようなすべての一致単語を文字列セットから削除することです。Ravikant Sonareによる。

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による
Ravikant Sonare

0

@Georgy Batalovソリューションを使用して、次の例を使用すると問題が発生しました

string original = "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();
    }

0

以下は、文字の大文字小文字を無視して文字列を置き換える代替方法です

String thisString = "hello world"; 
String replaceString = "World";

//thisString.Replace("World", "csharp"); 
//below is the alternative to replace string ignoring character case

int start = StringUtils.indexOfIgnoreCase(thisString,replaceString);
String searchKey = thisString.substring(start, start+replaceString.length());
thisString= thisString.replaceAll(searchKey ,replaceString );
System.out.println(thisString);

//prints hello World

0

Regexクラスを試すこともできます。

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


-3

私はこれを好む-"Hello World" .ToLower()。Replace( "world"、 "csharp");


1
これにより、置き換えられるはずのない単語も含めて、すべて小文字になります。
JJJ

明らかに、あなたがケースについて気になっていない場合にのみ、これを使用できます。
Harshal
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.