문제

문자열을 검색하고 다음 항목을 모두 바꿔야 합니다. %FirstName% 그리고 %PolicyAmount% 데이터베이스에서 가져온 값으로.문제는 FirstName의 대문자 사용이 다양하다는 것입니다.그로 인해 내가 String.Replace() 방법.나는 주제에 관해 다음과 같은 내용을 제안하는 웹페이지를 본 적이 있습니다.

Regex.Replace(strInput, strToken, strReplaceWith, RegexOptions.IgnoreCase);

그러나 어떤 이유로 교체하려고 하면 %PolicyAmount% ~와 함께 $0, 교체가 발생하지 않습니다.나는 그것이 정규식에서 예약된 문자인 달러 기호와 관련이 있다고 가정합니다.

정규식 특수 문자를 처리하기 위해 입력을 삭제하지 않고 사용할 수 있는 다른 방법이 있습니까?

도움이 되었습니까?

해결책

MSDN에서
$ 0- "그룹 번호 번호 (소수점)와 일치하는 마지막 하부 스트링을 대체합니다."

.NET 정규 표현식 그룹 0은 항상 전체 경기입니다. 문자 그대로 $를 위해 필요합니다

string value = Regex.Replace("%PolicyAmount%", "%PolicyAmount%", @"$$0", RegexOptions.IgnoreCase);

다른 팁

것 같아 string.Replace ~해야 한다 시간이 걸리는 과부하가 있습니다 StringComparison 논쟁. 그렇지 않기 때문에 다음과 같은 것을 시도 할 수 있습니다.

public static string ReplaceString(string str, string oldValue, string newValue, StringComparison comparison)
{
    StringBuilder sb = new StringBuilder();

    int previousIndex = 0;
    int index = str.IndexOf(oldValue, comparison);
    while (index != -1)
    {
        sb.Append(str.Substring(previousIndex, index - previousIndex));
        sb.Append(newValue);
        index += oldValue.Length;

        previousIndex = index;
        index = str.IndexOf(oldValue, index, comparison);
    }
    sb.Append(str.Substring(previousIndex));

    return sb.ToString();
}

질문 제목이 실제로 다음과 같기 때문에 일종의 혼란스러운 답변 그룹입니다. 많이 질문되는 특정 질문보다 더 큽니다.끝까지 읽어본 후, 여기의 모든 좋은 내용을 동화하는 데 몇 가지 수정을 가하는 것이 답인지 확신할 수 없어서 요약해 볼까 생각했습니다.

여기에 언급된 함정을 피하고 가장 광범위하게 적용 가능한 솔루션을 제공하는 확장 방법이 있습니다.

public static string ReplaceCaseInsensitiveFind(this string str, string findMe,
    string newValue)
{
    return Regex.Replace(str,
        Regex.Escape(findMe),
        Regex.Replace(newValue, "\\$[0-9]+", @"$$$0"),
        RegexOptions.IgnoreCase);
}

그래서...

안타깝게도, @HA 님의 멘트는 꼭 해봐야 할 것 같아요 Escape 세 가지 모두 옳지 않아요.초기값과 newValue 그럴 필요는 없습니다.

메모: 그래도 탈출해야지 $삽입하려는 새 값에 있습니다. "캡처된 값" 마커로 보이는 것의 일부인 경우.따라서 Regex.Replace [sic] 내부의 Regex.Replace에 있는 3개의 달러 기호입니다.그게 없으면 이런게 깨져요...

"This is HIS fork, hIs spoon, hissssssss knife.".ReplaceCaseInsensitiveFind("his", @"he$0r")

오류는 다음과 같습니다.

An unhandled exception of type 'System.ArgumentException' occurred in System.dll

Additional information: parsing "The\hisr\ is\ he\HISr\ fork,\ he\hIsr\ spoon,\ he\hisrsssssss\ knife\." - Unrecognized escape sequence \h.

Regex에 익숙한 사람들은 Regex를 사용하면 오류를 피할 수 있다고 생각합니다. 하지만 저는 종종 문자열을 바이트 스니핑하는 데 부분적인 관심을 갖고 있습니다(그러나 읽은 후에만 가능). 인코딩에 대한 Spolsky) 중요한 사용 사례에 대해 의도한 것을 얻고 있는지 절대적으로 확신할 수 있습니다."에 관한 Crockford가 생각나네요.안전하지 않은 정규식" 조금.우리는 원하는 것을 허용하지만(운이 좋다면) 의도치 않게 더 많은 것을 허용하는 정규 표현식을 작성하는 경우가 너무 많습니다(예: Is $10 실제로 위의 newValue 정규식에 유효한 "캡처 값" 문자열이 있습니까?) 왜냐하면 우리가 충분히 사려깊지 않았기 때문입니다.두 방법 모두 가치가 있으며 서로 다른 유형의 의도하지 않은 오류를 조장합니다.복잡성을 과소평가하기 쉬운 경우가 많습니다.

