C++ — Покажите пример обработчика регулярных выражений CC++


C++ — Покажите пример обработчика регулярных выражений C/C++

БлогNot. Есть ли регулярные выражения в C++?

Есть ли регулярные выражения в C++?

При программном сопоставлении строк со сколь угодно сложными шаблонами человечество пока не придумало ничего удобнее регулярных выражений, которые есть сегодня буквально везде (см. по ссылке).

Есть ли поддержка «регулярок» в C++? Да, есть, но только в новых стандартах C++11/14.

Пример по ссылке тоже неплох, но мы сделаем проще и наглядней. Проверено в Visual Studio 2015, должно работать примерно с 2013-й версии. Пространство имён std подключено не только для , но и потому, что методы для работы с RegExp определены также в нём.

При работе в QT пятых версий для поддержки стандарта C++11 достаточно указать опцию

в файле проекта .pro .

17.05.2020, 21:44; рейтинг: 2332

Записки программиста

Регулярные выражения в C++11 и парсинг логов Nginx

Ранее мы уже выясняли, что C++ никогда не умрет, и знать низкоуровневые вещи приходится, даже если фултайм пишешь на Scala. Поэтому я решил уделять некоторое время пописыванию небольших программок на C/C++. Тем более, что с тех пор, когда я активно этим делом увлекался, прошло уже лет семь-восемь и многое сильно изменилось. Так, например, в стандартной библиотеке C++ появились регулярные выражения, пример работы с которыми и приводится в этом посте.

Рассмотрим следующую проблему. Есть сервер, на котором поднят Nginx. Этот Nginx раздает какие-то mp3-файлы. Стоит задача определить, какие файлы и сколько раз были скачаны. Возможное решение на C++:

