Подтвердить что ты не робот

Как игнорировать регистр в String.replace

string sentence = "We know it contains 'camel' word.";
// Camel can be in different cases:
string s1 = "CAMEL";
string s2 = "CaMEL";
string s3 = "CAMeL";
// ...
string s4 = "Camel";
// ...
string s5 = "camel";

Как заменить "верблюда" в предложении "лошадью", несмотря на string.Replace, не поддерживает ignoreCase в левой строке?

4b9b3361

Ответ 1

Используйте регулярное выражение:

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

Конечно, это также будет соответствовать словам, содержащим верблюда, но не ясно, хотите ли вы этого или нет.

Если вам нужны точные соответствия, вы можете использовать собственный MatchEvaluator.

public static class Evaluators
{
    public static string Wrap( Match m, string original, string format )
    {
        // doesn't match the entire string, otherwise it is a match
        if (m.Length != original.Length)
        {
            // has a preceding letter or digit (i.e., not a real match).
            if (m.Index != 0 && char.IsLetterOrDigit( original[m.Index - 1] ))
            {
                return m.Value;
            }
            // has a trailing letter or digit (i.e., not a real match).
            if (m.Index + m.Length != original.Length && char.IsLetterOrDigit( original[m.Index + m.Length] ))
            {
                return m.Value;
            }
        }
        // it is a match, apply the format
        return string.Format( format, m.Value );
    }
} 

Используется с предыдущим примером, чтобы обернуть совпадение в диапазоне как:

var regex = new Regex( highlightedWord, RegexOptions.IgnoreCase );
foreach (var sentence in sentences)
{
    var evaluator = new MatchEvaluator( match => Evaluators.Wrap( match, sentence, "<span class='red'>{0}</span>" ) );
    Console.WriteLine( regex.Replace( sentence, evaluator ) );
}

Ответ 2

Добавьте метод расширения для строки, чтобы сделать трюк:

Использование:

string yourString = "TEXTTOREPLACE";
yourString.Replace("texttoreplace", "Look, I Got Replaced!", StringComparison.OrdinalIgnoreCase);

код:

using System;
using System.Collections.Generic;
using System.IO;

public static class Extensions
{
    public static string Replace(this string source, string oldString, string newString, StringComparison comp)
    {
        int index = source.IndexOf(oldString, comp);

        // Determine if we found a match
        bool MatchFound = index >= 0;

        if (MatchFound)
        {
            // Remove the old text
            source = source.Remove(index, oldString.Length);

            // Add the replacemenet text
            source = source.Insert(index, newString);            
        }

        return source;
    }
}

Ответ 3

Здесь используется метод расширения с использованием StringComparison, используя string.IndexOf:

    [Pure]
    public static string Replace(this string source, string oldValue, string newValue, StringComparison comparisonType)
    {
        if (source.Length == 0 || oldValue.Length == 0)
            return source;

        var result = new System.Text.StringBuilder();
        int startingPos = 0;
        int nextMatch;
        while ((nextMatch = source.IndexOf(oldValue, startingPos, comparisonType)) > -1)
        {
            result.Append(source, startingPos, nextMatch - startingPos);
            result.Append(newValue);
            startingPos = nextMatch + oldValue.Length;
        }
        result.Append(source, startingPos, source.Length - startingPos);

        return result.ToString();
    }

Btw, здесь также аналогичный Содержит-метод также принимает StringComparison:

    [Pure]
    public static bool Contains(this string source, string value, StringComparison comparisonType)
    {
        return source.IndexOf(value, comparisonType) >= 0;
    }

Некоторые тесты:

[TestFixture]
public class ExternalTests
{
    private static string[] TestReplace_args =
        {
            "ab/B/c/ac",
            "HELLO World/Hello/Goodbye/Goodbye World",
            "Hello World/world/there!/Hello there!",
            "hello WoRlD/world/there!/hello there!",
            "///",
            "ab///ab",
            "/ab/cd/",
            "a|b|c|d|e|f/|//abcdef",
            "a|b|c|d|e|f|/|/:/a:b:c:d:e:f:",
        };

    [Test, TestCaseSource("TestReplace_args")]
    public void TestReplace(string teststring)
    {
        var split = teststring.Split("/");
        var source = split[0];
        var oldValue = split[1];
        var newValue = split[2];
        var result = split[3];
        Assert.That(source.Replace(oldValue, newValue, StringComparison.OrdinalIgnoreCase), Is.EqualTo(result));
    }
}

Ответ 4

Использовать StringComparison из-за его удобного OrdinalIgnoreCase

    string sentence = "We know it contains 'camel' word."; 
    string wordToFind = "camel";
    string replacementWord = "horse";

    int index = sentence.IndexOf(wordToFind , StringComparison.OrdinalIgnoreCase)
    // Did we match the word regardless of case
    bool match = index >= 0;

    // perform the replace on the matched word
    if(match) {
        sentence = sentence.Remove(index, wordToFind.Length)
        sentence = sentence.Insert(index, replacementWord)
    }

Конечно, было бы неплохо, если бы класс С# String имел метод ignoreCase(), такой как Java.

Ответ 5

