Как игнорировать регистр в 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
в левой строке?
Ответы
Ответ 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;
}