Поиск значения до выполнения условия - PullRequest
2 голосов
/ 30 июня 2019

Есть ли способ упростить следующий код, чтобы он выглядел более четким и элегантным?

Следующий код возвращает коллекцию значений, найденных в коллекции текстов, используя Linq и regex:

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;
});

Как вы можете видеть, регулярное выражение сложно, оно в основном возвращает последнее значение, найденное в каждом тексте, прежде чем условие будет выполнено, и это желаемый результат.

Как вы могли бы упроститьпредыдущий код ищет элегантность?Использование памяти и процессора не является проблемой.

Ответы [ 3 ]

1 голос
/ 30 июня 2019

Несмотря на совет Бобинса о регулярных выражениях и HTML :) вот решение на основе регулярных выражений:

Механизм регулярных выражений .NET может идти в обратном направлении, поэтому мы можем использовать это и заставить наш рекс искать число в> <, которое является ближайшим (используйте пессимистическое сопоставление <code>.*?) последнего значения в скобках:

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

Это "соответствует и назовите число между> <, затем самое короткое количество любых символов, затем число между ()" - настройте по мере необходимости </p>

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
}

Например:

enter image description here

Смотрите здесь

1 голос
/ 30 июня 2019

Добавляя к ответу @ 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 оценивает результаты, возвращая массив значений типа double.

Наконец, следуя предложению @ 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();
1 голос
/ 30 июня 2019

Я бы сделал это так, если я правильно понимаю ваш код, этот синтаксис действителен в 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...