String.Replace mengabaikan case


214

Saya memiliki string yang disebut "hello world"

Saya perlu mengganti kata "dunia" menjadi "csharp"

untuk ini saya gunakan:

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

tetapi sebagai hasilnya, saya tidak mendapatkan string diganti. Alasannya adalah sensitivitas kasus. String asli berisi "dunia" sedangkan saya mencoba mengganti "Dunia".

Apakah ada cara untuk menghindari sensitivitas huruf ini dalam metode string. Ganti?



Jawaban:


309

Anda bisa menggunakan Regex dan melakukan penggantian case yang tidak sensitif:

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
Tidak berfungsi dengan elemen bahasa Regex , jadi itu bukan metode universal. Jawaban Steve B benar.
AsValeO

1
Jadi sebaiknya Anda tidak menulis hello. world?atau apa pun yang mengandung operator regex.
Sebastian Mach

Kalau-kalau ada orang yang tidak cenderung membaca lebih lanjut, ini adalah jawaban yang diterima pada tahun 2011 dan memiliki banyak suara. Ini berfungsi dengan baik jika Anda hanya perlu mengganti alfanumerik. Namun, jika Anda harus mengganti karakter tanda baca apa pun, Anda bisa mendapat masalah besar. Jawaban Oleg Zarevennyi lebih unggul, tetapi hanya memiliki sedikit suara karena telah diposting pada 2017.
Tony Pulokas

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

The Regex.Escape berguna jika Anda mengandalkan input pengguna yang dapat berisi unsur bahasa Regex

Memperbarui

Berkat komentar, Anda sebenarnya tidak perlu melarikan diri dari string pengganti.

Berikut adalah biola kecil yang menguji kode :

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

Outputnya adalah:

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
Metode ini gagal jika penggantian = "! @ # $% ^ & * ()" Anda mendapatkan "! @ \ # \ $% \ ^ & * ()" Diganti sebagai gantinya.
Kcoder

2
Yang kedua Regex.Escapeburuk, itu akan mengawali karakter khusus dengan garis miring terbalik. Sepertinya cara terbaik adalah .Replace ("$", "$$"), yang agak bodoh ( stackoverflow.com/a/10078353 ).
Danny Tuppeny

1
@dannyTuppeny: Anda benar ... Saya memperbarui jawaban yang sesuai
Steve B

54

2.5X metode LEBIH CEPAT dan PALING EFEKTIF daripada metode ekspresi reguler lainnya:

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

}

Catatan: abaikan case == StringComparison.OrdinalIgnoreCasesebagai parameter untuk StringComparison comparisonType. Ini adalah cara tercepat, tidak peka huruf besar-kecil untuk mengganti semua nilai.


Keuntungan dari metode ini:

  • Efisiensi CPU dan MEMORY tinggi;
  • Ini adalah solusi tercepat, 2,5 kali lebih cepat dari metode lain dengan ekspresi reguler (bukti pada akhirnya);
  • Cocok untuk melepas bagian-bagian dari string input (diatur newValueke null ), dioptimalkan untuk ini;
  • Sama seperti aslinya .NET C # string.Replace perilaku , pengecualian yang sama;
  • Berkomentar bagus, mudah dimengerti;
  • Lebih sederhana - tidak ada ekspresi reguler. Ekspresi reguler selalu lebih lambat karena fleksibilitasnya (bahkan dikompilasi);
  • Metode ini telah teruji dengan baik dan tidak ada kekurangan tersembunyi seperti infinite loop dalam solusi orang lain, bahkan dinilai tinggi:

@ ASValeO: Tidak berfungsi dengan elemen bahasa Regex, jadi ini bukan metode universal

@ Mike Stillion: Ada masalah dengan kode ini. Jika teks dalam yang baru adalah superset dari teks yang lama, ini dapat menghasilkan loop tanpa akhir.


Benchmark-proof : solusi ini 2,59 kali lebih cepat daripada regex dari @Steve B., kode:

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

Ide asli - @ Darky711; terima kasih @MinerR untuk StringBuilder.


5
Saya yakin Anda bisa membuatnya lebih cepat menggunakan StringBuilder daripada string.
MineR

1
@ MinR Anda benar, saya awalnya hanya memperbarui solusi @ Darky711 tanpa loop tak terbatas, jadi saya menggunakan String. Namun, StringBuilderini benar-benar lebih cepat 30-40% dari String. Saya telah memperbarui solusinya. Terima kasih;)
Oleg Zarevennyi

2
Pendekatan yang menarik. Mungkin yang lebih baik (lebih baik dari milik saya :)) ketika masalah kinerja. Biasanya metode untuk menambahkan ke pustaka kode bersama yang umum.
Steve B

2
Penggunaan ekspresi 'nameof' membuat ini hanya berlaku untuk C # 6.0 dan seterusnya. Jika Anda menggunakan VS2013, Anda dapat menggunakannya hanya dengan menghapus operan dalam pengecualian.
LanchPad

Untuk komentar "// if (oldValue.Equals (newValue, comparisonType))" gantilah comparType dengan StringComparison.Ordinal?
Roger Willcocks

31