Вот мой метод расширения, в котором сочетаются Tom Beech с рекурсивностью sntbob и более чистое исправление ошибки, указанной в ksun.

код:

public static string Replace(this string source, string oldString, 
                             string newString, StringComparison comparison)
{
    int index = source.IndexOf(oldString, comparison);

    while (index > -1)
    {
        source = source.Remove(index, oldString.Length);
        source = source.Insert(index, newString);

        index = source.IndexOf(oldString, index + newString.Length, comparison);
    }

    return source;
}

Использование:

string source = "banana";
Console.WriteLine(source.Replace("AN", "banana", StringComparison.OrdinalIgnoreCase));

Результат:

bbananabananaa

И, если вы все еще хотите, чтобы рекурсивный характер был необязательным:

код:

public static string Replace(this string source, string oldString, 
                             string newString, StringComparison comparison,
                             bool recursive = true)
{
    int index = source.IndexOf(oldString, comparison);

    while (index > -1)
    {
        source = source.Remove(index, oldString.Length);
        source = source.Insert(index, newString);

        if (!recursive)
        {
            return source;
        }
        index = source.IndexOf(oldString, index + newString.Length, comparison);
    }

    return source;
}

Использование:

string source = "banana";
Console.WriteLine(source.Replace("AN", "banana", StringComparison.OrdinalIgnoreCase, false));

Результат:

bbananaana

Ответ 6

Вы также можете использовать String.IndexOf

http://msdn.microsoft.com/en-us/library/system.string.indexof.aspx

Вы можете получить немного лучшую производительность, делая это так, как с RegExpressions (я ненавижу их, потому что они не интуитивно понятны и легко завинчиваются, хотя этот простой вызов функции .Net абстрагирует фактический беспорядочный RegEx и не обеспечивает много места для ошибки), но это, вероятно, не беспокоит вас; компьютеры ДЕЙСТВИТЕЛЬНО быстры в эти дни, правильно?:) Перегрузка для IndexOf, которая принимает объект StringComparison, позволяет необязательно проигнорировать регистр, а поскольку IndexOf возвращает первое вхождение из указанной позиции, вам придется закодировать цикл для обработки строки с несколькими вхождениями.

Ответ 7

    public static string CustomReplace(string srcText, string toFind, string toReplace, bool matchCase, bool replace0nce)
    {
        StringComparison sc = StringComparison.OrdinalIgnoreCase;
        if (matchCase)
            sc = StringComparison.Ordinal;

        int pos;
        while ((pos = srcText.IndexOf(toFind, sc)) > -1)
        {
            srcText = srcText.Remove(pos, toFind.Length);
            srcText = srcText.Insert(pos, toReplace);

            if (replace0nce)
                break;
        }

        return srcText;
    }

Ответ 8

Это может быть не так эффективно, как некоторые другие ответы, но я вроде как функция CustomReplace, написанная sntbob.

Однако в этом есть недостаток. Если замена текста является рекурсивной, это вызовет бесконечный цикл. Например, CustomReplace ( "Я ем бананы!", "An", "banana", false, false) приведет к бесконечному циклу, и строка будет продолжать расти. Например, после 4-й итерации строка будет "Я ем bbbbbananaanaanaanaanaan!"

Если вы хотите заменить только два экземпляра "an" внутри "банана", вам придется использовать другой подход. Я изменил код sntbob для учета этого случая. Я признаю, что он гораздо более запутанный, но он обрабатывает рекурсивные замены.

public static string CustomReplace(string srcText, string toFind, string toReplace, bool matchCase, bool replaceOnce)
    {
        StringComparison sc = StringComparison.OrdinalIgnoreCase;
        if (matchCase)
            sc = StringComparison.Ordinal;

        int pos;
        int previousProcessedLength = 0;
        string alreadyProcessedTxt = "";
        string remainingToProcessTxt = srcText;
        while ((pos = remainingToProcessTxt.IndexOf(toFind, sc)) > -1)
        {
            previousProcessedLength = alreadyProcessedTxt.Length;
            //Append processed text up until the end of the found string and perform replacement
            alreadyProcessedTxt += remainingToProcessTxt.Substring(0, pos + toFind.Length);
            alreadyProcessedTxt = alreadyProcessedTxt.Remove(previousProcessedLength + pos, toFind.Length);
            alreadyProcessedTxt = alreadyProcessedTxt.Insert(previousProcessedLength + pos, toReplace);

            //Remove processed text from remaining
            remainingToProcessTxt = remainingToProcessTxt.Substring(pos + toFind.Length);                

            if (replaceOnce)
                break;
        }

        return alreadyProcessedTxt + remainingToProcessTxt;
    }

Ответ 9

Вот еще одна альтернатива, которая использует StringComparison с расширением.

public static StringBuilder Replace(this StringBuilder original,
                    string oldString, string newString, StringComparison stringComparison)
    {
        if ( newString == null || original == null || string.IsNullOrEmpty(oldString))
            return original;

        int pos = original.ToString().IndexOf(oldString, 0, stringComparison);
        while ( pos >= 0 )
        {
            original.Remove(pos, oldString.Length).Insert(pos, newString);

            pos = original.ToString().IndexOf(oldString, pos + newString.Length + 1, stringComparison);
        }
        return original;
    }