代码之家  ›  专栏  ›  技术社区  ›  Aheho

是否有字符串的替代项。替换不区分大小写的字符串?

  •  293
  • Aheho  · 技术社区  · 17 年前

    我需要搜索一个字符串并替换所有出现的 %FirstName% 和 %PolicyAmount% 从数据库中提取值。问题是名字的大小写各不相同。这阻止了我使用 String.Replace() 方法。我看过关于这个主题的网页

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

    但是,由于某种原因,当我尝试替换 %保费金额% 具有 $0 ,更换永远不会发生。我认为这与美元符号在regex中是保留字符有关。

    我是否可以使用另一种方法来处理regex特殊字符,而不需要清理输入?

    15 回复  |  直到 8 年前
        1
  •  126
  •   Todd White    17 年前

    From MSDN
    $0-“替换与组编号(十进制)匹配的最后一个子字符串。”

    在.NET正则表达式中,组0始终是整个匹配项。为了一个字面上的美元,你需要

    string value = Regex.Replace("%PolicyAmount%", "%PolicyAmount%", @"$$0", RegexOptions.IgnoreCase);
    
        2
  •  291
  •   Neuron MonoThreaded    8 年前

    好像 string.Replace 应该 过载需要 StringComparison 争论。既然没有,你可以尝试这样的方法:

    public static string ReplaceString(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();
    }
    
        3
  •  35
  •   Community Mohan Dere    9 年前

    一组令人困惑的答案,部分原因是问题的标题实际上是 许多的 大于所问的特定问题。看完后,我不确定是否有任何答案是几处编辑远离吸收所有的好东西,所以我想我会尝试总结。

    这里有一个扩展方法,我认为它可以避免这里提到的陷阱,并提供最广泛适用的解决方案。

    public static string ReplaceCaseInsensitiveFind(this string str, string findMe,
        string newValue)
    {
        return Regex.Replace(str,
            Regex.Escape(findMe),
            Regex.Replace(newValue, "\\$[0-9]+", @"$$$0"),
            RegexOptions.IgnoreCase);
    }
    

    所以…

    不幸的是, @HA 's comment that you have to Escape all three isn't correct . 初始值和 newValue 不需要。

    注: 但是,你必须逃跑 $ 在您要插入的新值中 如果它们是“捕获值”标记的一部分 .因此,regex.replace.inside the regex.replace[sic]中的三美元符号。如果没有这个,像这样的事情就会中断…

    "This is HIS fork, hIs spoon, hissssssss knife.".ReplaceCaseInsensitiveFind("his", @"he$0r")

    错误如下:

    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.
    

    告诉你吧,我知道那些喜欢使用regex的人觉得他们的使用可以避免错误,但是我仍然倾向于字节嗅探字符串(但只有在读了 Spolsky on encodings )确保你得到了你想要的重要用例。让我想起克罗克福德 insecure regular expressions “有一点。我们经常编写regexp,允许我们想要的(如果幸运的话),但无意中允许更多的(例如 $10 真的是我的newValue regexp中有效的“capture value”字符串吗?因为我们考虑不够周到。这两种方法都有价值,并且都鼓励不同类型的无意错误。通常很容易低估复杂性。

    怪怪的 $ 逃走 Regex.Escape 并没有像 $0 正如我在替代价值方面所预期的那样)让我一时发疯。编程很难(C)1842

        4
  •  30
  •   rboarman    15 年前

    这里有一个扩展方法。不知道我在哪里找到的。

    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;
        }
    
    }
    
        5
  •  30
  •   DeveloperDan Asaf    10 年前

    似乎最简单的方法就是使用.NET附带的替换方法,该方法自.NET 1.0以来一直存在:

    string res = Microsoft.VisualBasic.Strings.Replace(res, 
                                       "%PolicyAmount%", 
                                       "$0", 
                                       Compare: Microsoft.VisualBasic.CompareMethod.Text);
    

    要使用此方法,必须添加对Microsoft.VisualBasic组件的引用。此程序集是.NET运行时的标准部分,不是额外下载或标记为已过时。

        6
  •  10
  •   Karl Glennon    14 年前
        /// <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);
        }
    
        7
  •  8
  •   Vitoc    10 年前

    受cfeduke答案的启发,我创建了这个函数,它使用indexof在字符串中查找旧值,然后用新值替换它。我在一个处理数百万行的ssis脚本中使用了这个方法,regex方法比这个慢得多。

    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;
    }
    
        8
  •  6
  •   Community Mohan Dere    9 年前

    扩展在 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();
         }
    }
    
        9
  •  3
  •   Mark Cranness    10 年前

    基于Jeff Reddy的回答,进行了一些优化和验证:

    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();
    }
    
        10
  •  2
  •   Allanrbo    15 年前

    类似于C.Dragon的版本,但如果您只需要一个替换:

    int n = myText.IndexOf(oldValue, System.StringComparison.InvariantCultureIgnoreCase);
    if (n >= 0)
    {
        myText = myText.Substring(0, n)
            + newValue
            + myText.Substring(n + oldValue.Length);
    }
    
        11
  •  1
  •   Brandon    12 年前

    这是执行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();
        }
    
        12
  •  0
  •   Joel Coehoorn    17 年前
    Regex.Replace(strInput, strToken.Replace("$", "[$]"), strReplaceWith, RegexOptions.IgnoreCase);
    
        13
  •  0
  •   cfeduke    17 年前

    正则表达式方法应该有效。但是,您还可以做的是小写数据库中的字符串、小写的变量百分比,然后从数据库中找到小写字符串中的位置和长度。记住,字符串中的位置不会因为其小写而改变。

    然后,使用一个反向循环(如果不这样做,则更容易,您将必须保留后续点移动到的位置的运行计数)从数据库中的非小写字符串中删除%variables%(按位置和长度),并插入替换值。

        14
  •  0
  •   Fredrik Johansson    8 年前

    (因为每个人都在为此努力)。以下是我的版本(带有空检查和正确的输入和替换转义)**灵感来自互联网和其他版本:

    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");
    
        15
  •  0
  •   Simon Hewitt    8 年前

    我来做我的案子,如果你愿意,你可以把我撕成碎片。

    Regex并不是这个问题的答案——速度太慢,内存不足,相对来说。

    StringBuilder比字符串管理要好得多。

    因为这将是补充 string.Replace ,我认为匹配它的工作方式很重要-因此,对于相同的参数问题抛出异常非常重要,因为如果没有进行替换,则返回原始字符串也是很重要的。

    我认为使用StringComparison参数不是一个好主意。 我确实试过了,但是刘先生最初提到的测试用例显示出一个问题:

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

    虽然indexof将匹配,但源字符串(1)中匹配的长度与oldvalue.length(2)之间不匹配。当oldValue.length被添加到当前匹配位置时,这通过在其他一些解决方案中导致indexoutofrange而表现出来,我找不到解决这一问题的方法。 无论如何,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();
        }
    
    推荐文章