Автор Гілка: Використання regex у C.  (Прочитано 2095 раз)

Відсутній tech

  • Письменник
  • *****
  • дописів: 586
  • Карма: +1/-0
  • Гигик
Насамперед, повинен зауважити, що ця стаття набагато перевишує мій рівень знань C.  Багато з того що тут викладено зібрано з уривків з Інтернету і ще не до кінця освоєно мною. Не не вважаю себе програмістом, це скоріше для мене забава, покищо. Дуже радий, якщо хтось хто дійсно програмує на C поправить/доповнить деякі розмазані місця.

Мене давно зацікавив regex.h заголовок  з стандартної бібліотеки. man 3 regex саме його описує.  Він дозволяє використання POSIX регулярних виразів всередині C програм, тобто люба наша C програмка може мати цю додаткову функціональність. Якщо ви заглянете у GNU libc докумeнтацію, ви також знайдете опис цієї бібліотеки:
http://www.gnu.org/software/libc/manual/html_node/Regular-Expressions.html#Regular-Expressions

Отже, існує 4 функції у заголовку regex.h:

int regcomp(regex_t *preg, const char *pattern, int cflags);
int regexec(const 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 струкруру даних яку ви можете використовувати із regexec для порівнювання із рядком. Змінна-покажчик regex_t типу preg вказує на адресу буфера,
де зберігатиметься "скомпільований" регулярний вираз. "Скомпільований" таким чином формат оптимізовано для ефективного порівнювання, здійснюваного пізніше regexec. Змінна-покажчик char типу, *pattern вказує на рядок із нульовим закінченням '\0' який власне буде скомпільовано regcomp.
Функція regcomp має наступні опції, вказані в кінці як cflags:  

    REG_EXTENDED = Extended Regular Expressions (використання розширених регулярних виразів)
    REG_ICASE = Пошук незалежний від регістра (case insensitive)
    REG_NOSUB = Повідомлювати лише про вдале/невдале виконання regexec().
    REG_NEWLINE = Читати через нові лінії

regexec
Виконує порівнювання рядків з нульовим закінченням (char *string) із буфером шаблона отриманим від
regcomp (regex_t *preg). Змінні size_t nmatch та regmatch_t pmatch[] використовуються для вказування місця знаходження співпадань. Ключами (int eflags) для regexec можуть бути:

    REG_NOTBOL - ^ не являєтиметься вказівником початку лінії. Цей ключ використовується коли різні       рядки подаються regexec і початок рядка не повинен розглядатися як початок нової лінії.
    REG_NOTEOL - $ не являтиметься знаком кінця лінії

regexec повертає 0 якщо співпадання відбулося, REG_NOMATCH, якщо ні, REG_ENOSYS якщо фунцкія (вираз) не пістримується.

regfree
Звільняє всю пам'ять яка використовується regexec.

Програмування.
Запам'ятате що regcomp повинен бути викликаним першим.
У regcomp ви завантажуєте рядок який вас цікавить, він обробляється у regex_t внутрішню структуру,
яка потім передається regexec для власне порівнювання із іншими рядками. Простий виклик regcomp
виглядатиме так:
    
    regcomp(&re, "^fre{1,}", 0);    /* &re і є regex_t структурою */

Простий виклик regexec:

    regexec(&re, "freedom", (size_t)0, NULL, 0);
 
Це приклад regexec завантажиться без додаткових опцій, так само як і попередній.
В кінці дуже важливо не забути звільнити пам'ять:

    regfree(&re);

Виклик regerror може виглядати так:
 
    regerror(error, &re, string, 128);

Де error, це десяткове число (int змінна), що містить повернене значення від regexec або regcomp,
і string, це рядок довжиною у 128 знаків.

В кінці, приклад функції, написаної із використанням викладеного вище:
#include <sys/types.h>
#include <regex.h>
 
int match(const char *string, char *pattern) {
int status;
regex_t re;
 
if(regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0)
{
        return 0;
}
    
        status = regexec(&re, string, (size_t)0, NULL, 0);
 
        regfree(&re);
 
if(status != 0)
{
        return 0;
}
        return 1;
}
« Змінено: 2005-08-07 02:39:08 від tech »

Відсутній tech

  • Письменник
  • *****
  • дописів: 586
  • Карма: +1/-0
  • Гигик
Re: Використання regex у C.
« Відповідей #1 : 2005-08-07 02:29:20 »
Тепер простенька програмка, яка використовує цю функціональність:

#include<stdio.h>    /* provides the fopen/fclose functios/
#include<stdlib.h>          
#include <strings.h>
#include <regex.h>    
             /* a static file : */
#define FILE_NAME "uacomp.dict"
#define PTT_LEN 51
#define MAX_LEN 501

int match(const char *string, char *pattern) {
    int status;
    regex_t re;
 
    if(regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0)
    {
        return 0;
    }
    
    status = regexec(&re, string, (size_t)0, NULL, 0);
 
    regfree(&re);
 
    if(status != 0)
    {
        return 0;
    }
    return 1;
}

char *mygetline(char *str, int size)
{
      if(fgets(str, size, stdin))
      {
            char *nl = strchr(str, '\n'); /* checking for newline */
            if(nl)
            {
                  *nl = '\0';
            }
      }
      return str;
}

int main()
{
      FILE *fp;             /* file pointer of type FILE *, defined in stdio.h */
        char line[MAX_LEN];   /* our line */
        char pattern[PTT_LEN];
      fputs("Enter the pattern: ", stdout);
      fflush(stdout);
      mygetline(pattern, sizeof pattern);

      
      fp = fopen(FILE_NAME, "r");
      if (fp == NULL)
      {
            printf("Failed opening file %s\n", FILE_NAME);
            exit(EXIT_FAILURE);  
      } else {
            /* printf("File %s opened for reading.\n", FILE_NAME); */
      
              while((fgets(line, sizeof(line), fp)) != NULL)
              {
                  if((match(line, pattern)) > 0)
                  {
                        printf("+ %s", line);
                  }
              }
      }            
      fclose(fp);     /* closing stream */
      return 0;
}
« Змінено: 2005-08-07 02:38:09 від tech »