Ekstensi membuat hidup kita lebih mudah:

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
Dan melarikan diri membuat hidup kita lebih tidak bermasalah :-) return Regex.Replace (input, Regex.Escape (search), replacement.Replace ("$", "$$"), RegexOptions.IgnoreCase);
Vman

29

Banyak saran menggunakan Regex. Bagaimana dengan metode ekstensi ini tanpa itu:

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

Perhatikan bahwa argumen perbandingan tidak digunakan untuk melakukan penggantian yang sebenarnya (selalu tidak peka huruf besar-kecil)
Bolo

2
Ada masalah dengan kode ini. Jika teks dalam yang baru adalah superset dari teks yang lama , ini dapat menghasilkan loop tanpa akhir. Setelah yang baru dimasukkan di FoundAt , nilai FoundAt harus ditingkatkan pada panjang yang baru .
Mike Stillion

comparisonparameter harus digunakan IndexOf, bukanStringComparison.CurrentCultureIgnoreCase
Maxence

@Bolo Saya sudah mengeditnya untuk menggunakan argumen perbandingan (mungkin butuh sedikit untuk ditinjau sejawat).
bradlis7

2
Saya juga akan memisahkan kondisi ini untuk mengembalikan string baru:, if(old.Equals(@new, comparison)) return @new;karena string baru mungkin berbeda dalam huruf besar / huruf kecil.
sɐunıɔ ןɐ qɐp

13

Anda dapat menggunakan namespace Microsoft.VisualBasic untuk menemukan fungsi pembantu ini:

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

Saya bangga dengan jawaban saya sampai saya melihat ini yang merupakan jawaban yang lebih baik karena sudah ada di dalamnya. Contoh: Strings.Replace ("TeStInG123", "t", "z", 1, -1, CompareMethod.Text) mengembalikan " zeSzInG123 "
Bolo

Peringatan, Strings.Replace mengembalikan null jika string yang dicari adalah string kosong.
Mafu Josh

1
Di .Net 4.7.2, Anda perlu menambahkan referensi ke Microsoft.VisualBasic untuk membuatnya berfungsi. Di .Net Core, kelas Microsoft.VisualBasic.Strings (dalam Versi 10.3.0) tampaknya tidak mengimplementasikan fungsi Ganti. Ini berfungsi di Powershell juga jika Anda Add-Class -AssemblyName Microsoft.VisualBasic pertama.
Prof Von Lemongargle

6

( Diedit: tidak mengetahui masalah 'tautan telanjang', maaf soal itu)

Diambil dari sini :

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

Tampaknya Anda bukan yang pertama mengeluh tentang kurangnya string case sensitif. Ganti.


5

Jawaban @ Darky711 yang dimodifikasi untuk menggunakan jenis perbandingan yang diteruskan dan mencocokkan kerangka kerja menggantikan penamaan dan komentar xml sedekat mungkin.

/// <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

Saya telah menulis metode ekstensi:

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

Saya menggunakan dua metode ekstensi tambahan untuk metode ekstensi sebelumnya:

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

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

2
Terpilih. Tetapi IsNotmengambil ekstensi terlalu serius :)
nawfal

Mengecewakan, ini tidak berhasil di semua situasi. Saya memberikan nama yang terkenal dan menambahkan hingga string adalah sejuta karakter dan kemudian kehabisan memori
Bbb

Alternatif yang ditawarkan di bawah itu memperbaiki masalah saya
Bbb

Saya sangat suka.IsNot
ttugate

1

Memperluas jawaban Petrucio dengan Regex.Escapepada string pencarian, dan melarikan diri dari grup yang cocok seperti yang disarankan dalam jawaban Steve B (dan beberapa perubahan kecil pada selera saya):

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

Yang akan menghasilkan hasil yang diharapkan sebagai berikut:

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

Namun tanpa melakukan pelarian Anda akan mendapatkan yang berikut, yang bukan perilaku yang diharapkan dari String.Replaceyang hanya case-sensitive:

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

Bukankah ini bekerja: Saya tidak bisa membayangkan hal lain menjadi lebih cepat atau mudah.

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

Saya tidak tahu apakah ini lebih cepat tapi ringkas, tidak menggunakan overhead regex dan masalah potensial dan menggunakan StringComparison bawaan.
fvlinden

0

Fungsi di bawah ini untuk menghapus semua kata yang cocok seperti (ini) dari rangkaian string. Oleh 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);
}

Fungsi ini menggantikan semua string dari set string ... oleh Ravikant Sonare,
Ravikant Sonare

0

Menggunakan solusi @Georgy Batalov, saya mengalami masalah saat menggunakan contoh berikut ini

string original = "bla, DC = bleh, DC = blih, DC = bloh, DC = com"; string diganti = original.ReplaceIgnoreCase (", DC =", ".")

Di bawah ini adalah bagaimana saya menulis ulang ekstensinya

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

di bawah ini adalah alternatif untuk mengganti karakter huruf yang mengabaikan string

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

Anda juga dapat mencoba Regexkelas.

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


-3

Saya lebih suka ini - "Halo Dunia" .ToLower (). Ganti ("dunia", "csharp");


1
Ini akan mengurangi segalanya, bahkan kata-kata yang tidak seharusnya diganti.
JJJ

Jelas, Anda dapat menggunakan ini hanya jika Anda tidak terganggu dengan kasus ini.
Harshal
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.