Dize.Yoksayma kasasını değiştirin


214

"Merhaba dünya" adlı bir dizem var

"Dünya" kelimesini "csharp" olarak değiştirmem gerekiyor

bunun için kullanıyorum:

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

ama sonuç olarak, dize yerine alamadım. Nedeni büyük / küçük harfe duyarlılıktır. Orijinal dize "dünya" içerirken ben "Dünya" yerine çalışıyorum.

Dizede bu büyük / küçük harfe duyarlılığı önlemenin bir yolu var mı?


5
Burada benzer bir sorun bulabilirsiniz: Dize için bir alternatif var mı.
Michał Kuliński

Yanıtlar:


309

Regex kullanabilir ve büyük / küçük harfe duyarlı olmayan bir değişiklik yapabilirsiniz:

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
Değil ile çalışır Regex dil elemanları , bu nedenle evrensel yöntem değil. Steve B'nin cevabı doğru.
AsValeO

1
Yani hello. world?regex operatörleri içeren başka bir şey yazmasanız iyi olur.
Sebastian Mach

Kimsenin daha fazla okumaya meyilli olmaması durumunda, bu 2011'de kabul edilen cevaptı ve çok sayıda oyu var. Yalnızca alfasayısal karakterleri değiştirmek zorunda kalırsanız bu işe yarar. Ancak, herhangi bir noktalama işaretini değiştirmeniz gerekiyorsa büyük sorun yaşayabilirsiniz. Oleg Zarevennyi'nin cevabı üstündür, ancak 2017'de yayınlandığı için çok az oyu vardır.
Tony Pulokas

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

Regex.Escape Eğer kullanıcı girişi güveniyorsanız can içeren yararlıdır Regex dil unsurları

Güncelleme

Yorumlar sayesinde, aslında yedek dizeden kaçmak zorunda değilsiniz.

İşte kodu test eden küçük bir keman :

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

Çıkışı:

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 = "! @ # $% ^ & * ()" Bunun yerine "! @ \ # \ $% \ ^ & * ()" Değiştirilirse bu yöntem başarısız olur.
Kcoder

2
İkincisi Regex.Escapekötü, özel karakterlerin önüne eğik çizgi ekler. En iyi yol gibi görünüyor . Aptal ("$", "$$"), biraz aptal ( stackoverflow.com/a/10078353 ).
Danny Tuppeny

1
@dannyTuppeny: haklısın ... Cevabı buna göre güncelledim
Steve B

54

2.5X HIZLI ve EN ETKİLİ diğer en düzenli ifadeler yöntemlerine göre daha metodu:

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

}

Not: case == StringComparison.OrdinalIgnoreCaseparametresini yoksayStringComparison comparisonType . Tüm değerleri değiştirmenin en hızlı, büyük / küçük harf duyarsız yoludur.


Bu yöntemin avantajları:

  • Yüksek CPU ve BELLEK verimliliği;
  • En hızlı çözüm, düzenli ifadelerle diğerlerinin yöntemlerinden 2,5 kat daha hızlı (sonunda kanıt);
  • Giriş dizesi parçaları çıkarmak için uygun (set newValueiçin null bu için optimize edilmiş);
  • Orijinal .NET C # ile aynı string.Replace davranışı ile aynı, aynı özel durumlar;
  • İyi yorumladı, anlaşılması kolay;
  • Daha basit - düzenli ifade yok. Düzenli ifadeler, çok yönlülükleri (hatta derlenmiş olsalar) nedeniyle daima daha yavaştır;
  • Bu yöntem iyi test edilmiştir ve diğerlerinin çözümlerinde sonsuz derece bile olsa gizli döngü yoktur:

@AsValeO: Regex dil öğeleriyle çalışmadığından evrensel bir yöntem değildir

@Mike Stillion: Bu kodla ilgili bir sorun var. Yeni metin eski metnin üst kümesiyse, bu sonsuz bir döngü oluşturabilir.


Kıyaslama testi : Bu çözüm @Steve B kodundan normal ifadeden 2,59 kat daha hızlıdır, kod:

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

Orijinal fikir - @ Darky711; @MinerR için teşekkürler StringBuilder.


5
Bahse girerim bir string yerine StringBuilder kullanarak bunu daha da hızlı yapabilirsiniz.
MineR

1
@MineR Haklısın, başlangıçta @ Darky711 çözümünü sonsuz döngü olmadan güncelledim, bu yüzden kullandım String. Ancak, StringBuildergerçekten hızlı gereğidir 30-40% daha String. Çözümü güncelledim. Teşekkürler;)
Oleg Zarevennyi

2
İlginç bir yaklaşım. Performans önemli olduğunda muhtemelen daha iyi (benimkinden daha iyi :)). Genellikle ortak bir ortak kod kitaplığına ekleme yöntemi.
Steve B

2
'Nameof' ifadelerinin kullanılması bunu yalnızca C # 6.0 ve sonrası için geçerli kılar. VS2013'teyseniz, istisnalardaki işlenenleri silerek kullanabilirsiniz.
LanchPad

"// if (oldValue.Equals (newValue, comparType))" "için yorumlanan dışarıda, karşılaştırmaType öğesini StringComparison.Ordinal?
Roger Willcocks

31