그거 이상해 $ 탈출 (그리고 Regex.Escape 다음과 같은 캡처된 값 패턴을 벗어나지 않았습니다. $0 대체 값에서 예상했던 것처럼) 한동안 나를 화나게 만들었습니다.프로그래밍은 어렵다(c) 1842

확장 방법은 다음과 같습니다. 내가 어디서 찾았는지 잘 모르겠습니다.

public static class StringExtensions
{
    public static string Replace(this string originalString, string oldValue, string newValue, StringComparison comparisonType)
    {
        int startIndex = 0;
        while (true)
        {
            startIndex = originalString.IndexOf(oldValue, startIndex, comparisonType);
            if (startIndex == -1)
                break;

            originalString = originalString.Substring(0, startIndex) + newValue + originalString.Substring(startIndex + oldValue.Length);

            startIndex += newValue.Length;
        }

        return originalString;
    }

}

가장 쉬운 방법은 단순히 .NET으로 배송되는 교체 방법을 사용하고 .NET 1.0 이후 주변에있는 것입니다.

string res = Microsoft.VisualBasic.Strings.Replace(res, 
                                   "%PolicyAmount%", 
                                   "$0", 
                                   Compare: Microsoft.VisualBasic.CompareMethod.Text);

이 방법을 사용하려면 Microsoft.visualBasic Assemblly에 대한 참조를 추가해야합니다. 이 어셈블리는 .NET 런타임의 표준 부분이며 추가 다운로드 또는 쓸모없는 것으로 표시되지 않습니다.

    /// <summary>
    /// A case insenstive replace function.
    /// </summary>
    /// <param name="originalString">The string to examine.(HayStack)</param>
    /// <param name="oldValue">The value to replace.(Needle)</param>
    /// <param name="newValue">The new value to be inserted</param>
    /// <returns>A string</returns>
    public static string CaseInsenstiveReplace(string originalString, string oldValue, string newValue)
    {
        Regex regEx = new Regex(oldValue,
           RegexOptions.IgnoreCase | RegexOptions.Multiline);
        return regEx.Replace(originalString, newValue);
    }

Cfeduke의 답변에서 영감을 얻은 나는 Indexof를 사용하여 문자열에서 이전 값을 찾은 다음 새 값으로 대체하는이 기능을 만들었습니다. 나는 이것을 수백만 행을 처리하는 SSIS 스크립트에서 사용했으며, Regex-Method는 이것보다 느 렸습니다.

public static string ReplaceCaseInsensitive(this string str, string oldValue, string newValue)
{
    int prevPos = 0;
    string retval = str;
    // find the first occurence of oldValue
    int pos = retval.IndexOf(oldValue, StringComparison.InvariantCultureIgnoreCase);

    while (pos > -1)
    {
        // remove oldValue from the string
        retval = retval.Remove(pos, oldValue.Length);

        // insert newValue in it's place
        retval = retval.Insert(pos, newValue);

        // check if oldValue is found further down
        prevPos = pos + newValue.Length;
        pos = retval.IndexOf(oldValue, prevPos, StringComparison.InvariantCultureIgnoreCase);
    }

    return retval;
}

확장 C. 드래곤 76그의 코드를 기본값을 과부하시키는 확장으로 만들어 인기있는 답변 Replace 방법.

public static class StringExtensions
{
    public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
    {
        StringBuilder sb = new StringBuilder();

        int previousIndex = 0;
        int index = str.IndexOf(oldValue, comparison);
        while (index != -1)
        {
            sb.Append(str.Substring(previousIndex, index - previousIndex));
            sb.Append(newValue);
            index += oldValue.Length;

            previousIndex = index;
            index = str.IndexOf(oldValue, index, comparison);
        }
        sb.Append(str.Substring(previousIndex));
        return sb.ToString();
     }
}

Jeff Reddy의 답변을 기반으로 일부 최적화 및 유효성 검사를 기반으로합니다.

public static string Replace(string str, string oldValue, string newValue, StringComparison comparison)
{
    if (oldValue == null)
        throw new ArgumentNullException("oldValue");
    if (oldValue.Length == 0)
        throw new ArgumentException("String cannot be of zero length.", "oldValue");

    StringBuilder sb = null;

    int startIndex = 0;
    int foundIndex = str.IndexOf(oldValue, comparison);
    while (foundIndex != -1)
    {
        if (sb == null)
            sb = new StringBuilder(str.Length + (newValue != null ? Math.Max(0, 5 * (newValue.Length - oldValue.Length)) : 0));
        sb.Append(str, startIndex, foundIndex - startIndex);
        sb.Append(newValue);

        startIndex = foundIndex + oldValue.Length;
        foundIndex = str.IndexOf(oldValue, startIndex, comparison);
    }

    if (startIndex == 0)
        return str;
    sb.Append(str, startIndex, str.Length - startIndex);
    return sb.ToString();
}

C. Dragon 's와 유사한 버전이지만 단일 교체 만 필요하다면 :

int n = myText.IndexOf(oldValue, System.StringComparison.InvariantCultureIgnoreCase);
if (n >= 0)
{
    myText = myText.Substring(0, n)
        + newValue
        + myText.Substring(n + oldValue.Length);
}

