C#Linq / Lambda表达式:如何从字符串中选择一个整数?

时间:2010-02-02 19:34:49

标签: c# linq linq-to-objects

我认为解释我的问题的最好方法是使用简短的(通用的)linq-to-objects代码示例:

IEnumerable<string> ReadLines(string filename)
{
    string line;
    using (var rdr = new StreamReader(filename))
        while ( (line = rdr.ReadLine()) != null)
           yield return line;
}

IEnumerable<int> XValuesFromFile(string filename)
{
    return ReadLines(filename)
               .Select(l => l.Substring(3,3))
               .Where(l => int.TryParse(l))
               .Select(i => int.Parse(i));
}

请注意,此代码会解析整数两次。我知道我错过了一种明显的简单方法来安全地消除其中一个呼叫(因为我之前已经完成了)。我现在找不到它。我怎么能这样做?

3 个答案:

答案 0 :(得分:9)

怎么样:

int? TryParse(string s)
{
    int i;
    return int.TryParse(s, out i) ? (int?)i : (int?)null;
}
IEnumerable<int> XValuesFromFile(string filename)
{
    return from line in ReadLines(filename)
           let start = line.Substring(3,3)
           let parsed = TryParse(start)
           where parsed != null
           select parsed.GetValueOrDefault();
}

如果您愿意,您可以组合第二/第三行:

    return from line in ReadLines(filename)
           let parsed = TryParse(line.Substring(3,3))

GetValueOrDefault的选择是因为这会跳过执行(int).Value执行的验证检查 - 即它(稍微有点)更快(我们已经检查它不是null)。

答案 1 :(得分:3)

它不是很漂亮,但你可以这样做:

return ReadLines(filename)
    .Select(l =>
                {
                    string tmp = l.Substring(3, 3);
                    int result;
                    bool success = int.TryParse(tmp, out result);
                    return new
                               {
                                   Success = success,
                                   Value = result
                               };
                })
    .Where(i => i.Success)
    .Select(i => i.Value);

当然,这主要是将工作推入lambda,但它确实提供了正确的答案,只需一次解析(但额外的内存分配)。

答案 2 :(得分:3)

我想我会用这样的东西:

IEnumerable<O> Reduce<I,O>(this IEnumerable<I> source, Func<I,Tuple<bool, O>> transform )
{
    foreach (var item in source)
    {
       try
       {
          Result<O> r = transform(item);
          if (r.success) yield return r.value;
       }
       catch {}
    }
}

ReadLines().Reduce(l => { var i; new Tuple<bool, int>(int.TryParse(l.Substring(3,3),i), i)} );

我不是很喜欢这个,因为我已经on the record as not liking using tuples in this way了。不幸的是,除了滥用异常或将其限制为引用类型(其中null被定义为转换失败)之外,我没有看到很多其他选择,这两种情况都不是更好。