Uzantılar hayatımızı kolaylaştırır:

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
Ve kaçmak hayatımızı daha az buggy yapar :-) dönüş Regex.Replace (giriş, Regex.Escape (arama), değiştirme.
Vman

29

Regex kullanarak birçok öneri. Onsuz bu uzantı yöntemi hakkında:

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

Karşılaştırma argümanının asıl değişikliği yapmak için kullanılmadığına dikkat edin (her zaman büyük / küçük harfe duyarsızdır)
Bolo

2
Bu kodla ilgili bir sorun var. Metin ise yeni metnin bir üst kümesidir eski , bu sonsuz bir döngüye üretebilir. Bir kez yeni sokulur Vak , değeri Vak uzunluğu tarafından ileri gereken yeni .
Mike Stillion

comparisonparametresi IndexOfyerine kullanılmalıdırStringComparison.CurrentCultureIgnoreCase
Maxence

@ Bolo'yu karşılaştırma bağımsız değişkenini kullanmak için düzenledim (akranların gözden geçirilmesi biraz zaman alabilir).
bradlis7

2
Ayrıca if(old.Equals(@new, comparison)) return @new;, yeni dize büyük / küçük harf olarak değişebileceğinden , yeni dizeyi döndürmek için bu koşulu ayırırdım.
sɐunıɔ ןɐ qɐp

13

Bu yardımcı işlevi bulmak için Microsoft.VisualBasic ad alanını kullanabilirsiniz :

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

Bu daha iyi bir cevap olduğunu görene kadar cevabımla gurur duydum çünkü yerleşik. Ex: Strings.Replace ("TeStInG123", "t", "z", 1, -1, CompareMethod.Text) döndürür " zeSzInG123 "
Bolo

Uyarı, Strings.Replace, aranmakta olan dize boş bir dize ise null değerini döndürür.
Mafu Josh

1
Net 4.7.2'de, bunun çalışması için Microsoft.VisualBasic'e bir başvuru eklemeniz gerekir. Net Core'da, Microsoft.VisualBasic.Strings sınıfı (yine de Sürüm 10.3.0'da) Değiştir işlevini uygulamak için görünmüyor. İlk önce Add-Class -AssemblyName Microsoft.VisualBasic yaparsanız, bu Powershell de çalışır.
Prof Von Lemongargle

6

( Düzenlendi: `` çıplak bağlantı '' sorununun farkında değildi, bunun için üzgünüm)

Alındığı burada :

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

Büyük / küçük harfe duyarlı olmayan dize eksikliğinden ilk şikayet eden siz değilsiniz.


5

@ Darky711'in, karşılaştırma türünde geçirilen ve çerçeve yerine adlandırma ve xml yorumlarını mümkün olduğunca yakından kullanmak için değiştirildi.

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

Uzantı yöntemi yazdım:

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

Önceki uzantı yöntemi için iki ek uzantı yöntemi kullanıyorum:

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

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

2
Upvoted. Ancak IsNotuzantıları çok ciddiye alıyor :)
nawfal

Hayal kırıklığı, bu her durumda işe yaramaz. Ben seçkin bir isim geçiyordu ve dize bir milyon karakter uzunluğunda ve daha sonra bellek bitene kadar ekler
Bbb

Aşağıda sunulan alternatif sorunumu düzeltti
Bbb

Gerçekten seviyorum.IsNot
ttugates

1

Arama dizisinde Petrucio'nun cevabını genişletmek Regex.Escapeve Steve B'nin cevabında (ve zevkime göre bazı küçük değişiklikler) önerildiği gibi eşleşen gruptan kaçmak :

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

Bu, aşağıdaki beklenen sonuçları üretecektir:

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

Ancak kaçışları gerçekleştirmeden aşağıdakileri elde edersiniz String.Replaceki bu, büyük / küçük harfe duyarlı olmayan bir davranıştan beklenen bir davranış değildir:

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

Bu işe yaramıyor: Başka bir şeyin daha hızlı veya daha kolay olduğunu göremiyorum.

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

Daha hızlı olup olmadığını bilmiyorum ama özlü, regex ek yükü ve potansiyel sorunları kullanmıyor ve yerleşik StringComparison kullanıyor.
fvlinden

0

Aşağıdaki işlev, dizgi kümesinden (this) gibi tüm eşleme kelimelerini kaldırmaktır. Ravikant Sonare tarafından.

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

Bu işlev, dize kümesindeki tüm dizeyi değiştirir ... tarafından Ravikant Sonare,
Ravikant Sonare

0

@Georgy Batalov çözümünü kullanarak aşağıdaki örneği kullanırken bir sorun yaşadım

string original = "blah, DC = bleh, DC = blih, DC = bloh, DC = com"; string değiştirildi = original.ReplaceIgnoreCase (", DC =", ".")

Aşağıda onun uzantısını nasıl yeniden yazdım

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

Aşağıda karakter büyük / küçük harf durumunu görmezden gelmenin alternatifidir

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

Ayrıca Regexsınıfı deneyebilirsiniz .

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


-3

Bunu tercih ederim - "Merhaba Dünya" .ToLower (). Değiştirin ("dünya", "csharp");


1
Bu, değiştirilmemesi gereken kelimeleri bile her şeyi küçük harcayacaktır.
JJJ

Açıkçası, bunu sadece davadan rahatsız değilseniz kullanabilirsiniz.
Harshal
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.