Все о Linux. LinuxRSP.Ru


Cвежие новости Linux и BSD, анонсы статей и книг прямо в почтовый ящик!
Подписаться письмом


 Сегодняшние новости:

25 лет исполнилось ядру Linux

Релиз KDevelop 5.0

Oracle открывает код JDK9 для ARM

Выпущен Timewarrior 1.0.0

Релиз Android 7.0

Percona Memory Engine для MongoDB на базе WiredTiger

PowerShell открыт и доступен для Linux

Форк TrueCrypt: VeraCrypt 1.18

Релиз Snapcraft 2.14

Релиз Go 1.7

Стабильный выпуск рабочего стола Lumina

Вышла первая версия аналога OpenCV - DCV 0.1

Выпуск минималистичной программы для мониторинга jsonmon 3

В MIT разработали новый язык программирования

Первый релиз Qt5Gtk2

Godot 2.1 - новая версия открытого игрового движка

Свободная цифровая станция звукозаписи: Ardour 5.0

Обновление SkypeWeb Plugin for Pidgin

Вышла версия 3.0 Android File Transfer для Linux (и для OS X)

Программный аналог MIDI-контроллера для создания музыки: Launchpadd v1.3

Mozilla спонсирует поддержку Python 3.5 в PyPy

Ef 0.08 - программа для моделирования динамики заряженных частиц

Обновление текстового редактора TEA до версии 42.0.0

Релиз OpenOrienteering Mapper 0.6.4

Вышли Guix и GuixSD 0.11

Релиз Opera 39

Выпуск LibreOffice 5.2

В OpenSSH обнаружены и устранены некоторые уязвимости

Эмулятор FCEUX 2.2.3

Компания Билайн переходит на российскую СУБД с открытым исходным кодом Tarantool

Google

 Новые статьи :

Утилиты для восстановления потерянных данных в Linux

Лучшие файловые менеджеры для Android

20 лучших бесплатных книг о Linux

Как сгенерировать открытый/закрытый SSH-ключ в Linux

Grive - клиент Google Drive для Linux с открытым исходным кодом

Протокол IPv6: варианты подключения

Сервер из образа: DHCP + TFTP + Initrd + OpenVZ

Обзор веб-панелей управления хостингом

Приёмы работы с Vim

Nginx как Reverse Proxy для сайта, использующего SSL

Разработка модулей ядра Linux

Мониторинг нагрузки http-сервера Apache 2

Перевод комментариев к файлу конфигурации Squid

Решение проблем при использовании "1c предприятие" 8.2 в Linux

Advanced Bash-Scripting Guide Искусство программирования на языке сценариев командной оболочки







Rambler's Top100





 
 

POSIX-совместимые функции

Описания данных функции содержатся в заголовочном файле /usr/include/preposix.h и библиотеке /usr/lib/preposix.a. Они поддерживают малое количество возможностей и созданы лишь для совместимости со стандартными. Вот их список:


int regcomp(regex_t *preg, const char *pattern, int cflags);
int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
void regfree(regex_t *preg);

Функция regcomp предназначена для преобразования регулярного выражения во внутреннюю форму. Первый параметр (regex_t *preg) - это переменная, в которую заносится преобразованный шаблон, который затем используется в функции regexec. Вторым параметром (const char *pattern) передается преобразовываемое регулярное выражение. Последний параметр (int cflags) содержит флаги, задающие параметры преобразования:

REG_ICASE
Указывает, что сравнение будет производится без учета регистра символов.
REG_NEWLINE
Указывает, что сравнение будет производится в многострочной строке, т.е. содержащей '\n'.

В случае успешного преобразования функция regcomp возвращает 0, иначе код ошибки.

Коды ошибок могут иметь следующие значения:

REG_BADRPT
Неправильное использование модификатора, такого как `*' в качестве первого символа.
REG_BADBR
Неправильное использование оператора обратной ссылки.
REG_EBRACE
Незакрытая фигурная скобка в операторе интервала.
REG_EBRACK
Незакрытая скобка в операторе списка.
REG_ERANGE
Неправильное использование оператора диапазона
REG_ECTYPE
Неизвестный класс символа.
REG_ECOLLATE
Неправильный символ шаблона.
REG_EPAREN
Незакрытая скобка в операторе группировки.
REG_ESUBREG
Неправильная обратная ссылка.
REG_EEND
Неизвестная ошибка.
REG_EESCAPE
Конечный backslash.
REG_BADPAT
Неправильное использование таких операторов шаблона как группирование и список.
REG_ESIZE
Преобразованное регулярное выражение требует буффер больше, чем 64K.
REG_ESPACE
Не хватает памяти.

Функция regexec производит сравнение строки с преобразованным регулярным выражением. Первый параметр (regex_t *preg) - это переменная, в которой содержится преобразованный функцией regcomp шаблон. Второй параметр (const char *string) - строка, которая Последний параметр (int eflags) содержит флаги, задающие параметры поиска:

REG_NOTBOL
Запрещает использование символа-шаблона начала строки (^) в регулярном выражении. Поиск по таким шаблонам всегда дает отрицательный результат.
REG_NOTEOL
Запрещает использование символа-шаблона конца строки ($) в регулярном выражении. Поиск по таким шаблонам всегда дает отрицательный результат.

Третий (size_t nmatch) и четвертый (regmatch_t pmatch[]) параметры функции regexec содержат результаты успешного поиска. Параметр pmatch является массивом переменных, содержащих структуру, описанную следующим образом:


typedef struct
{
   regoff_t rm_so;
   regoff_t rm_eo;
} 
regmatch_t;   

Элемент rm_so содержит номер символа, первого в подстроке, соответствующей шаблону. rm_eo содержит номер символа, последнего в подстроке, соответствующей шаблону.

Параметр nmatch задает максимальное количество элементов массива pmatch.

В случае успеха (нахождения подстроки, соответствующей регулярному выражению) функция regexec возвращает 0. В противном случает - REG_NOMATCH.

Рассмотрим простейший пример поиска по шаблону.


#include <stdio.h>
#include <pcreposix.h>

int main(int argc, char* argv[])
{
    char *str="Hello123!"; /* строка, в которой будет производится поиск */
    char *pattern="\\w{3}\\d+"; /* шаблон, по которому будет производится поиск */
    regmatch_t p[20]; /* массив, в который будут заноситься результаты поиска */
    regex_t f; /* переменная, для хранения преобразованного шаблона */
    int i,j;
	    
    if(regcomp(&f,pattern,0)) /* если возвращаемое значение не 0 - ошибка */
    {
	printf("Ошибка в шаблоне! %i\n",i);
        exit(1);
    }
    if(regexec(&f,str,20,p,0)) /* если возвращаемое значение не 0 - значит ничего не найдено */
    {
        printf("Не найдено!\n");
        exit(1);
    }
    printf("Найдено:\n");
    /* номер первого и последнего символов найденной подстроки, соответствующей шаблону, */
    /* заносятся в первый элемент массива p */
    for(j=p[0].rm_so;j<p[0].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    return 0;
}

Собирается программа таким образом:


gcc -o имя_файла имя_файла.c `pcre-config --libs-posix`

Например, если файл исходника называется example1.c, то собираться он будет при помощи команды:


gcc -o example1 example1.c `pcre-config --libs-posix`

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


Найдено:
llo123

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


#include <stdio.h>
#include <pcreposix.h>

int main(int argc, char* argv[])
{
    char *str="Hello123!"; /* строка, в которой будет производится поиск */
    char *pattern="([A-Za-z]+)(\\d+)"; /* шаблон, по которому будет производится поиск */
    regmatch_t p[20]; /* массив, в который будут заноситься результаты поиска */
    regex_t f; /* переменная, для хранения преобразованного шаблона */
    int i,j;
	    
    if(regcomp(&f,pattern,0)) /* если возвращаемое значение не 0 - ошибка */
    {
        printf("Ошибка в шаблоне! %i\n",i);
        exit(1);
    }
    if(regexec(&f,str,20,p,0)) /* если возвращаемое значение не 0 - значит ничего не найдено */
    {
        printf("Не найдено!\n");
        exit(1);
    }
    printf("Найдено:\n");
    /* номер первого и последнего символов найденной подстроки, соответствующей всему шаблону, */
    /* заносятся в первый элемент массива p */
    /* номера первого и последнего символов найденной подстроки, соответствующей частям шаблона, */
    /* заключенных в скобки, попадают во второй и последующий элементы массива p */
    /* остальные элементы массива p заполняются парами (-1,-1) */
    for(i=0;p[i].rm_eo!=-1;i++) 
    {
        for(j=p[i].rm_so;j<p[i].rm_eo;j++) 
	    putchar(str[j]);
	putchar('\n');
    }
    return 0;
}

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


Найдено:
Hello123
Hello
123

Давайте рассмотрим пример посложнее, типичный для web-программирования - разбор URL'а на составные части - протокол://имя_сервера/путь/имя_файла?параметры. Шаблон в таком случае будет выглядеть таким образом: ^(\w+)://(.+?)/(.+)/(.+?)\?(.+?)$.


#include <stdio.h>
#include <pcreposix.h>

int main(int argc, char* argv[])
{
    char *str="http://dh.opennet.ru/cgi-bin/dbi_mysql_stat.pl?start=1&ip=129.55.55.55"; 
    char *pattern="^(\\w+)://(.+?)/(.+)/(.+?)\\?(.+?)$"; 
    regmatch_t p[20]; 
    regex_t f; 
    int i,j;
	    
    if(regcomp(&f,pattern,0)) /* если возвращаемое значение не 0 - ошибка */
    {
        printf("Ошибка в шаблоне!\n");
        exit(1);
    }
    if(regexec(&f,str,20,p,0)) /* если возвращаемое значение не 0 - значит ничего не найдено */
    {
	printf("Не найдено!\n");
	exit(1);
    }
    /* Первая группа скобок - протокол */
    printf("Протокол: ");
    for(j=p[1].rm_so;j<p[1].rm_eo;j++) 
        putchar(str[j]);
    putchar('\n');
    /* Вторая группа скобок - сервер */
    printf("Имя сервера: ");
    for(j=p[2].rm_so;j<p[2].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    /* Третья группа скобок - путь */
    printf("Путь к файлу: ");
    for(j=p[3].rm_so;j<p[3].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    /* Четвертая группа скобок - файл */
    printf("Имя файла: ");
    for(j=p[4].rm_so;j<p[4].rm_eo;j++) 
        putchar(str[j]);
    putchar('\n');
    /* Пятая группа скобок - параметры */
    printf("Параметры: ");
    for(j=p[5].rm_so;j<p[5].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
			
    return 0;
}

Программа должна вывести:


Протокол: http
Имя сервера: dh.opennet.ru
Путь к файлу: cgi-bin
Имя файла: dbi_mysql_stat.pl
Параметры: start=1&ip=129.55.55.55

У предыдущего примера есть недостаток - в URL'е необязательными элементами являются: путь, имя файла, параметры. Любой из перечисленных элементов может отсутствовать. Предыдущий шаблон требует иметь эти элементы в строке URL'а. Надо создать шаблон, который будет предусмативать отсутсвие данных элементов. Сделать можно с помощью такой конструкции - (шаблон)?, которая указывает, что подстрока, соответствующая указанному шаблону может встречаться один раз или отсутствовать. Выбраным нами условиям удовлетворяет такой шаблон: ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$. Разберем его по составным частям. Составные части буду выделять цветом. В первой группе - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - шаблон, под который должен попадать протокол. Предполагается, что он должен указан обязательно. Во второй группе - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - имя сервера. Также обязательный параметр. Третья группа - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - путь, имя файла, параметры. Вся эта группа может присутствовать или отсутствовать. Третья группа состоит из более мелких групп, первая из которых ( и четвертая в общем контексте ) - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - указывает на путь к файлу с завершающим "слэшем", который может также присутствовать или отсутствовать. В четвертую группу входит пятая - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - обязательная для четвертой группы, т.е. если подстрока, соответствующая четвертой группе присутствует (т.е. путь+/ ), значит обязательно должна присутствовать пятая группа (т.е. просто путь). Вторая группа в третьей группе /* я уже сам начинаю запутываться в нумерации, представляю, как ты себя сейчас чувствуешь...:) */ или шестая в общем контексте - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$ - указывает на необязательный элемент имя файла+парметры. Шестая группа также состоит из подгрупп /* никто и не обещал, что будет легко и безболезненно разобраться с регулярными выражениями...:)) */ - самого имени файла (седьмая группа - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$), необязательного для шестой группы, и символа "?"+параметры (восьмая группа - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$), также необязательного для шестой группы. Восьмая группа имеет внутри себя еще одну группу (девятую - ^(\w+)://(.+?)(/((.+)/)?((.+?)?(\?(.+?))?)?)?$. Наверное, можно это сделать более понятно и коротко, но я выбрал такой шаблон, чтобы объяснить тебе, как работает группировка. Надеюсь, мне это удалось. А вот и программа:


#include <stdio.h>
#include <pcreposix.h>

int main(int argc, char* argv[])
{
    char *str1="http://dh.opennet.ru/cgi-bin/dbi_mysql_stat.pl?start=1&ip=129.55.55.55";
    char *str2="http://dh.opennet.ru";
    char *str3="http://dh.opennet.ru/files/aboutyou.tar.gz";
    char *str4="http://dh.opennet.ru/main.html";
    char *str=str4; /* здесь можно поменять на str1,str2 или str3. Поэкспериментировать..:) */
		    
    char *pattern="^(\\w+)://(.+?)(/((.+)/)?((.+?)?(\\?(.+?))?)?)?$"; 
    regmatch_t p[20]; /* массив, в который будут заноситься результаты поиска */
    regex_t f; /* переменная, для хранения преобразованного шаблона */
    int i,j;
	    
    if(regcomp(&f,pattern,0)) /* если возвращаемое значение не 0 - ошибка */
    {
        printf("Ошибка в шаблоне!\n");
        exit(1);
    }
    if(regexec(&f,str,20,p,0)) /* если возвращаемое значение не 0 - значит ничего не найдено */
    {
        printf("Не найдено!\n");
        exit(1);
    }
    /* Первая группа скобок - протокол */
    printf("Протокол: ");
    for(j=p[1].rm_so;j<p[1].rm_eo;j++) 
        putchar(str[j]);
    putchar('\n');
    /* Вторая группа скобок - сервер */
    printf("Имя сервера: ");
    for(j=p[2].rm_so;j<p[2].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    /* Третья группа скобок - путь+файл+параметры */
    printf("Путь+файл+параметры: ");
    for(j=p[3].rm_so;j<p[3].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    /* Четвертая группа скобок - путь+/ */
    printf("Путь+/: ");
    for(j=p[4].rm_so;j<p[4].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    /* Пятая группа скобок - путь */
    printf("Путь к файлу: ");
    for(j=p[5].rm_so;j<p[5].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    /* Шестая группа скобок - имя файла+параметры */
    printf("Имя файла+параметры: ");
    for(j=p[6].rm_so;j<p[6].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    /* Седьмая группа скобок - имя файла */
    printf("Имя файла: ");
    for(j=p[7].rm_so;j<p[7].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    /* Восьмая группа скобок - ?+параметры */
    printf("?+параметры: ");
    for(j=p[8].rm_so;j<p[8].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    /* Девятая группа скобок - параметры */
    printf("Параметры: ");
    for(j=p[9].rm_so;j<p[9].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');

    return 0;
}

А вот и вывод:


Протокол: http
Имя сервера: dh.opennet.ru
Путь+файл+параметры: /main.html
Путь+/:
Путь к файлу:
Имя файла+параметры: main.html
Имя файла: main.html
?+параметры:
Параметры:

Пришло время рассказать о двух оставшихся функциях - regerror и regfree.

Функция regerror преобразует код ошибки, возвращаемый функциями regcomp и regexec, в сообщение об ошибке. Первым параметром (int errcode) передается код ошибки. Второй параметр (const regex_t *preg) - преобразованный шаблон. Третий (char *errbuf) - строка, в которую будет записано сообщение. Четвертый (size_t errbuf_size) - максимальная длина строки. Возьмем самый первый пример в этой главе, добавив к нему обработку ошибок и изменив немного шаблон так, чтобы в нем была ошибка, например - незакрытая скобка.


#include <stdio.h>
#include <pcreposix.h>

int main(int argc, char* argv[])
{
    char *str="Hello123!"; /* строка, в которой будет производится поиск */
    char *pattern="(\\w{8}\\d+"; /* шаблон с ошибкой */
    regmatch_t p[20]; /* массив, в который будут заноситься результаты поиска */
    regex_t f; /* переменная, для хранения преобразованного шаблона */
    int error; /* код ошибки */
    char erbuf[256]; /* сообщение об ошибке */
		
    int i,j;
		    
    if(error=regcomp(&f,pattern,0)) /* если возвращаемое значение не 0 - ошибка */
    {
        regerror(error,&f,erbuf,256);
        printf("Ошибка в шаблоне: %s\n",erbuf);
        exit(1);
    }
    if(error=regexec(&f,str,20,p,0)) /* если возвращаемое значение не 0 - значит ничего не найдено */
    {
        regerror(error,&f,erbuf,256);
        printf("Не найдено! %s\n",erbuf);
        exit(1);
    }
    printf("Найдено:\n");
    for(j=p[0].rm_so;j<p[0].rm_eo;j++) 
	putchar(str[j]);
    putchar('\n');
    return 0;
}

Вывод:


Ошибка в шаблоне: unbalanced () at offset 9

Функция regfree служит для освобождения (очистки) переменной для хранения преобразованного шаблона, которая передается как параметр (regex_t *preg). Это полезно, когда ты хочешь использовать одну и ту же переменную при нескольких операциях поиска. Например, когда ты выводишь все совпадения с указанным шаблоном в одной строке.


#include <stdio.h>
#include <pcreposix.h>

int main(int argc, char* argv[])
{
    char *str="Hello123!";
    char *str_tmp=str;
    char *pattern=".{3}"; 
    regmatch_t p[20]; 
    regex_t f; 
    int error; 
    char erbuf[256]; 
				
    int i,j;
    while(2*2==4)
    {
	if(error=regcomp(&f,pattern,0)) 
	{
	    regerror(error,&f,erbuf,256);
	    printf("Ошибка в шаблоне: %s\n",erbuf);
	    exit(1);
	}
							    
	if(error=regexec(&f,str_tmp,20,p,0)) 
	{
	    regerror(error,&f,erbuf,256);
	    exit(1);
	}
	printf("Найдено:\n");
							
							
	for(j=p[0].rm_so;j<p[0].rm_eo;j++) 
	    putchar(str_tmp[j]);
        putchar('\n');
			    
        regfree(&f);
        str_tmp+=p[0].rm_eo;
					    
    }
    return 0;
}

Вывод:


Найдено:
Hel
Найдено:
lo1
Найдено:
23!

Попробуй закомментировать строчку с regfree и посмотри, что будет выведено.:)

Вроде все рассказал о POSIX-совместимых функциях. Теперь перейдем к собственным функциям библиотеки PCRE.

Предыдущая

Содержание

Следующая


      

Связь | О проекте LinuxRSP | Реклама | О Linux
© 1999-2017 LinuxRSP