使用正则表达式的字符串到字典(想要优化)

时间:2011-04-16 13:07:51

标签: c# .net regex optimization dictionary

我在格式"$0Option one$1$Option two$2$Option three"(等)上有字符串,我希望将其转换为字典,其中每个数字对应一个选项。我目前有一个解决这个问题的工作方案,但是因为我导入的每个条目(几千个)都调用了这个方法,所以我希望它尽可能地优化。

public Dictionary<string, int> GetSelValsDictBySelValsString(string selectableValuesString)
{
    // Get all numbers in the string. 
    var correspondingNumbersArray = Regex.Split(selectableValuesString, @"[^\d]+").Where(x => (!String.IsNullOrWhiteSpace(x))).ToArray();

    List<int> correspondingNumbers = new List<int>();

    int number;
    foreach (string s in correspondingNumbersArray)
    {
        Int32.TryParse(s, out number);
        correspondingNumbers.Add(number);
    }

    selectableValuesString = selectableValuesString.Replace("$", "");

    var selectableStringValuesArray = Regex.Split(selectableValuesString, @"[\d]+").Where(x => (!String.IsNullOrWhiteSpace(x))).ToArray();

    var selectableValues = new Dictionary<string, int>();

    for (int i = 0; i < selectableStringValuesArray.Count(); i++)
    {
        selectableValues.Add(selectableStringValuesArray.ElementAt(i), correspondingNumbers.ElementAt(i));
    }

    return selectableValues;
}

2 个答案:

答案 0 :(得分:4)

在您的代码中引起我注意的第一件事是它处理输入字符串三次:使用Split()处理两次,使用Replace()处理一次。对于这项工作,Matches()方法是比Split()更好的工具。有了它,您可以一次性提取所需的一切。它使代码也更容易阅读。

我注意到的第二件事是所有这些循环和中间对象。你已经在使用LINQ了; 真的使用它,你可以消除所有杂乱提高性能。看看:

public static Dictionary<int, string> GetSelectValuesDictionary(string inputString)
{
  return Regex.Matches(inputString, @"(?<key>[0-9]+)\$*(?<value>[^$]+)")
    .Cast<Match>()
    .ToDictionary(
        m => int.Parse(m.Groups["key"].Value),
        m => m.Groups["value"].Value);
}

备注:

  • Cast<Match>()是必要的,因为MatchCollection仅宣传自己为IEnumerable,我们需要它为IEnumerable<Match>
  • 我使用[0-9]代替\d,因为您的值可能包含来自非拉丁文书写系统的数字;在.NET中,\d将它们全部匹配。
  • Matches()这样的静态正则表达式方法会自动缓存Regex对象,但如果要调用此方法(特别是如果你还使用了很多其他正则表达式),你可能想要创建无论如何,一个静态正则表达式对象。如果性能非常重要,您可以在使用时指定Compiled选项。
  • 我的代码与您的代码一样,不会尝试处理格式错误的输入。特别是,如果数字太大,我的将抛出异常,而你的只是将其转换为零。这可能与您的真实代码无关,但我觉得有必要表达我的不安,因为您在没有检查返回值的情况下调用TryParse()。 :/
  • 您也无法确保您的钥匙是唯一的。就像@Gabe一样,我翻转它使用数值作为键,因为它们恰好是唯一的而字符串值不是。我也相信,这对您的真实数据来说也不是问题。 ;)

答案 1 :(得分:2)

您的selectableStringValuesArray实际上不是数组!这意味着每次您索引它(使用ElementAt或用Count计算)时,它必须重新运行正则表达式并遍历查找非空格的结果列表。你需要这样的东西:

var selectableStringValuesArray = Regex.Split(selectableValuesString, @"[\d]+").Where(x => (!String.IsNullOrWhiteSpace(x))).ToArray();

您还应该修复correspondingNumbersString,因为它有同样的问题。

我看到你正在使用C#4,所以你可以使用Zip来组合列表,然后就不必创建数组或使用任何循环。您可以像这样创建字典:

return correspondingNumbersString.Zip(selectableStringValuesArray,
       (number, str) => new KeyValuePair<int, string>(int.Parse(number), str))
      .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);