用有效版本替换无效字符列表(如tr)

时间:2011-05-30 00:49:10

标签: c# string

我需要做一些梦想.trReplace

这样的事情
  str = str.trReplace("áéíüñ","aeiu&");

它应该改变这个字符串:

  a stríng with inválid charactérs

为:

  a string with invalid characters

我目前的想法是:

 str = str.Replace("á","a").Replace("é","e").Replace("í","ï"...

 sb = new StringBuilder(str)
 sb.Replace("á","a").
 sb.Replace("é","e")
 sb.Replace("í","ï"...

但我不认为它们对长串有效。

4 个答案:

答案 0 :(得分:4)

理查德有一个很好的答案,但是在较长的字符串上性能可能略有下降(比直接字符串替换慢约25%,如下所示)。我感到很满意,可以进一步了解这一点。实际上StackOverflow上已有几个很好的相关答案如下所示:

Fastest way to remove chars from string

C# Stripping / converting one or more characters

CodeProject上还有一篇很好的文章,涵盖了不同的选项。

http://www.codeproject.com/KB/string/fastestcscaseinsstringrep.aspx

解释为什么理查兹提供的函数答案变得越来越慢,因为更换字符串是一次发生一个字符的事实;因此,如果您有大量非映射字符,则在将字符串重新附加在一起时会浪费额外的周期。因此,如果您想从CodePlex文章中获取几点,您最终会得到一个稍微修改过的Richards答案,如下所示:

private static readonly Char[] ReplacementChars = new[] { 'á', 'é', 'í', 'ü', 'ñ' };
private static readonly Dictionary<Char, Char> ReplacementMappings = new Dictionary<Char, Char>
                                                               {
                                                                 { 'á', 'a'},
                                                                 { 'é', 'e'},
                                                                 { 'í', 'i'},
                                                                 { 'ü', 'u'},
                                                                 { 'ñ', '&'}
                                                               };

private static string Translate(String source)
{
  var startIndex = 0;
  var currentIndex = 0;
  var result = new StringBuilder(source.Length);

  while ((currentIndex = source.IndexOfAny(ReplacementChars, startIndex)) != -1)
  {
    result.Append(source.Substring(startIndex, currentIndex - startIndex));
    result.Append(ReplacementMappings[source[currentIndex]]);

    startIndex = currentIndex + 1;
  }

  if (startIndex == 0)
    return source;

  result.Append(source.Substring(startIndex));

  return result.ToString();
}

注意并非所有边缘情况都经过测试。

注意可以用ReplacementMappings.Keys.ToArray()替换ReplacementChars,但费用不小。

假设并非每个字符都是替换字符,那么这实际上会比straigt字符串替换稍快一些(再次大约20%)。

话虽如此,请记住在考虑性能成本时,我们实际谈论的是什么......在这种情况下......优化解决方案与原始解决方案之间的差异在1000字符串上超过100,000次迭代约为1秒。

无论哪种方式,只是想在这个问题的答案中添加一些信息。

答案 1 :(得分:2)

我为ICAO Passport做了类似的事情。这些名字必须是“音译”。基本上我有一个char到char映射的字典。

Dictionary<char, char> mappings;

static public string Translate(string s)
{
   var t = new StringBuilder(s.Length);
   foreach (char c in s)
   {
      char to;
      if (mappings.TryGetValue(c, out to))
         t.Append(to);
      else
         t.Append(c);
    }
    return t.ToString();
 }

答案 2 :(得分:1)

你想要的是一种通过字符串一次并完成所有替换的方法。如果你想要效率,我不确定正则表达式是最好的方法。很可能在for循环中用于测试每个字符的case开关(对于你想要替换的所有字符)更快。我想介绍两种方法。

答案 3 :(得分:0)

最好使用char数组代替Stringbuilder。 索引器比调用Append方法更快,因为:

  • 将所有局部变量推入堆栈
  • 移动到附加地址
  • 返回地址
  • 从堆栈中弹出所有局部变量

下面的示例大约快20%(取决于您的硬件和输入字符串)

static Dictionary<char, char> mappings;
public static string TranslateV2(string s)
{
    var len = s.Length;
    var array = new char[len];
    char c;

    for (var index = 0; index < len; index++)
    {
        c = s[index];
        if (mappings.ContainsKey(c))
            array[index] = mappings[c];
        else
            array[index] = c;
    }

    return new string(array);
}