int main ( int argc, char ** argv ) <
const std :: regex re ( » \» GET (?:http://.+?/)?(.+? \\ .mp3) »
«HTTP/1 \\ .[01] \» \\ d <3>( \\ d+)» ) ;
std :: map std :: string , std :: pair long , long >> stat ;
for ( std :: string line ; std :: getline ( std :: cin , line ) ; ) <
std :: smatch match ;
if ( std :: regex_search ( line, match, re ) ) <
const std :: string fname = match [ 1 ] ;
long size = atol ( match [ 2 ] . str ( ) . c_str ( ) ) ;
auto it = stat. find ( fname ) ;
if ( it == stat. end ( ) ) < // not found
stat [ fname ] = std :: pair long , long > ( size, size ) ;
> else <
auto pair = stat [ fname ] ;
long sum = pair. first ;
long max = pair. second ;
sum + = size ;
max = std :: max ( max, size ) ;
stat [ fname ] = std :: pair long , long > ( sum, max ) ;
>
>
>

for ( auto & it : stat ) <
auto & key = it. first ;
auto & value = it. second ;
long sum = value. first ;
long max = std :: max ( value. second , 1L ) ; // avoid division by zero
double downloads = ( double ) sum / ( double ) max ;
std :: cout «Key: » key » downloads: » downloads
» (max size: » max «)» std :: endl ;
>
>

Кстати, в современном C++ строки можно объявлять так:

// g++ -Wall -std=c++1y test.cpp -o test
#include

int main() <
std::cout g++ -Wall -std =c++1y -O2 parse.cpp -o parse_gcc

… то мы не получим никаких ошибок, однако программа работать не будет. При создании std::regex будет бросаться исключение std::regex_error. Если заменить аргумент конструктора на какую-то более простую строку, исключение пропадет, но ни одна строчка из логов не будет соответствовать регулярному выражению, даже самому простому. Кажется, я где-то час пытался разобраться, пока наконец не понял, что в gcc 4.8 просто еще нет поддержки регулярных выражений. Хоть бы варнинг при компиляции показали! В gcc 4.9.2, говорят, поддержка есть, но вроде как она сильно завязана на конкретный тип (char) и в общем случае приводит к багам.

У clang в этом смысле все намного лучше:

На моем компьютере программа успешно отрабатывает за 8.5 секунд при одном большом распакованном файле из 378к строк на входе, по 0.022 мс на строку. Если использовать unordered_map, программа справляется с задачей за 8 секунд, но требует написания дополнительного кода для вывода результатов в отсортированном порядке. Примечательно, что аналогичный скрипт на Perl:

use strict ;
use warnings ;
use 5.018 ;

my % stat ;
my $re = qr #GET (?:http://.+?/)?(.+?\.mp3) HTTP/1\.[01]» \d <3>(\d+)#;

while ( my $line = ) <
my ( $fname , $size ) = $line =

$re ;
next unless $fname ;
if ( $stat < $fname >) <
my $sum = $stat < $fname >[ 0 ] ;
my $max = $stat < $fname >[ 1 ] ;
$sum += $size ;
$max = $max > $size ? $max : $size ;
$stat < $fname >= [ $sum , $max ] ;
> else <
$stat < $fname >= [ $size , $size ] ;
>
>


for my $key ( keys % stat ) <
my $max = $stat < $key >[ 1 ] ;
$max = 1 if $max 0 ;
say «$key => » . ( $stat < $key >[ 0 ] / $max ) . » (max: $max)» ;
>

… справляется с той же задачей за 1.5 секунды. При этом я не особо преуспел в оптимизации кода на C++ до такого же уровня путем повторного использования объектов или замены значений на ссылки. И хотя я допускаю, что могу чего-то не знать, скажем, о буферизации std::cin, в реальных проектах вам может захотеться поэкспериментировать с libpcre. Эта либа может оказаться намного быстрее текущей реализации регулярных выражений в стандартной библиотеке C++.

А пользуетесь ли вы std::regex в своих проектах и довольны ли скоростью?

Учебник: Регулярные выражения (regular expressions)

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

  • Вы пишете программу, в которой обрабатываются номера телефонов, допустим в формате +7(ххх)ххх-хх-хх. Возможно их надо найти в тексте, а может быть — проверить корректность. На месте номеров могли бы быть номер банковской карты, IP-адрес, электронная почта, ФИО (в формате Петров А.Ю.), да и вообще что угодно.
  • В Microsoft Word при поиске и замене можно включить режим поддержки регулярных выражений поставив галочку напротив пункта «подстановочные знаки». Потом можно искать все то, что указано в первом пункте, но программу писать не требуется. И заменять можно. В LibreOffice/OpenOffice это тоже поддерживается.
  • Естественно, регулярные выражения поддерживаются во всех современных средах разработки — Qt Creator, Microsoft Visual Studio, NetBeans, IntelliJ IDEA и даже блокнотах — Notepad++, kate, gedit и др. Вы пишете код и решили что-то переименовать, да как-то особенно…

Остается научиться всем этим пользоваться. Значительную часть описанных ниже примеров можно проверить в том же Notepad++ или Microsoft Word. Для других (связанных с программированием) — можно использовать сервис regex101, он удобен не только для обучения, но и для реальной разработки.

Содержание:

1 Теоретический раздел

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

1.1 Одиночные символы

Символ «точка» (.) заменяет в регулярных выражениях любой символ. Так, например, если в тексте есть слова «порог» и «пирог» — то выражение «п.рог» будет удовлетворять обоим из них. Ниже приведен пример такого поиска в тектовом редакторе kate, остальные примеры будут даваться без скриншотов.

Если же нас интересуют не все варианты замены символа — используется представление с квадратными скобками. В скобках перечисляются альтернативные символы. Также, в квадратных скобках можно задавать диапазоны символов с помощью «тире». Ниже приведена схема для выражения «var_[a-d][123]», можно попробовать выписать строки, которое оно описывает:

Цукерберг рекомендует:  Eclips - НУЖНА ПОМОЩЬ С НАПИСАНИЕМ ПРОГРМАММЫ

Если символ «тире» должен являться частью перечисления — его нужно ставить первым или последним. Например, в таком выражении:

ставить тире между «+» и «*» нельзя, так как это будет интерпретировано как диапазон.

Также с помощью перечислений можно искать «все символы кроме», для этого первым символом перечисления должен быть «^» . Так, чтобы найти в тексте все символы кроме «ё» , «й» и символов «a-z» можно использовать такое выражение: «[^ёйa-z]» .

Если символ «^» стоит вне квадратных скобок — то он задает начало строки (до сих пор поиск осуществлялся во всем тексте). Символ «$» соответствует концу строки.

Если вдруг вам нужно найти в тексте какой-либо из «управляющих символов» — то его нужно экранировать с помощтю слеша. Так, например, символы «^» , «[» в регулярном выражении должны быть заменены на «\^» , «\[» . На практике часто приходится искать символ слеша, который также является управляющим и заменяется на «\\» .

Наконец, для часто встречающихся категорий символов введены специальные обозначения:

Выражение Символ
«\d» цифра
«\D» все кроме цифры «[^0-9]»
«\s» пробельный символ (табуляции, пробелы)
«\S» все, кроме пробельных символов
«\w» буква (любой язык, в любом регистре)
«\W» все кроме букв
«\b» граница слова
«\B» не граница слова

Такие обозначения могут использоваться в качестве элементов перечисления, например «[\d\w]» соответствует букве или цифре.


1.2 Квантификация

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

Выражение Количество повторений
«*» 0 или более раз
«+» 1 или более раз
«?» 0 или 1 раз
«« точно n раз
«« от n до m раз

С помощью кванторов мы можем описать, например строку, содержащую номер банковской карты:

Под такое описание подойдут, например, строки «1234-1234-1234-1234» и «12345678 12345678» .

1.3 группировка (подвыражения)

Выражение может состоять из подвыражений, заключенных в круглые скобки. Для программиста это очень важно, так как к подвыражению можно обратиться по индексу. Кроме того, подвыражения используются для задания альтернатив, которые можно перечислять с помощью вертикальной черты. Так, например, следующее выражение соответствует строкам «+7 902», «8(902)» и еще множеству вариантов:

Тут «\(» используется для экранирования скобок. Подвыражения на практике применяются очень часто, но нам не всегда нужна возможность получить подстроку, соответствующую подвыражению. При выборе подстрок в коде вашей программы «лишние» подвыражения мешают, из-за них «съезжают» индексы, исправить ситуацию можно с использованием следующего синтаксиса: «(?:pattern)» . Кроме того, такая форма записи более эффективна, т.к. сохраняет меньше данных.

Также, с группами связано так называемое «заглядывание вперед» — это нечасто применяемая на практике техника позволяет проверить соответствие подвыражению, не смещая позицию поиска и не запоминая найденное соответствие. Синтаксис используется следующий «(?=pattern)» . Пусть дан следующий файл со списком языков программирования:

мы ожидаемо получим три строки, однако что если, к уже найденному подвыражению требуется применить какие-то дополнительные «фильтры»? То есть, после этой проверки мы хотим еще раз проверить названия языков. Сделать это можно заменив «?:» на «?=» .

Теперь будут получены только две строки — Lua и Lisp, а второе подвыражение «(.*)» будет сопоставлено с типами соответствующих языков.

Негативное заглядывания вперед ищет несоответствие строки шаблону «(?!pattern)» . Такое выражение выбирает подстроки, не соответствующие «pattern» без запоминания подстроки и не смещая текущую позицию поиска. Так, для рассмотренного выше примера, такой тип заглядывания вернет единственную строку с языком Logo. Первое подвыражение выберет строки с языками Basic, Prolog, С++ и Logo, а второе — оставит из них только те, чьи названия начинаются с символа «L» .

1.4 Что есть еще?

Наряду с заглядыванием вперед, в некоторых реализациях поддерживается позитивное и негативное заглядывания назад — «(? и «(? , соответственно. Полезно знать, что нечто подобное существует, чтобы в случае чего — найти в справочнике и применить.

Описанное выше должно одинаково работать в любой среде, поддерживающей регулярные выражения, однако в отдельных реализациях доступно больше возможностей или синтаксис выражений может незначительно отличаться. С помощью регулярных выражений можно искать строки в тексте, однако в каком регистре выполняется поиск? — ответ зависит от реализации. Управлять регистром можно с помощью модификаторов : «(?i)» включает чувствительность к регистру, а «(?-i)» — выключает ее. Существуют и другие модификаторы, но они используются реже. Работа модификаторов зависит от реализации. Некоторые реализации поддерживают также флаги, которыми также можно управлять регистром.

Ряд реализаций поддерживает очень удобный поиск по условию: «(?(?=если)то|иначе)» . Нечто подобное позволяет реализовать «просмотр вперед». «Если» условие выполнится — будет выполнено сопоставление с «то», в противном случае — с «иначе». Сопоставление в данном случае создает группу, к которой можно обратиться по индексу из вашего кода.

2 Практический раздел. Ссылки

Перед тем, как использовать регулярные выражения, стоит посмотреть в документацию по вашему языку программирования и используемой библиотеке, так как диалекты обладают особенностями. Например в Perl и некоторых версиях php можно описывать рекурсивные регулярные выражения, которые не поддерживаются большинством других реализаций; механизмом флагов отличается JavaScript и так далее. Незначительными отличиями могут обладать даже различные версии одной и той же библиотеки.

Отличаются регулярные выражения не только синтаксисом, но и реализацией. Регулярные выражения — это «не просто так». Строка, задающее выражение, преобразуется в автомат, от реализации которого зависит эффективность. Масштаб проблемы хорошо иллюстрирует график зависимости времени выполнения поиска от длины строки и реализации:

Картинка взята из статьи «Поиск с помощью регулярных выражений может быть простым и быстрым«. В ней можно прочитать про различные реализации выражений, а также о том, как написать выражение так, чтобы оно работало быстрее. Кстати, так как выражение преобразуется в автомат, то зачастую его удобно визуализировать — для этого есть специальные сервисы, например. Для последнего выражения статьи будет построен такой автомат:

Примеры использования регулярных выражений:

  • для валидации вводимых в поля данных: QVal >javax.faces.validator.Validator ;
  • для парсинга сайтов: Парсер сайта на Qt, использование QRegExp. В примере с сайта-галереи выбираются и скачиваются картинки заданных категорий;
  • для валидации данных, передаваемых в формате JSON ряд библиотек позволяет задавать схему. При этом для строковых полей могут быть заданы регулярные выражения. В качестве упражнения можно попробовать составить выражение для пароля — проверить что строка содержит символы в разном регистре и цифры.

В сообществе Программирование и алгоритмы можно посмотреть дополнительную литературу по теме. Книгу Гойвертса и Левитана рекомендую посмотреть особенно, так как в ней по-полочкам разобраны десятки примеров, причем с учетом специфики реализации регулярных выражений в конкретных языках программирования.


Библиотека регулярных выражений

Эта страница была переведена автоматически с английской версии вики используя Переводчик Google.

Перевод может содержать ошибки и странные формулировки. Наведите курсор на текст, чтобы увидеть оригинал. Вы можете помочь в исправлении ошибок и улучшении перевода. Для инструкций перейдите по ссылке.

Язык
Заголовочные файлы
Концепты
Поддержка языка
Ошибка
Утилиты
Строки
Контейнеры
Алгоритмы
Итераторы
Работа с числами
Ввод/вывод
Локализация
Регулярные выражения (C++11)
Атомарные операции (C++11)
Потоки (C++11)
Файловая система (C++17)
Технические спецификации

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

Почти все операции с регулярными выражениями могут быть осуществлены посредством следующих объектов:

  • Целевая последовательность. Последовательность символов, искомая по шаблону. Эта последовательность может быть представлена парой итераторов, нуль-терминированной строкой или объектом std::string .
  • Шаблон. Представляет собой само регулярное выражение. Шаблон определяет совпадение с содержимым целевой строки и является объектом типа std::basic_regex , созданным из строки с особым синтаксисом.
  • Массив совпадений. Информация о совпадениях, доступ к которой осуществляется через объект типа std::match_results .
  • Строка замены. Строка определяющая, как следует заменять совпадения, управляемых специальными флагами.

Содержание

[править] Основные классы

Эти классы инкапсулируют регулярные выражения и результаты поиска совпадений с регулярным выражением в заданной последовательности символов.

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

Вступление

Регулярные выражения (иногда называемые регулярными выражениями или регулярными выражениями ) представляют собой текстовый синтаксис, который представляет шаблоны, которые могут быть сопоставлены в используемых строках.

Регулярные выражения, введенные в c ++ 11 , могут опционально поддерживать возвращаемый массив совпадающих строк или другой текстовый синтаксис, определяющий, как заменить сопоставленные шаблоны в строках, на которых работает.

Регулярные выражения в C#


Регулярные выражения — это часть небольшой технологической области, невероятно широко используемой в огромном диапазоне программ. Регулярные выражения можно представить себе как мини-язык программирования, имеющий одно специфическое назначение: находить подстроки в больших строковых выражениях.

Это не новая технология, изначально она появилась в среде UNIX и обычно используется в языке программирования Perl. Разработчики из Microsoft перенесли ее в Windows, где до недавнего времени эта технология применялась в основном со сценарными языками. Однако теперь регулярные выражения поддерживаются множеством классов .NET из пространства имен System.Text.RegularExpressions. Случаи применения регулярных выражений можно встретить во многих частях среды .NET Framework. В частности, вы найдете их в серверных элементах управления проверкой ASP.NET.

Введение в регулярные выражения

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

Набор управляющих кодов для идентификации специфических типов символов

Система для группирования частей подстрок и промежуточных результатов таких действий

С помощью регулярных выражений можно выполнять достаточно сложные и высокоуровневые действия над строками:

Идентифицировать (и возможно, помечать к удалению) все повторяющиеся слова в строке

Сделать заглавными первые буквы всех слов

Преобразовать первые буквы всех слов длиннее трех символов в заглавные

Обеспечить правильную капитализацию предложений

Выделить различные элементы в URI (например, имея http://www.professorweb.ru, выделить протокол, имя компьютера, имя файла и т.д.)

Главным преимуществом регулярных выражений является использование метасимволов — специальные символы, задающие команды, а также управляющие последовательности, которые работают подобно управляющим последовательностям C#. Это символы, предваренные знаком обратного слеша (\) и имеющие специальное назначение.

В следующей таблице специальные метасимволы регулярных выражений C# сгруппированы по смыслу:

Метасимволы, используемые в регулярных выражениях C#

Символ Значение Пример Соответствует
Классы символов
[. ] Любой из символов, указанных в скобках [a-z] В исходной строке может быть любой символ английского алфавита в нижнем регистре
[^. ] Любой из символов, не указанных в скобках [^0-9] В исходной строке может быть любой символ кроме цифр
. Любой символ, кроме перевода строки или другого разделителя Unicode-строки
\w Любой текстовый символ, не являющийся пробелом, символом табуляции и т.п.
\W Любой символ, не являющийся текстовым символом
\s Любой пробельный символ из набора Unicode
\S Любой непробельный символ из набора Unicode. Обратите внимание, что символы \w и \S — это не одно и то же
\d Любые ASCII-цифры. Эквивалентно [0-9]
\D Любой символ, отличный от ASCII-цифр. Эквивалентно [^0-9]
Символы повторения
Соответствует предшествующему шаблону, повторенному не менее n и не более m раз s «Press», «ssl», «progressss»
Соответствует предшествующему шаблону, повторенному n или более раз s «ssl»
Соответствует в точности n экземплярам предшествующего шаблона s «Press», «ssl», но не «progressss»
? Соответствует нулю или одному экземпляру предшествующего шаблона; предшествующий шаблон является необязательным Эквивалентно
+ Соответствует одному или более экземплярам предшествующего шаблона Эквивалентно
* Соответствует нулю или более экземплярам предшествующего шаблона Эквивалентно
Символы регулярных выражений выбора
| Соответствует либо подвыражению слева, либо подвыражению справа (аналог логической операции ИЛИ).
(. ) Группировка. Группирует элементы в единое целое, которое может использоваться с символами *, +, ?, | и т.п. Также запоминает символы, соответствующие этой группе для использования в последующих ссылках.
(. ) Только группировка. Группирует элементы в единое целое, но не запоминает символы, соответствующие этой группе.
Якорные символы регулярных выражений
^ Соответствует началу строкового выражения или началу строки при многострочном поиске. ^Hello «Hello, world», но не «Ok, Hello world» т.к. в этой строке слово «Hello» находится не в начале
$ Соответствует концу строкового выражения или концу строки при многострочном поиске. Hello$ «World, Hello»
\b Соответствует границе слова, т.е. соответствует позиции между символом \w и символом \W или между символом \w и началом или концом строки. \b(my)\b В строке «Hello my world» выберет слово «my»
\B Соответствует позиции, не являющейся границей слов. \B(ld)\b Соответствие найдется в слове «World», но не в слове «ld»

Использование регулярных выражений в C#

Безуcловно, задачу поиска и замены подстроки в строке можно решить на C# с использованием различных методов System.String и System.Text.StringBuilder. Однако в некоторых случаях это потребует написания большого объема кода C#. Если вы используете регулярные выражения, то весь этот код сокращается буквально до нескольких строк. По сути, вы создаете экземпляр объекта RegEx, передаете ему строку для обработки, а также само регулярное выражение (строку, включающую инструкции на языке регулярных выражений) — и все готово.

В следующей таблице показана часть информации о перечислении RegexOptions, экземпляр которого можно передать конструктору класса RegEx:

Структура перечисления RegexOptions

Член Описание
CultureInvariant Предписывает игнорировать национальные установки строки
ExplicitCapture Модифицирует способ поиска соответствия, обеспечивая только буквальное соответствие
IgnoreCase Игнорирует регистр символов во входной строке
IgnorePatternWhitespace Удаляет из строки не защищенные управляющими символами пробелы и разрешает комментарии, начинающиеся со знака фунта или хеша
Multiline Изменяет значение символов ^ и $ так, что они применяются к началу и концу каждой строки, а не только к началу и концу всего входного текста
RightToLeft Предписывает читать входную строку справа налево вместо направления по умолчанию — слева направо (что удобно для некоторых азиатских и других языков, которые читаются в таком направлении)
Singleline Специфицирует однострочный режим, в котором точка (.) символизирует соответствие любому символу

После создания шаблона регулярного выражения с ним можно осуществить различные действия, в зависимости от того, что вам необходимо. Можно просто проверить, существует ли текст, соответствующий шаблону, в исходной строке. Для этого нужно использовать метод IsMatch(), который возвращает логическое значение:

Если нужно вернуть найденное соответствие из исходной строки, то можно воспользоваться методом Match(), который возвращает объект класса Match, содержащий сведения о первой подстроке, которая сопоставлена шаблону регулярного выражения. В этом классе имеется свойство Success, которое возвращает значение true, если найдено следующее совпадение, которое можно получить с помощью вызова метода Match.NextMatch(). Эти вызовы метода можно продолжать пока свойство Match.Success не вернет значение false. Например:

Извлечь все совпадения можно и более простым способом, используя метод Regex.Matches(), который возвращает объект класса MatchCollection, который, в свою очередь, содержит сведения обо всех совпадениях, которые обработчик регулярных выражений находит во входной строке. Например, предыдущий пример может быть переписан для вызова метода Matches вместо метода Match и метода NextMatch:

Наконец, можно не просто извлекать совпадения в исходной строке, но и заменять их на собственные значения. Для этого используется метод Regex.Replace(). В качестве замены методу Replace() можно передавать как строку, так и шаблон замены. В следующей таблице показано как формируются метасимволы для замены:

Метасимволы замены в регулярных выражениях C#

Символ Описание Пример шаблона Пример шаблона замены Результат (входная -> результирующая строки)
$ number Замещает часть строки, соответствующую группе number \b(\w+)(\s)(\w+)\b $3$2$1 «один два» -> «два один»
$$ Подставляет литерал «$» \b(\d+)\s?USD $$$1 «103 USD» -> «$103»
$& Замещает копией полного соответствия (\$*(\d*(\.+\d+)?)<1>) **$& «$1.30» -> «**$1.30**»
$` Замещает весь текст входной строки до соответствия B+ $` «AABBCC» -> «AAAACC»
$’ Замещает весь текст входной строки после соответствия B+ $’ «AABBCC» -> «AACCCC»
$+ Замещает последнюю захваченную группу B+(C+) $+ «AABBCCDD» -> «AACCDD»
$_ Замещает всю входную строку B+ $_ «AABBCC» -> «AAAABBCCCC»


Давайте рассмотрим метод Regex.Replace() на примере:

Для закрепления темы давайте рассмотрим еще один пример использования регулярных выражений, где будем искать в исходном тексте слово «сериализация» и его однокоренные слова, при этом выделяя в консоли их другим цветом:

Результат работы данной программы:

Для проверки гибкости работы регулярных выражений, подставьте в исходный текст еще несколько слов «сериализация», вы увидите, что они будут автоматически выделены зеленым цветом в консоли.

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

Для работы с регулярными выражениями в С/С++ создана библиотека regex.h. В ней предопределены основные функцкии:

Функция regcomp предназначена для компиляции регулярного выражения. Эта функция компилирует регулярное выражение pattern учитывая флаги cflags и помещает его в структуру preg.
Флаги могут быть составлены как побитовое или следующих элементов:

  • REG_EXTENDED — использовать расширенные регулярные выражения
  • REG_ICASE — не различать заглавные и строковые буквы при сопоставлении строки с регулярным выражением

Функция regexec сопоставляет регулярное выражение скомпилированное и помещённое в структуру preg со строкой string. При этом в случае успешного сопоставления возвращается нулевое значение, в протичном случае — код ошибки. Аргумент eflags — побитовое ИЛИ REG_NOTBOL и REG_NOTEOL. Они определяет, являются ли границы цепочки границами строки (для обработки фиксаторов ^ и $). Если аргумент nmatch равен нулю, то pmatсh игнорируется, иначе он должен указывать на массив nmatch элементов, который будет заполнен смещениями подцепочек.

Для расшифровки ошибок используется функция regerror(). Она преобразует errcode возвращённый сопоставлением регулярного выражения и строки и помещает строковое значение ошибки в переменную errbuf, при этом следует указывать её размер.

Функция regfree() освобождает память запрошенную при компилировании регулярного выражения. Указатель на структуру регулярного выражения использовать после этого нельзя.

Эта функция — простейшая функция для распознания IP адреса. При помощи директивы define мы предоприделили строку с регулярным выражением и строку для проверки. В главной функции прежде всего компилируется регулярное выражение PATTERN и помещается в структуру preg. Сразу проверяем наличие ошибок и если они есть, выводим на экран. После этого сравниваем строку STRING с регулярным выражением в preg. В случае совпадения выводим «true», в противном случае — «false» и расшифровуем ошибку при помощи regerror.

Регулярные выражения в си++.

Здравствуйте. Помогите решить проблему.

Дана строка, в которой содержится http заголовок (типа GET /HTTP/1.0 и т.д.).
Надо выделить из строки адрес запрашиваемого ресурса.

Знаю, как это сделать в perle с помощью regex, но надо написать на си++.
Я плохо ориентируюсь в си++, как это сделать?
Если можно покажите пример какой-нибудь.

2 ответа

Originally posted by SN_ok
Здравствуйте. Помогите решить проблему.

Дана строка, в которой содержится http заголовок (типа GET /HTTP/1.0 и т.д.).
Надо выделить из строки адрес запрашиваемого ресурса.

Знаю, как это сделать в perle с помощью regex, но надо написать на си++.
Я плохо ориентируюсь в си++, как это сделать?
Если можно покажите пример какой-нибудь.

есть набор POSIX функций для обработки регекспов:

для С++ вроде в BOOST есть спец. классы.

Спасибо за указания вектора поиска.

Здесь — BOOST RUS — дано частичное описание этой библиотеки на русском языке.
Вот прямая ссылка на документацию BOOST::REGEX.


2. Здесь — «C++ Boost настройка, установка, использование — просто» — дано (неполное) описание «Установка на примере windows».

Я установил библиотеку для c++ builder 6 так.
Cкачал исходники библиотеки (около 13Mb),
и инструмент установки, компиляции бибилиотеки BOOST Jam (100Kb).

Разархивировал оба файла, скопировал bjam.exe (из архива boost-jam-3.1.12-1-ntx86.zip) в папку с исходниками (из архива boost_1_33_1.tar.gz).

Теперь из папки с исходниками открываем консоль cmd и пишем команду: bjam «-sTOOLS=borland» install
Ждем завершения компиляции (около 2 мин). В папке C:\Boost\ увидим результаты компиляции.

Переносим все содержимое папки C:\Boost\lib\
С:\Program Files\Borland\CBuilder6\Lib\

Копируем папку boost из из исходников в
E:\Program Files\Borland\CBuilder6\Include\

#include
#include
#include
#include

int main ( vo >) <
using namespace std ;
using namespace boost ;

regex expression ( «([_a-zA-Z \\ d \\ — \\ .]+)@([_a-zA-Z \\ d \\ -]+( \\ .[_a-zA-Z \\ d \\ -]+)+)» ) ;
string str ( «Мой E-mail: [email]sem@ciam.ru[/email]. Задавайте любые вопросы.» ) ;
cmatch what ;

if ( regex_search ( str , what , expression ) ) <
string name , domain ;
name . assign ( what [ 1 ] . first , what [ 1 ] . second ) ;
domain . assign ( what [ 2 ] . first , what [ 2 ] . second ) ;
cout «Имя: » name ;
cout «, домен: » domain endl ;
> else
cout «E-Mail в строке не найден» endl ;

Работа с регулярными выражениями в C#

В C# поддерживается работа с регулярными выражениями. Средства для работы с ними собраны в пространстве имён System.Text.RegularExpressions. При этом основой механизма обработки регулярных выражений является класс Regex.

Создание объекта для обработки регулярного выражения

Допустим, есть регулярное выражение для проверки номера телефона в определённом формате.

Для того чтобы начать работу с этим регулярным выражением необходимо создать экземпляр класса Regex передав в качестве параметра конструктора данное выражение.

Регулярное выражение задаётся только один раз при создании экземпляра класса Regex. Для работы с другим регулярным выражением потребуется создать новый экземпляр этого класса.

Поиск соответствий

Единичный результат поиска соответствий представлен объектом класса Match и доступен через его свойство Value.

Для того чтобы найти единичное соответствие регулярному выражению (при его наличии) необходимо воспользоваться методом Match класса Regex передав ему в качестве параметра анализируемую строку.

Этот способ корректно работает и в случае наличия в анализируемой строке нескольких соответствий. В этом случае в объекте Match будет храниться только самое первое соответствие от её начала.

Если же требуется получить все соответствия можно прибегнуть к одному из двух следующих способов:

    Воспользоваться методом Matches класса Regexp (вызывается аналогично Match), который возвратит все соответствия регулярному выражению в виде объекта класса MatchCollection.

C ++ множественное совпадение строк без использования регулярных выражений

Я пытаюсь отредактировать существующую программу C ++ для поиска текста по нескольким строкам, а не по одной. Я полный нуб C ++, поэтому я надеюсь, что кто-то может указать мне на функцию, которая будет работать и быстро. Я пытался использовать регулярные выражения (см мой предыдущий пост ) и это работает, но регулярные выражения слишком медленные для моей цели. Возьмите следующий код (любезно предоставлено @ WiktorStribiżew):

Это приводит к желаемому ответу (5), но слишком медленный в моей цели (поиск каждой строки в файлах

100 + ГБ). Как я могу сделать это быстрее? Входная строка темы s читается из файла и строки запроса шаблон предоставляется в командной строке, поэтому он представлен в виде строки (например, разделенной трубами в A | D).

Решение

Регулярные выражения и производительность кусают друг друга.
Я переписал ваш пример, чтобы не использовать Regex, и скомпилировал обе версии (на OS X, используя g ++ 5.3.0 с -O3 -fwhole-program ):

Сгенерированная сборка составила 545 строк:

Сборка из вашего примера составляет 31’559 строк, и при попытке опубликовать ее здесь или на Pastebin вылетела вкладка моего браузера.

Тем не менее, я также написал C версию вышеупомянутой программы:

Что составляет всего 95 строк сборки:

Я предлагаю вам перевести хотя бы эту часть кода на C. Если у вас есть окружающая кодовая база C ++, встраивание ее не должно быть слишком большой проблемой.

В любом случае, вы можете взять один из приведенных выше примеров, изменить str чтобы быть прочитанным из файла, измените patterns массив столько шаблонов, сколько необходимо (не забудьте установить NUM_PATTERNS на ту же сумму!), и если этого все еще недостаточно для повышения производительности, реализуйте цикл, который будет разделен на несколько потоков (хотя это слишком широкий вопрос, чтобы охватить и C, и C ++ в этом ответе).

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

Если вы компилируете примеры C здесь как C ++, убедитесь, что изменили все директивы include с в т.е. в и т.п.

Цукерберг рекомендует:  Бесплатка хэллоуин-иконки
Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих