регулярные выражения в C/C++

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: регулярные выражения в C/C++

Непрочитанное сообщение Olej » 14 сен 2016, 12:39

Olej писал(а):Здесь вот онлайновый тестер регулярных выражений, который позволяет проверить результат сопоставления, без написания какого-либо программного кода и не используя никакие GNU утилиты: Regex Pal.
Очень удобно при отработке.
И ещё:
RegExrv2.1

И ещё:
0.3.1b built by gskinner.com

Но самое смешное, так это что регулярные выражения, сопоставляются в одном из таких онлайнов, а также grep, egrep и др. - не сопоставляются во всех остальных и наоборот. :-o :-(
В этом смысле показательна фраза (стр.12) из книги Майкла Фицджеральда:
Большинство из указанных реализаций регулярных выражений в чем-то сходны, а в чем-то различаются. Я не могу подробно обсудить все отличия в столь маленькой кни­ге, но о многих расскажу. Любые попытки задокументировать все различия между все­ ми реализациями наверняка привели бы меня в больницу.
:lol:

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: регулярные выражения в C/C++

Непрочитанное сообщение Olej » 18 ноя 2016, 23:11

Сами регулярные выражения, перед их внесением в код C/C++ хорошо бы проверять (отлаживать) ... хотя бы просто потому, что регулярные выражения - уж точно "не фишка" C/C++, не самое сильное место этих языков.
Для этого хорошо бы иметь под рукой тестовый инструмент регулярных выражений.
Я предлагаю использовать в этом качестве sed - потоковый текстовый редактор.
И в упоминаемой там книге Краткий учебник по sed есть Глава 2 - вполне достаточное введение в технику регулярных выражений.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: регулярные выражения в C/C++

Непрочитанное сообщение Olej » 13 май 2017, 09:46

Не прошло и пол-года, как возникла ещё раз оказия порепетировать с регулярными выражениями C++, в связи с вот этой проблемой: интерпретатор программного кода (на C++). Но с регулярными выражениями (на любом инструменте их использования: Perl, Python, Ruby, sed, awk, grep, egrep, ...) проблема состоит в том, что для синтаксиса регулярных выражений существует 10000000 стандартов граматики, и никогда не знаешь деталей того, который сейчас используешь.
Вот так же и с библиотекой regex C++:
- она новая, появилась только в стандарте C++11 (2011г.)
- нигде толком не описана (в деталях граматик)
- при создании объектов регулярных выражений можно указать константой в конструкторе 5 (как минимум) различающихся граматик:
basic Basic POSIX grammar
extended Extended POSIX grammar
awk Awk POSIX grammar
grep Grep POSIX grammar
egrep Egrep POSIX grammar
- мелкие детали того, что они понимают под каждой, нигде не описано. :-(

Хоть вот это ;-) : syntax specifications std::ECMAScript syntax
The following syntax is used to construct regex objects (or assign) that have selected ECMAScript as its grammar.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: регулярные выражения в C/C++

Непрочитанное сообщение Olej » 12 дек 2017, 17:01

Olej писал(а):
Olej писал(а):P.S. В приложении - все сразу показанные до сих пор примеры.
Дальнейшие редакции этого текста (дополнения, изменения, исправления, ...) см. здесь: Регулярные выражения C/C++
Регулярные выражения в С++ с блеском показали себя на 2-х проектах:
интерпретатор программного кода (на C++)
симулятор процессора
Особенно во 2-м!
Синтаксический разбор языка ассемблера, который мог бы делаться неделями или даже месяцами, был прописан в 3 дня работы.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: регулярные выражения в C/C++

Непрочитанное сообщение Olej » 12 дек 2017, 17:18

Olej писал(а): Особенно во 2-м!
Синтаксический разбор языка ассемблера, который мог бы делаться неделями или даже месяцами, был прописан в 3 дня работы.
Но во всех задачах подбирать вид регулярного выражения - занятие тягомутное и трудоёмкое.
Чтоб его сделать простым и быстрым, сделано такое приложение regc.cc :
- считывает любое число строк-патернов (регулярных шаблонов) из отдельного файла, расширения .pat
- потом в диалоге прогоняющем все вводимые строки на результат отождествления c этими патернами.

Код: Выделить всё

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

inline regex pattern( const string& r ) {
   //   try { return regex( r ); }                     // с учётом регистра
   try { return regex( r, regex_constants::icase ); }  // без учёта регистра
   catch( regex_error& e ) {                           // ошибка записи регулярного выражения
      cerr << "синтаксис регулярного выражения:\t" << r << endl;
      exit( 3 );
   }
}
// static constexpr flag_type basic = regex_constants::basic;
// static constexpr flag_type extended = regex_constants::extended;
// static constexpr flag_type awk = regex_constants::awk;
// static constexpr flag_type grep = regex_constants::grep;

int test_table( int p, char* fname[] ) {
   while( --p ) {
      ifstream fin;
      fin.open( fname[ p ] ); 
      if( !fin ) break;
      fin.close();
   }
   return p; 
}

vector<regex> load_table( int p, char* fname[] ) { 
   vector<regex> vr;
   int n = 1;
   while( --p ) {
      ifstream fin;
      string cur;
      fin.open( fname[ p ] ); 
      while( true ) {              
         getline( fin, cur );
         if( fin.eof() ) break;                        // EOF
         if( '#' == cur[ 0 ] ) continue;               // комментарий
         if( !cur.length() ) continue;                 // пустая строка
         try { 
            regex rc = regex( cur );
            vr.push_back( rc );
            cout << "#" << n++ << ":\t" << cur << endl;
         }
         catch( regex_error& e ) {                     // ошибка трансляции регулярного выражения
            cout << "ошибка:\t" << cur << endl;
         }
      }
      fin.close();
   }      
   cout << "-------------------------------------------" << endl;
   return vr;
}

int main( int argc, char *argv[] ) {
   if( argc < 2 ) {
       cout << "не заданы файлы щаблонов?" << endl;
       return 1;
   }
   int n = test_table( argc, argv );
   if( n ) {
      cout << "файл шаблонов не найден: " << argv[ n ] << endl;      
      return 2;
   }
   vector<regex> r = load_table( argc, argv );
   smatch m;
   while( true ) { 
      cout << "> ";
      string cur;
      getline( cin, cur );
      if( cin.eof() ) {
         cout << endl;
         return 0; 
      }
      if( cur == "!" || cur == "~" ) {
         r = load_table( argc, argv );
         continue;
      }
      n = 1;
      for( auto &pt : r ) { 
         if( regex_match( cur, m, pt ) ) {
            cout << "#" << n << " [" << m.size() << "]: " << m[ 0 ].str() << " => " << endl;
            for( unsigned i = 1; i < m.size(); i++ )
               cout << i << ": [" << m[ i ].str() << "] <" << m.position( i ) << ":" 
                    << m.length( i ) << ">" << endl;
         }
         n++;
      }
   }
}
Вложения
regs.218.tgz
(2.13 КБ) 64 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: регулярные выражения в C/C++

Непрочитанное сообщение Olej » 12 дек 2017, 17:27

Olej писал(а): Но во всех задачах подбирать вид регулярного выражения - занятие тягомутное и трудоёмкое.
Чтоб его сделать простым и быстрым, сделано такое приложение regc.cc :
- считывает любое число строк-патернов (регулярных шаблонов) из отдельного файла, расширения .pat
- потом в диалоге прогоняющем все вводимые строки на результат отождествления c этими патернами.
Выглядеть это может примерно так ... чтобы долго не объяснять:

Код: Выделить всё

[olej@dell 3]$ ./regs cmd.pat
#1:     ^\s*(nop)\s*$
#2:     ^\s*(halt)\s*$
#3:     ^\s*(rrmovl)\s+%e(ax|cx|dx|bx|sp|bp|si|di)\s*,\s*%e(ax|cx|dx|bx|sp|bp|si|di)\s*$
#4:     ^\s*(irmovl)\s+(\w+|\$[\+|\-]?[0-9]+|\$0x[0-9|a-f]+)\s*,\s*%e(ax|cx|dx|bx|sp|bp|si|di)\s*$
#5:     ^\s*(rmmovl)\s+%e(ax|cx|dx|bx|sp|bp|si|di)\s*,\s*([\+|\-]?[0-9]+|0x[0-9|a-f]+)*\s*\(\s*%e(ax|cx|dx|bx|sp|bp|si|di)\s*\)\s*$
#6:     ^\s*(mrmovl)\s+([\+|\-]?[0-9]+|0x[0-9|a-f]+)*\s*\(\s*%e(ax|cx|dx|bx|sp|bp|si|di)\s*\)\s*,\s*%e(ax|cx|dx|bx|sp|bp|si|di)\s*$
#7:     ^\s*(addl)\s+%e(ax|cx|dx|bx|sp|bp|si|di)\s*,\s*%e(ax|cx|dx|bx|sp|bp|si|di)\s*$
#8:     ^\s*(subl)\s+%e(ax|cx|dx|bx|sp|bp|si|di)\s*,\s*%e(ax|cx|dx|bx|sp|bp|si|di)\s*$
#9:     ^\s*(andl)\s+%e(ax|cx|dx|bx|sp|bp|si|di)\s*,\s*%e(ax|cx|dx|bx|sp|bp|si|di)\s*$
#10:    ^\s*(xorl)\s+%e(ax|cx|dx|bx|sp|bp|si|di)\s*,\s*%e(ax|cx|dx|bx|sp|bp|si|di)\s*$
#11:    ^\s*(jmp)\s+(\w+)\s*$
#12:    ^\s*(jle)\s+(\w+)\s*$
#13:    ^\s*(jl)\s+(\w+)\s*$
#14:    ^\s*(je)\s+(\w+)\s*$
#15:    ^\s*(jne)\s+(\w+)\s*$
#16:    ^\s*(jge)\s+(\w+)\s*$
#17:    ^\s*(jg)\s+(\w+)\s*$
#18:    ^\s*(call)\s+(\w+)\s*$
#19:    ^\s*(ret)\s*$
#20:    ^\s*(pushl)\s+%e(ax|cx|dx|bx|sp|bp|si|di)\s*$
#21:    ^\s*(popl)\s+%e(ax|cx|dx|bx|sp|bp|si|di)\s*$
-------------------------------------------
> nop
#1 [2]: nop =>
1: [nop] <0:3>
> halt
#2 [2]: halt =>
1: [halt] <0:4>
> rrmovl %eax , %ebx
#3 [4]: rrmovl %eax , %ebx =>
1: [rrmovl] <0:6>
2: [ax] <9:2>
3: [bx] <16:2>
> irmovl array , %edx
#4 [4]: irmovl array , %edx =>
1: [irmovl] <0:6>
2: [array] <7:5>
3: [dx] <17:2>
> irmovl $4 , %esi
#4 [4]: irmovl $4 , %esi =>
1: [irmovl] <0:6>
2: [$4] <7:2>
3: [si] <14:2>
> irmovl $-10 , %esi
#4 [4]: irmovl $-10 , %esi =>
1: [irmovl] <0:6>
2: [$-10] <7:4>
3: [si] <16:2>
> rmmovl  %ecx , -3(%ebx)
#5 [5]: rmmovl  %ecx , -3(%ebx) =>
1: [rmmovl] <0:6>
2: [cx] <10:2>
3: [-3] <15:2>
4: [bx] <20:2>
> rmmovl %esp, 0x12345 (%edx)
#5 [5]: rmmovl %esp, 0x12345 (%edx) =>
1: [rmmovl] <0:6>
2: [sp] <9:2>
3: [0x12345] <13:7>
4: [dx] <24:2>
> mrmovl (%edx), %eax
#6 [5]: mrmovl (%edx), %eax =>
1: [mrmovl] <0:6>
2: [] <19:0>
3: [dx] <10:2>
4: [ax] <17:2>
> mrmovl 8(%ebp), %edx
#6 [5]: mrmovl 8(%ebp), %edx =>
1: [mrmovl] <0:6>
2: [8] <7:1>
3: [bp] <11:2>
4: [dx] <18:2>
> mrmovl -12(%ebp), %edx
#6 [5]: mrmovl -12(%ebp), %edx =>
1: [mrmovl] <0:6>
2: [-12] <7:3>
3: [bp] <13:2>
4: [dx] <20:2>
> mrmovl 0xf012( %eax ) , %ebx
#6 [5]: mrmovl 0xf012( %eax ) , %ebx =>
1: [mrmovl] <0:6>
2: [0xf012] <7:6>
3: [ax] <17:2>
4: [bx] <26:2>
> addl %eax , %ebx
#7 [4]: addl %eax , %ebx =>
1: [addl] <0:4>
2: [ax] <7:2>
3: [bx] <14:2>
>   subl %eax , %ebx
#8 [4]:   subl %eax , %ebx =>
1: [subl] <2:4>
2: [ax] <9:2>
3: [bx] <16:2>
>   andl %eax , %ebx
#9 [4]:   andl %eax , %ebx =>
1: [andl] <2:4>
2: [ax] <9:2>
3: [bx] <16:2>
>  xorl %eax , %ebx
#10 [4]:  xorl %eax , %ebx =>
1: [xorl] <1:4>
2: [ax] <8:2>
3: [bx] <15:2>
>  jmp yyyy
#11 [3]:  jmp yyyy =>
1: [jmp] <1:3>
2: [yyyy] <5:4>
>  jle yyyy
#12 [3]:  jle yyyy =>
1: [jle] <1:3>
2: [yyyy] <5:4>
> jl yyyy
#13 [3]: jl yyyy =>
1: [jl] <0:2>
2: [yyyy] <3:4>
> je yyyy
#14 [3]: je yyyy =>
1: [je] <0:2>
2: [yyyy] <3:4>
> jne yyyy
#15 [3]: jne yyyy =>
1: [jne] <0:3>
2: [yyyy] <4:4>
> jge yyyy
#16 [3]: jge yyyy =>
1: [jge] <0:3>
2: [yyyy] <4:4>
> jg yyyy
#17 [3]: jg yyyy =>
1: [jg] <0:2>
2: [yyyy] <3:4>
>   call xxxx
#18 [3]:   call xxxx =>
1: [call] <2:4>
2: [xxxx] <7:4>
>   ret
#19 [2]:   ret =>
1: [ret] <2:3>
>  pushl %ebp
#20 [3]:  pushl %ebp =>
1: [pushl] <1:5>
2: [bp] <9:2>
>  popl %ebp
#21 [3]:  popl %ebp =>
1: [popl] <1:4>
2: [bp] <8:2>
> ^C
Всё должно быть понятно!

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: регулярные выражения в C/C++

Непрочитанное сообщение Olej » 12 дек 2017, 18:11

Вопрос (задают!):
- почему сами строки регулярных выражений в примерах кода C++ записываем так ( R"(...)" ) :?: :

Код: Выделить всё

R"(^\s*(.align)\s+(1|2|4|8|16|32)\s*$)"
Ответ состоит в том, что в записи C/C++ (по синтаксису строк) экранирующий символ '\' нужно бы записывать 2 раза подряд: "\\d".
Например, для показанной выше строки шаблона: R"(^\s*(.align)\s+(1|2|4|8|16|32)\s*$)" - полностью эквивалент можно записать и так (сравнивайте):

Код: Выделить всё

"^\\s*(.align)\\s+(1|2|4|8|16|32)\\s*$"

Но при большом числе '\' в строке шаблона (а их всегда много) такая запись совсем не читабельная.

Это всё касается так называемого std::ECMAScript синтаксиса записи регулярных выражений.
Про это (и про синтаксис шаблонов ECMAScript) можете почитать здесь.
В <regex> можно изменять используемый синтаксис ... варианты:

Код: Выделить всё

string r = "...";            // шаблон
regex R = regex( r );  // по умолчанию, ECMAScript 
regex R = regex( r, regex_constants::icase ); // без учёта регистра
// static constexpr flag_type basic = regex_constants::basic; // варианты синтаксиса:
// static constexpr flag_type extended = regex_constants::extended;
// static constexpr flag_type awk = regex_constants::awk;
// static constexpr flag_type grep = regex_constants::grep;

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: регулярные выражения в C/C++

Непрочитанное сообщение Olej » 12 дек 2017, 20:52

Olej писал(а): Чтоб его сделать простым и быстрым, сделано такое приложение regc.cc :
- считывает любое число строк-патернов (регулярных шаблонов) из отдельного файла, расширения .pat
- потом в диалоге прогоняющем все вводимые строки на результат отождествления c этими патернами.
Ещё для тестирования регулярных выражений оказываются сильно полезны онлайн сервисы:

- https://regexr.ru/
r1.png
- https://regexr.com/
r2.png

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: регулярные выражения в C/C++

Непрочитанное сообщение Olej » 14 дек 2022, 03:25

Olej писал(а): Это всё касается так называемого std::ECMAScript синтаксиса записи регулярных выражений.
Про это (и про синтаксис шаблонов ECMAScript) можете почитать здесь.
Эта ссылка, кстати, является вполне достаточной справочной страницей по std::ECMAScript syntax, вполне достаточной для написания собственных патернов синтаксического разбора:
ECMAScript regular expressions pattern syntax
...
Special pattern characters
...
Quantifiers
...

Groups
(subpattern) Group Creates a backreference.
(?:subpattern) Passive group Does not create a backreference.

Assertions
...
Alternatives
...
Character classes
...
[:xdigit:] hexadecimal digit character isxdigit
...
Ну, и ещё вот это - public member function <regex>
std::regex_traits::lookup_classname:
...
alpha alphabetic character isalpha
blank blank character isblank
cntrl control character iscntrl
digit decimal digit character isdigit
graph character with graphical representation isgraph
lower lowercase letter islower
print printable character isprint
punct punctuation mark character ispunct
space whitespace character isspace
upper uppercase letter isupper
xdigit hexadecimal digit character isxdigit
...


Тема поднималась пользователем Olej 14 дек 2022, 03:25.

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 15 гостей