대소 문자를 무시하고 String.Replace


214

"hello world"라는 문자열이 있습니다

"world"라는 단어를 "csharp"로 바꿔야합니다

이를 위해 나는 다음을 사용한다.

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

그러나 결과적으로 문자열을 교체하지 않습니다. 이유는 대소 문자를 구분하기 때문입니다. 원래 문자열에 "world"가 포함되어 있지만 "World"를 바꾸려고합니다.

string에서 대소 문자를 구분할 수있는 방법이 있습니까?



답변:


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 언어 요소 와 함께 작동 하지 않으므로 보편적 인 방법이 아닙니다. Steve B의 답변이 맞습니다.
AsValeO

1
따라서 hello. world?정규식 연산자를 포함하거나 다른 것을 쓰지 않는 것이 좋습니다 .
Sebastian Mach

누구든지 더 이상 읽을 의향이없는 경우를 대비하여 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

2
두 번째 Regex.Escape는 좋지 않습니다. 특수 문자 앞에 백 슬래시가 붙습니다. 가장 좋은 방법은 .Replace ( "$", "$$")입니다. 이것은 다소 바보입니다 ( stackoverflow.com/a/10078353 ).
Danny Tuppeny

1
@dannyTuppeny : 당신 말이 맞아요. 나는 그에 따라 대답을 업데이트했습니다
Steve B

54

2.5X FASTERMOST EFFECTIVE 방법은 다른 정규식 방법보다 :

/// <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 대한 매개 변수로 case == 를 무시하십시오StringComparison comparisonType . 모든 값을 바꾸는 가장 빠르고 대소 문자를 구분하지 않는 방법입니다.


이 방법의 장점 :

  • 높은 CPU 및 메모리 효율성;
  • 정규식을 사용하는 다른 방법보다 2.5 배 빠른 가장 빠른 솔루션입니다.
  • 입력 문자열에서 부품을 제거하는 데 적합합니다 (로 설정 newValuenull).
  • 원본 .NET C # string.Replace 동작과 동일하지만 예외는 동일합니다.
  • 잘 설명되고 이해하기 쉽다.
  • 더 단순 – 정규식이 없습니다. 정규 표현식은 다양성 때문에 컴파일 속도가 느립니다.
  • 이 방법은 잘 테스트되었으며 다른 솔루션의 무한 루프와 같은 숨겨진 결함은 없으며 등급도 높습니다.

@ AsValeO : Regex 언어 요소와 함께 작동하지 않으므로 보편적 인 방법이 아닙니다.

@ Mike Stillion :이 코드에 문제가 있습니다. new의 텍스트가 이전 텍스트의 수퍼 세트 인 경우 무한 루프가 발생할 수 있습니다.


벤치 마크 방지 :이 솔루션은 @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

1
@MineR 맞습니다. 원래 무한 루프없이 @ Darky711 솔루션을 업데이트 했으므로을 사용했습니다 String. 그러나,이 StringBuilder정말 빠른 것입니다 30-40%String. 솔루션을 업데이트했습니다. 감사합니다;)
Oleg Zarevennyi 2016 년

2
재미있는 접근법. 아마도 성능이 중요 할 때 아마도 나보다 더 나은 것입니다. 일반적으로 공통 공유 코드 라이브러리에 추가하는 방법입니다.
Steve B

2
'nameof'표현식을 사용하면 C # 6.0 이상에서만 유효합니다. VS2013에 있다면 예외에서 피연산자를 삭제하여 사용할 수 있습니다.
LanchPad

주석 처리 된 "// if (oldValue.Equals (newValue, compareType))"의 경우 compareType을 StringComparison.Ordinal?
Roger Willcocks

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
그리고 탈출은 우리의 삶을 덜 번거롭게 만듭니다 :-) Regex.Replace (input, Regex.Escape (search), replacement.Replace ( "$", "$$"), RegexOptions.IgnoreCase);
Vman

29

정규식을 사용하는 많은 제안. 이 확장 방법이 없으면 어떻습니까?

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
이 코드에 문제가 있습니다. 텍스트 경우 새가 텍스트의 상위 집합 오래된 ,이 무한 루프를 생성 할 수 있습니다. FoundAt에 new 를 삽입 하면 FoundAt 의 값 을 new 길이만큼 늘려야 합니다 .
Mike Stillion

comparison매개 변수가 사용되어야한다 IndexOf대신에,StringComparison.CurrentCultureIgnoreCase
헨스

@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 함수를 구현하지 않는 것으로 보입니다. 추가 클래스 -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);

대소 문자를 구분하지 않는 문자열의 부족을 처음으로 불평 한 것 같습니다.


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

이전 확장 방법에는 두 가지 추가 확장 방법을 사용합니다.

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

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

2
공감. 그러나 IsNot확장을 너무 심각하게 생각하고 있습니다 :)
nawfal

실망 스럽지만 모든 상황에서 작동하지는 않습니다. 나는 구별 이름을 전달하고 문자열이 백만 자 길이가 될 때까지 추가 한 다음 메모리가 부족합니다.
Bbb

Bbb

정말 좋아 나는.IsNot
ttugates

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 솔루션을 사용하면 다음 예제를 사용할 때 문제가 발생했습니다

문자열 원본 = "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 2019 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.