2015-11-04 6 views
1

Я ищу регулярное выражение для поиска чисел в строке; если у меня есть строка, как:regex с номерами в строке C++

li 12.12 si 43,23 45 31 uf 889 uf31 3.12345

Я хочу найти только номера:

12.12 45 31 889 3.12345

Я попытался с помощью следующей схеме:

((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?

но выход включал uf31 и 43,23.

Я попытался с:

(?!([a-z]*((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?[a-z]*))?((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?

, но это дало тот же результат.

Какое решение?

РЕШЕНИЯ оставить потомство решения:

  • Если вы ищете простое и эффективное решение, которое не использует регулярное выражение, см Jonathan Mee's post below
  • Если вы ищете решение с использованием регулярных выражений, см the wonderful regex from stribizhev

    R"((?:^|\s)([+-]?[[:digit:]]+(?:\.[[:digit:]]+)?)(?=$|\s))"

+1

Можете ли вы определить, что вы хотите использовать для разделителей? Я понимаю, что вы * не * хотите включать что-либо, кроме пробелов, разделенных пробелами. Это верно? –

+2

Вы хотите поддержать '.5'? –

+0

Как насчет научной нотации? Вам нужно будет указать, что вы хотите захватить. Строка вашего примера не содержит + или - и вы не упоминаете их в тексте, но ваши попытки regexp делают. – user2079303

ответ

3

На самом деле, модуль регулярного выражения C++ поддерживает поиск.

Вот мое предложение:

#include <iostream> 
#include <regex> 
using namespace std; 

int main() { 
    std::string buffer = " li 12.12 si 43,23 45 31 uf 889 uf31 3.12345"; 
    std::regex rx(R"((?:^|\s)([+-]?[[:digit:]]+(?:\.[[:digit:]]+)?)(?=$|\s))"); // Declare the regex with a raw string literal 
    std::smatch m; 
    std::string str = buffer; 
    while (regex_search(str, m, rx)) { 
     std::cout << "Number found: " << m[1] << std::endl; // Get Captured Group 1 text 
     str = m.suffix().str(); // Proceed to the next match 
    } 
    return 0; 
} 

См IDEONE demo

Благодаря сырьевой струнной буквальной декларации, нет никакой необходимости, используя два раз подряд с \s.

Lookahead (?=$|\s) проверяет наличие, но не использует пробелы, а последовательные номера могут быть извлечены.

Обратите внимание, что если вам нужно извлечь десятичные значения как .5, вам нужно

R"((?:^|\s)([+-]?[[:digit:]]*\.?[[:digit:]]+)(?=$|\s))" 
+0

Я стою исправлен, я использовал Visual Studio 2013, в прошлом я тестировал внешний вид. Похоже, C++ теперь полностью поддерживает ECMAScript! Однако я все же делаю так, чтобы смотреть вокруг - это самая дорогая операция с регулярным выражением. Их следует избегать, если это абсолютно необходимо, если они абсолютно отсутствуют. –

+0

В этом случае, следуя этой логике, внешний вид выглядит следующим образом: * обязательный *. Вы не можете сопоставить числа в '' без –

+0

Является ли ваш '' «буквальным пробелом? Если не из моего понимания, OP не хотел бы, чтобы эти числа совпадали. Если это буквальный w hitespace Я не понимаю, почему вам нужно искать вокруг? –

0

Вам нужно это регулярное выражение:

(?<!,)\b([\d\.]+)\b(?!,) 
+0

спасибо! но с вашим регулярным выражением я печатаю 'токен. token, token .' – Mau

+0

@ user3641602 Это будет соответствовать 1.2.3 ... Вы хотите ввести правильную нумерацию на свой номер? –

+2

Кто, черт возьми, поддержал это * цензура *? –

0

Используйте отрицательный предпросмотр и назад 'утверждать, что нет забавных символов на каждую стороне номера:

(?<![^\\s])(\\+|-)?[0-9]+(\\.[0-9]*)?(?![^\\s]) 

К сожалению, вы будете нуждаться Boost.Regex для задача как встроенная не поддерживает эти конструкции.

Возможно, вам лучше отказаться от ввода ввода в слов, а затем с помощью простого регулярного выражения для каждого слова.

+0

C++ не поддерживает поиск вперед или заглядывает –

+0

ATM I на самом деле не вижу другого способа сделать это. –

+0

Просто примечание: '[^ \\ s]' ищет символы, которые не являются '' \\ ''или' 's''. То, что вы на самом деле имели в виду, было '\ S' –

1

Как это stated by stribizhev, это может быть выполнено только через внешний вид. Так как в противном случае необходимо было бы использовать единичные пробелы, разделяющие числа, которые будут использоваться в поиске номера до и после пробела.

user2079303 poses a viable option to regexes, который может быть упрощено до точки, где он конкурировал с простотой регулярных выражений:

for_each(istream_iterator<string>(istringstream(" li 12.12 si 43,23 45 31 uf 889 uf31 3.12345")), 
     istream_iterator<string>(), 
     [](const string& i) { 
      char* it; 
      double num = strtod(i.c_str(), &it); 
      if (distance(i.c_str(), const_cast<const char*>(it)) == i.size()) cout << num << endl; }); 

Однако можно выполнить это без весу istringstream или регулярное выражение, просто используя strtok:

char buffer[] = " li 12.12 si 43,23 45 31 uf 889 uf31 3.12345"; 

for (auto i = strtok(buffer, " \f\n\r\t\v"); i != nullptr; i = strtok(nullptr, " \f\n\r\t\v")) { 
    char* it; 
    double num = strtod(i, &it); 

    if (*it == '\0') cout << num << endl; 
} 

Обратите внимание, что для моего разделителем аргумента я просто использовать параметр по умолчанию isspace значения.

+0

Не требуется экранирование, если используется строковый литерал. '31' не соответствует, BTW. –

+0

@KarolyHorvath Неверно, обратите внимание, что это не захватывающая скобка. –

+1

+1 Спасибо за упрощенное использование второго параметра 'strtod'. Потребовалось некоторое время, чтобы понять документацию. – user2079303

0

Две попытки:

#include <string> 
#include <iostream> 
#include <regex> 
#include <sstream> 


int main() 
{ 
    using namespace std; 

    string buffer(" li 12.12 si 43,23 45 31 uf 889 uf31 3.12345 .5"); 

    regex num_regex("(^|\\s)([\\+-]?([0-9]+\\.?[0-9]*|\\.?[0-9]+))(\\s|$)"); 
    smatch num_match; 
    while (regex_search(buffer, num_match, num_regex)) 
    { 
     if (num_match.size() >= 4) //3 groups = 4 matches 
     { 
      //We only need the second group 
      auto token = num_match[2].str(); 
      cout << token << endl; 
     } 

     buffer = num_match.suffix().str(); 
    } 
    return 0; 
} 

#include <string> 
#include <iostream> 
#include <regex> 
#include <sstream> 


int main() 
{ 
    using namespace std; 

    string buffer(" li 12.12 si 43,23 45 31 uf 889 uf31 3.12345 .5"); 

    istringstream iss(buffer); 
    vector<string> tokens{ istream_iterator<string>{iss}, istream_iterator<string>{} }; 

    regex num_regex("^[\\+-]?([0-9]+\\.?[0-9]*|\\.?[0-9]+)$"); 
    for(auto token : tokens) 
    { 
     if (regex_search(token, num_regex)) 
     { 
      //Valid entry 
      cout << token << endl; 
     } 
    } 

    return 0; 
} 
+0

Я пробую сейчас! спасибо – Mau

+0

сначала правильно, но игнорировать 31 и .5 второй игнорировать всегда – Mau

+1

31 не игнорируется - я просто тестировал оба варианта. Вы правы насчет '.5' - я обновлю свой ответ –

0

Вы можете играть с trick потреблять вещи вы не хотите. Что-то вроде этого.

Modfiy для всего, что должно быть исключено в трубках предварительного захвата и захвата захвата первой группы.

See demo at regex101. Не знаю, если (: группа не захвата в порядке для C++. Удалите, если нет.

+0

Впечатляющий способ подумать об этом, но это будет захватывать:« 123abc »и« 12 # 3 »есть ли у вас способ обойти это? –

+1

@JonathanMee Этот подход имеет смысл только в том случае, если известны случаи, которые могут произойти. Для ваших образцов необходимо [добавить те случаи, как это] (https://regex101.com/r/tU2wH4/1). –

1

Регулярные выражения обычно не читаются и не могут быть точными. Регулярные выражения, соответствующие только действительным рациональным числам, должны быть сложными и легко разбираться. Поэтому я предлагаю альтернативный подход. Вместо регулярных выражений, обозначьте свою строку с помощью C++ и используйте std::strtod, чтобы проверить, является ли ввод допустимым числом. Вот пример кода:

std::vector<std::string> split(const std::string& str) { 
    std::istringstream iss(str); 
    return { 
     std::istream_iterator<std::string>{iss}, 
     std::istream_iterator<std::string>{} 
    }; 
} 

bool isValidNumber(const std::string& str) { 
    char* end; 
    std::strtod(str.data(), &end); 
    return *end == '\0'; 
} 

// ... 
auto tokens = split(" li 12.12 si 43,23 45 31 uf 889 uf31 3.12345"); 
std::vector<std::string> matches; 
std::copy_if(tokens.begin(), tokens.end(), std::back_inserter(matches), isValidNumber); 
+0

Вы избили меня до использования 'strtod' +1 –

+0

Да, это возможный путь. Но у меня есть решение проблемы. Я хотел бы уменьшить свой код через регулярное выражение, потому что если вы используете регулярное выражение, то у вас есть мощный инструмент руками! :) Но, например, вы упомянуты раньше: «Регулярные выражения обычно не читаются и трудно обосновываются». :) – Mau

+0

@ user3641602 Его решение я считаю более простым, чем решение регулярного выражения, в первую очередь. Я оптимизировал его код в одном из вариантов, которые я предоставляю в своем ответе: http://stackoverflow.com/a/33521413/2642059 –