Regex 대체를 실행하기 위한 또 다른 옵션은 다음과 같습니다. 일치 항목에 문자열 내의 위치가 포함되어 있다는 사실을 알아차린 사람이 많지 않은 것 같기 때문입니다.

    public static string ReplaceCaseInsensative( this string s, string oldValue, string newValue ) {
        var sb = new StringBuilder(s);
        int offset = oldValue.Length - newValue.Length;
        int matchNo = 0;
        foreach (Match match in Regex.Matches(s, Regex.Escape(oldValue), RegexOptions.IgnoreCase))
        {
            sb.Remove(match.Index - (offset * matchNo), match.Length).Insert(match.Index - (offset * matchNo), newValue);
            matchNo++;
        }
        return sb.ToString();
    }
Regex.Replace(strInput, strToken.Replace("$", "[$]"), strReplaceWith, RegexOptions.IgnoreCase);

정규 표현 방법이 작동해야합니다. 그러나 당신이 할 수있는 것은 데이터베이스의 문자열을 소문자로, 소문자 % 변수 %를 가지고있는 다음 데이터베이스에서 낮은 CASED 문자열에서 위치와 길이를 찾습니다. 스트링의 위치는 낮은 케이스가 있기 때문에 변경되지 않습니다.

그런 다음 리버스로 들어가는 루프를 사용하여 (더 쉬워지면, 나중에 포인트가 이동하는 위치의 실행 수를 유지해야 함) 데이터베이스에서 비 선장 케이스 문자열에서 % 변수 %를 해당 위치 및 위치에 따라 % 변수를 제거합니다. 길이와 교체 값을 삽입하십시오.

(모두가 이것을 촬영하고 있기 때문에). 내 버전은 다음과 같습니다 (Null Checks 및 올바른 입력 및 교체 탈출) ** 인터넷 및 기타 버전에서 영감을 얻었습니다.

using System;
using System.Text.RegularExpressions;

public static class MyExtensions {
    public static string ReplaceIgnoreCase(this string search, string find, string replace) {
        return Regex.Replace(search ?? "", Regex.Escape(find ?? ""), (replace ?? "").Replace("$", "$$"), RegexOptions.IgnoreCase);          
    }
}

용법:

var result = "This is a test".ReplaceIgnoreCase("IS", "was");

내가 내 사건을 만들어 보자, 당신이 원한다면 나를 찢어 버릴 수 있습니다.

Regex는이 문제에 대한 답이 아닙니다. 비교적 말하면 너무 느리고 추억이 굶주리고 있습니다.

StringBuilder는 String Mangling보다 훨씬 낫습니다.

이것은 보충의 확장 방법이기 때문입니다 string.Replace, 나는 그것이 어떻게 작동하는지에 맞는 것이 중요하다고 생각합니다. 따라서 대체품을 만들지 않은 경우 원래 문자열을 반환하는 것과 같은 인수 문제에 대한 예외를 던지는 것이 중요합니다.

String Comparison 매개 변수를 갖는 것은 좋은 생각이 아니라고 생각합니다. 나는 그것을 시도했지만 Michael-Liu가 원래 언급 한 테스트 사례는 다음을 보여 주었다.

[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]

Indexof가 일치하는 동안 소스 문자열 (1)과 OldValue.length (2)의 일치 길이 사이에는 불일치가 있습니다. OldValue.length가 현재 일치 위치에 추가되었을 때 다른 솔루션에서 IndexoutOfRange를 유발함으로써 이렇게하면이 주변의 길을 찾을 수 없었습니다. REGEX는 어쨌든 케이스와 일치하지 않기 때문에 사용에 대한 실용적인 솔루션을 가져갔습니다. StringComparison.OrdinalIgnoreCase 내 해결책을 위해.

내 코드는 다른 답변과 비슷하지만 내 비틀기는 StringBuilder. 아무것도 발견되지 않으면 잠재적으로 큰 할당을 피할 수 있습니다. 코드는 a가됩니다 do{...}while a보다는 while{...}

나는 다른 답변에 대해 광범위한 테스트를 수행했으며 이것은 분수로 더 빨리 나왔고 약간의 메모리를 사용했습니다.

    public static string ReplaceCaseInsensitive(this string str, string oldValue, string newValue)
    {
        if (str == null) throw new ArgumentNullException(nameof(str));
        if (oldValue == null) throw new ArgumentNullException(nameof(oldValue));
        if (oldValue.Length == 0) throw new ArgumentException("String cannot be of zero length.", nameof(oldValue));

        var position = str.IndexOf(oldValue, 0, StringComparison.OrdinalIgnoreCase);
        if (position == -1) return str;

        var sb = new StringBuilder(str.Length);

        var lastPosition = 0;

        do
        {
            sb.Append(str, lastPosition, position - lastPosition);

            sb.Append(newValue);

        } while ((position = str.IndexOf(oldValue, lastPosition = position + oldValue.Length, StringComparison.OrdinalIgnoreCase)) != -1);

        sb.Append(str, lastPosition, str.Length - lastPosition);

        return sb.ToString();
    }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top