如何在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";
如何用'马'replace'骆驼'尽pipestring.Replace
不支持ignoreCase
左string?
使用正则expression式:
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 (ie, not a real match). if (m.Index != 0 && char.IsLetterOrDigit( original[m.Index - 1] )) { return m.Value; } // has a trailing letter or digit (ie, 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 ) ); }
为string添加一个扩展方法来实现这个技巧:
用法:
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; } }
这里是一个扩展方法,采用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(); }
顺便说一句,这里也是一个类似的Contains方法也采取StringComparison:
[Pure] public static bool Contains(this string source, string value, StringComparison comparisonType) { return source.IndexOf(value, comparisonType) >= 0; }
一些testing:
[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)); } }
由于其方便的OrdinalIgnoreCase
实用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) }
如果C#String类有一个像Java这样的ignoreCase()
方法,那肯定会很好。
这里是我的扩展方法,它结合了汤姆·比奇的和sntbob的recursion性,以及对ksun指出的bug的更清晰的修复。
码:
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
而且,如果您仍然希望recursion性质是可选的:
码:
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
你也可以使用String.IndexOf
http://msdn.microsoft.com/en-us/library/system.string.indexof.aspx
这样做比使用RegExpressions(我憎恨他们,因为他们不直观,容易搞砸,虽然这个简单的.Net函数调用提取实际凌乱的正则expression式,并没有提供太多的空间错误),但这可能不是你的问题; 电脑真的很快这些天,对吧? :)使用StringComparison对象的IndexOf的重载允许您可选地忽略大小写,并且因为IndexOf返回指定位置的第一个匹配项,您必须编写一个循环来处理具有多个匹配项的string。
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; }
它可能不如其他一些答案有效,但我有点像sntbob写的CustomReplace函数。
但是,它有一个缺陷。 如果文本replace是recursion的,将导致无限循环。 例如,CustomReplace(“我吃香蕉!”,“an”,“banana”,false,false)会导致无限循环,并且string会继续变大。 例如,在第四次迭代之后,string将是“我吃bbbbbananaanaanaanaanas!”
如果你只想replace“香蕉”中的“an”的两个实例,那么你就不得不采取另一种方法。 我修改了sntbob的代码来解释这种情况。 我承认这更复杂,但它处理recursionreplace。
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; }
这是另一个使用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; }