Есть ли альтернатива string.Заменить это без учета регистра?
мне нужно найти строку и заменить все вхождения %FirstName% и %PolicyAmount% со значением из базы данных. Проблема капитализации имя меняется. Это мешает мне использовать String.Replace() метод. Я видел веб-страницы на эту тему, которые предлагают
Regex.Replace(strInput, strToken, strReplaceWith, RegexOptions.IgnoreCase);
однако по какой-то причине, когда я пытаюсь заменить %PolicyAmount% С замена не происходит. Я предполагаю, что это как-то связано с тем, что знак доллара является зарезервированным символом в регулярном выражении.
есть ли другой метод, который я могу использовать, который не включает в себя очистку ввода для работы со специальными символами регулярных выражений?
15 ответов:
от MSDN
$0 - " заменяет последнюю подстроку, соответствующую номеру группы (десятичный)."в .NET регулярных выражений группа 0 всегда все совпадение. Для литерала $ вам нужно
string value = Regex.Replace("%PolicyAmount%", "%PolicyAmount%", @"$", RegexOptions.IgnoreCase);
вид запутанной группы ответов, отчасти потому, что название вопроса на самом деле много больше, чем конкретный заданный вопрос. После прочтения я не уверен, что какой-либо ответ-это несколько изменений от ассимиляции всех хороших вещей здесь, поэтому я решил, что попытаюсь подвести итог.
вот метод расширения, который, я думаю, позволяет избежать подводных камней, упомянутых здесь, и обеспечивает наиболее широкое применение решение.
public static string ReplaceCaseInsensitiveFind(this string str, string findMe, string newValue) { return Regex.Replace(str, Regex.Escape(findMe), Regex.Replace(newValue, "\$[0-9]+", @"$$"), RegexOptions.IgnoreCase); }так...
- это метод расширения @MarkRobinson
- этой не пытается пропустить регулярное выражение @Helge (вы действительно должны сделать байт за байтом, если вы хотите, чтобы строка нюхать, как это за пределами регулярного выражения)
- проходит @MichaelLiu ' s отличный тест,
"œ".ReplaceCaseInsensitiveFind("oe", ""), хотя у него, возможно, было немного другое поведение в виду.к сожалению, @HA 's комментарий, что вы должны
Escapeвсе три не правильно. Начальное значение иnewValueне нужно.Примечание: вы, однако, должны бежать
$s в новом значении, которое вы вставляете если они являются частью того, что, по-видимому, является маркером "захваченного значения". Таким образом, три знака доллара в регулярном выражении.Заменить внутри регулярного выражения.Заменить [sic]. Без этого, что-то вроде этого разрыв...
"This is HIS fork, hIs spoon, hissssssss knife.".ReplaceCaseInsensitiveFind("his", @"her")вот ошибка:
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.вот что я вам скажу, я знаю, что люди, которые удобны с регулярным выражением, чувствуют, что их использование позволяет избежать ошибок, но я часто все еще неравнодушен к байтовым обнюхивающим строкам (но только после прочтения Спольски на кодировках), чтобы быть абсолютно уверенным, что вы получаете то, что вы предназначены для важных случаев использования. Напоминает мне Крокфорд на "небезопасные регулярные выражения" немного. Слишком часто мы напишите регулярные выражения, которые позволяют то, что мы хотим (если нам повезет), но непреднамеренно позволяют больше (например, Is
действительно допустимая строка "capture value" в моем регулярном выражении newValue, выше? потому что мы были недостаточно внимательны. Оба метода имеют ценность, и оба поощряют различные типы непреднамеренных ошибок. Часто легко недооценивать сложность.странно
$побег (а этоRegex.Escapeне удалось избежать захваченных шаблонов значений, таких каккак я и ожидал в замена значения) сводил меня с ума на некоторое время. Программирование трудно (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; } }
Кажется, самый простой способ-просто использовать метод Replace, который поставляется с .Net и существует с .Net 1.0:
string res = Microsoft.VisualBasic.Strings.Replace(res, "%PolicyAmount%", "", Compare: Microsoft.VisualBasic.CompareMethod.Text);чтобы использовать этот метод, необходимо добавить ссылку на Microsoft.VisualBasic Assembly. Эта сборка является стандартной частью среды выполнения .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 обработка миллионов строк и регулярные выражения-способ был намного медленнее, чем это.
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. Dragon 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(); } }
на основе ответа Джеффа Редди, с некоторыми оптимизациями и проверками:
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, но для того, если вам нужна только одна замена:
int n = myText.IndexOf(oldValue, System.StringComparison.InvariantCultureIgnoreCase); if (n >= 0) { myText = myText.Substring(0, n) + newValue + myText.Substring(n + oldValue.Length); }
вот еще один вариант для выполнения замены регулярных выражений, так как не многие люди, похоже, замечают, что совпадения содержат местоположение в строке:
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(); }
метод регулярного выражения должен работать. Однако то, что вы также можете сделать, - это строчная строка из базы данных, строчные переменные%%, которые у вас есть, а затем найдите позиции и длины в строке с нижним регистром из базы данных. Помните, что позиции в строке не меняются только потому, что ее нижняя оболочка.
затем, используя цикл, который идет в обратном направлении (его проще, если вы этого не сделаете, вам придется держать текущий счет того, куда перемещаются более поздние точки) удалить из вашего строка без нижнего регистра из базы данных % переменные% по их положению и длине и вставить значения замены.
(Так как все пытаются это сделать). Вот моя версия (с нулевыми проверками и правильным вводом и заменой) * * вдохновленный со всего интернета и других версий:
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");
позвольте мне сделать мое дело, а затем вы можете разорвать меня в клочья, если хотите.
регулярное выражение не является ответом на эту проблему - слишком медленно и память голодна, относительно говоря.
StringBuilder намного лучше, чем string mangling.
так как это будет метод расширения для дополнения
string.Replace, Я считаю, что важно соответствовать тому, как это работает - поэтому выбрасывание исключений для тех же проблем аргумента важно, как возвращает исходную строку, если a замена не производилась.Я считаю, что наличие параметра StringComparison не является хорошей идеей. Я попробовал, но тестовый случай, первоначально упомянутый Майклом-Лю, показал проблему: -
[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]в то время как IndexOf будет соответствовать, существует несоответствие между длиной соответствия в исходной строке (1) и oldValue.Длина (2). Это проявилось, вызвав IndexOutOfRange в некоторых других решениях, когда oldValue.Длина была добавлена в текущую позицию будет соответствовать и я не мог найти способ обойти это. Регулярное выражение не соответствует случаю в любом случае, поэтому я взял прагматическое решение только с помощью
StringComparison.OrdinalIgnoreCaseна мое решение.мой код похож на другие ответы, но мой поворот заключается в том, что я ищу совпадение, прежде чем приступать к созданию
StringBuilder. Если ни один не найден, то потенциально большое распределение избегается. Затем код становитсяdo{...}while, а не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(); }
Comments