Есть ли альтернатива string.Replace без учета регистра? - PullRequest
297 голосов
/ 28 октября 2008

Мне нужно найти строку и заменить все вхождения %FirstName% и %PolicyAmount% значением, извлеченным из базы данных. Проблема в том, что заглавные буквы FirstName меняются. Это мешает мне использовать метод String.Replace(). Я видел веб-страницы на эту тему, которые предлагают

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

Однако по какой-то причине, когда я пытаюсь заменить %PolicyAmount% на $0, замена никогда не происходит. Я предполагаю, что это как-то связано с тем, что знак доллара является зарезервированным символом в регулярном выражении.

Есть ли другой метод, который я могу использовать, который не предусматривает дезинфекции ввода для работы со специальными символами регулярных выражений?

Ответы [ 15 ]

1 голос
/ 15 августа 2014

Вот еще один вариант выполнения замен 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();
    }
0 голосов
/ 20 июля 2018

Позвольте мне сделать мое дело, и тогда вы можете разорвать меня на куски, если хотите.

Regex не является решением этой проблемы - слишком медленно и, по большому счету, требует много памяти.

StringBuilder намного лучше, чем искажение строк.

Поскольку это будет метод расширения для дополнения string.Replace, я считаю, что важно сопоставить, как это работает, поэтому важно создавать исключения для тех же проблем с аргументами, что и возврат исходной строки, если замена не была сделана. 1008 *

Я считаю, что наличие параметра StringComparison не очень хорошая идея. Я попробовал, но тестовый пример, упомянутый Майклом-Лю, показал проблему: -

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

Хотя IndexOf будет совпадать, существует несоответствие между длиной совпадения в исходной строке (1) и oldValue.Length (2). Это проявилось в появлении IndexOutOfRange в некоторых других решениях, когда oldValue.Length был добавлен к текущей позиции совпадения, и я не смог найти способ обойти это. В любом случае, Regex не подходит к этому случаю, поэтому я выбрал прагматичное решение, используя только 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();
    }
0 голосов
/ 19 января 2018

(Так как все это делают). Вот моя версия (с нулевыми проверками и корректным выходом и заменой при выходе) ** Вдохновленные из Интернета и других версий:

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");
0 голосов
/ 28 октября 2008

Метод регулярного выражения должен работать. Однако то, что вы также можете сделать, это нижний регистр строки из базы данных, нижний регистр% переменных%, который у вас есть, а затем найти позиции и длины в строчной строке из базы данных. Помните, что позиции в строке не меняются только потому, что ее нижний регистр.

Затем, используя цикл, который идет в обратном направлении (проще, если вы этого не сделаете, вам нужно будет вести текущий подсчет того, куда переходят более поздние точки), удалить из вашей строки без нижестоящей строки из базы данных% переменные% на их положение и длину и вставьте значения замены.

0 голосов
/ 28 октября 2008
Regex.Replace(strInput, strToken.Replace("$", "[$]"), strReplaceWith, RegexOptions.IgnoreCase);
...