在满足条件之前查找值

Posted

技术标签:

【中文标题】在满足条件之前查找值【英文标题】:Finding a value before a condition is met 【发布时间】:2019-11-11 07:45:42 【问题描述】:

有什么办法可以简化下面的代码,让它看起来更清晰、更优雅?

以下代码使用 Linq 和正则表达式返回在文本集合中找到的值集合:

IEnumerable<double> _results = pages.Select(result => 
    Regex _regex = new Regex("<my regex here>", RegexOptions.None);
    MatchCollection _matches = _regex.Matches(result);
    double _number = 0.0;

    foreach (Match _match in _matches) 
        if (_match.Groups["value"].Value.Contains("("))
            break;
        else
           double.TryParse(_match.Groups["value"].Value, out _number);
    

    return _number;
);

如您所见,正则表达式很棘手,它基本上返回在满足条件之前在每个文本中找到的最后一个值,这就是预期的结果。

您如何简化前面的代码以寻求优雅?内存和 CPU 利用率不是问题。

【问题讨论】:

为什么不对你的正则表达式进行编码,这样它就不会选择带有括号的匹配项,或者使用括号的存在来确定哪个第一个先验值作为匹配集合值返回?这个问题将受益于您的原始数据样本,其中突出显示您想要从中提取哪些数据 @CaiusJard 相信我,你不想看到原始数据,它比 html 更糟糕,它基本上是服务器生成的非常神秘的日志。但是该算法仍然非常有效:正则表达式在满足条件之前找到的最后一个数字。 如果我不想看,我就不会问了 ;) 如果你坚持:) ...这是一个块:reactid="17"/>2,941.7616.84(12.7) 【参考方案1】:

如果我正确理解您的代码,我会这样做,此语法在 C#7.0 中有效,内联 out 变量声明:

Regex _regex = new Regex("<my regex here>", RegexOptions.None);

IEnumerable<double> _results = pages.Select(_regex.Matches)
 .Where(match => !match.Groups["Value"].Value.Contains("("))
 .Select(match => double.TryParse(match.Groups["Value"].Value, out double number) ? number : number);

【讨论】:

请注意,在找到括号之前匹配可能不止一个,在这种情况下,我需要最后一个。您的代码似乎返回了条件之前找到的所有项目,而我只需要最后一个。 我喜欢这个想法,我目前正在改进它,因为它也不能编译......但我知道你来自哪里。 感谢您向我展示了一条可以遵循的路径,我已根据您的建议使用最终代码添加了此问题的答案。【参考方案2】:

尽管bobince's advice 关于正则表达式和 HTML :) 这是一个基于正则表达式的解决方案:

.NET 的正则表达式引擎可以倒退,所以我们可以利用这一点,让我们的 rex 在 > .*?):

>(?<v>[,.0-9]+)<.*?\([.0-9]+\)

这是“匹配并命名 >

Regex r = new Regex(">(?<v>[,.0-9]+)<.*?\([.0-9]+\)", RegexOptions.RightToLeft /*other options here*/);
foreach(var p in pages)

  Match m = r.Match(p, p.Length - 1);

  MessageBox.Show(m.Groups["v"].Value); //finds 16.84

例如:

See it here

【讨论】:

为什么有人会说他的答案没有用?我会尝试@Caius 并使用我真正的正则表达式让你知道。谢谢! 我听从了你的建议,更改了正则表达式,现在它返回一个值,这进一步简化了 Linq 语句。谢谢!【参考方案3】:

在@dan-d 的回答之上添加,这可能是最简单易读且更优雅的代码:

double[] _results = _pages
    .Select(page => _regex.Matches(page).Cast<Match>().Select(value => value.Groups["value"].Value))
    .Select(value => value.TakeWhile(condition => !condition.Contains("(")).Last())
    .Select(number => double.TryParse(number, out double _result) ? _result : _result)
    .ToArray();

第一个 select 遍历所有数据页并使用正则表达式返回包含所有找到值的数组。第二个 select 查找每个页面的条件之前的最后一个值(该值是否有括号);而最后的 select 评估结果,返回一个双精度数组。

最后,在遵循@caius-jard 的建议后,改进正则表达式现在返回一个值,因此进一步将linq 语句简化为:

double[] _results = _pages
    .Select(page => _regex.Matches(page).Cast<Match>().Select(value => value.Groups["value"].Value).First())
    .Select(number => double.TryParse(number, out double _result) ? _result : _result)
    .ToArray();

【讨论】:

以上是关于在满足条件之前查找值的主要内容,如果未能解决你的问题,请参考以下文章

linq+lambda+delegate,从list中查找到满足匹配条件的所有数据索引值

使用“减号”操作来查找是不是满足条件

Hive:如果两个表之间满足条件,则查找唯一值

excel 满足一个条件 显示对应行倒数第二行?

如何在条件满足之前用 N 行中的一些对条件行进行子集化,比我的代码更快?

EXCEL中用函数查找显示同时满足多个条件的最小值和对应数据行的字段内容