Основанная на регулярных выражениях функция strstr в C - PullRequest
0 голосов
/ 05 мая 2011

Мне нужно было найти способ получить указатель на подстроку (например, strstr, первый случай) на несколько возможных игл (шаблонов) большой строки. Стандарт C strstr() поддерживает только одну иглу, мне нужно 2 иглы или даже 3. Почему все это? Мне нужно иметь возможность «разбить» HTML-документ на части для дальнейшего разбора этих «фрагментов». «Якорь», который мне нужен для токенизации, может варьироваться, например <div class="blub"> или <span id="bla, а теги html, которые будут использоваться в качестве токена, могут содержать числа в значениях атрибута id / class (там я могу использовать \d+ или такое фильтровать).

Поэтому я подумал написать функцию с использованием регулярного выражения posix.

Функция выглядит так:

char * reg_strstr(const char *str, const char *pattern) {
    char *result = NULL;
    regex_t re;
    regmatch_t match[REG_MATCH_SIZE];

    if (str == NULL)
        return NULL;

    if (regcomp( &re, pattern, REG_ICASE | REG_EXTENDED) != 0) {
        regfree( &re );         
        return NULL;
    }

    if (!regexec(&re, str, (size_t) REG_MATCH_SIZE, match, 0)) {

        fprintf( stdout, "Match from %2d to %2d: \"%s\"\n",
             match[0].rm_so,
             match[0].rm_eo,
             str + match[0].rm_so);
        fflush(stdout);

        if ((str + match[0].rm_so) != NULL) {
            result = strndup(str + match[0].rm_so, strlen(str + match[0].rm_so));
        }
    }

    regfree( &re );

    return result;
}

Константа REG_MATCH_SIZE равна 10

Прежде всего, имеет ли смысл идея использовать регулярное выражение в качестве расширенной функции strstr?

В простых тестовых случаях эта функция работает нормально:

char *str_result = reg_strstr("<tr class=\"i10\"><td><div class=\"xyz\"><!--DDDD-1234--><div class=\"xx21\">", "<div class=\"xyz\">|<div class=\"i10 rr");

printf( "\n\n"
    "reg_strstr result: '%s' ..\n", str_result);

free( str_result) ;

Использование этой функции в реальной среде с использованием полного HTML-документа не работает должным образом. Это не находит образец. Использование этой функции в отображенной в памяти строке (я использую файл mmap в качестве кэша для хранения tmp при анализе данных документа HTML).

EDIT:

Здесь в цикле, как используется:

Переменные: parse_tag->firsttoken и parse_tag->nexttoken - это якоря html, которые я пытаюсь сопоставить, как показано выше. doc является входным документом, из кэша mmap'а выделенная строка с завершением '\ 0' (с strndup()). Код ниже работает с strstr(), как и ожидалось. Если я узнаю, идея использования regex strstr действительно работает для меня, я могу переписать цикл и, возможно, вернуть все совпадения из reg_strstr (в виде списка строк или тому подобного). Так что сейчас я просто пытаюсь ...

<pre> ... char *tokfrom = NULL, *tokto = NULL; char *listend = NULL;</p> <p>/* first token found ? */ if ((tokfrom = strstr(doc, parse_tag->firsttoken)) != NULL) { /* is skipto_nexttoken set ? */ if (!parse_tag->skipto_nexttoken) tokfrom += strlen(parse_tag->firsttoken); else { /* ignore string between firsttoken and first nexttoken */ if ((tokfrom = strstr(tokfrom, parse_tag->nexttoken)) == NULL) goto end_parse; } </p> <pre><code>/* no listend tag found ? */ if (parse_tag->listend == NULL || (listend = reg_strstr(tokfrom, parse_tag->listend)) == NULL) { listend = doc + strlen(doc); } *listend = '\0'; /* truncate */ do { if((tokto = reg_strstr(tokfrom + 1, parse_tag->nexttoken)) == NULL) tokto = listend; tokto--; /* tokto-- : this token up to nexttoken */ if (tokto <= tokfrom) break; /* do some filtering with current token here ... */ /* ... */ } while ((tokfrom = tokto + 1) < listend);

} ...

РЕДАКТИРОВАТЬ КОНЕЦ

Я что-то здесь скучаю? Как сказал, возможно ли это вообще, чего я пытаюсь достичь? Является ли шаблон регулярного выражения ошибочным?

Предложения приветствуются!

Andreas

Ответы [ 3 ]

1 голос
/ 05 мая 2011

Я пробовал код в тестовом HTML-файле, который я просто вводил из текстового файла через stdin с помощью перенаправления, и это, казалось, прекрасно работало при повторных чтениях до fgets(). Тогда я подозреваю, что проблема заключается в форматировании строковых данных в вашем файле с отображением в памяти. Я подозреваю, что где-то в вашем файле отображения памяти есть завершающий ноль символ, так что если вы просто используете сам файл отображения памяти в качестве буфера символов, он заканчивает строку намного раньше, чем вы ожидали.

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


EDIT

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

#include <regex.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>

#define REG_MATCH_SIZE 10
#define FILE_SIZE 60000

static int total_matches = 0;

char * reg_strstr(const char *str, const char *pattern) 
{
    char *result = NULL;
    regex_t re;
    regmatch_t match[REG_MATCH_SIZE];

    if (str == NULL)
        return NULL;

    if (regcomp( &re, pattern, REG_ICASE | REG_EXTENDED) != 0) {
        regfree( &re );         
        return NULL;
    }

    if (!regexec(&re, str, (size_t) REG_MATCH_SIZE, match, 0)) {

        fprintf( stderr, "@@@@@@ Match from %2d to %2d @@@@@@@@@\n",
             match[0].rm_so,
             match[0].rm_eo);

    total_matches++;

        if ((str + match[0].rm_so) != NULL) {
            result = strndup(str + match[0].rm_so, strlen(str + match[0].rm_so));
        }
    }

    regfree( &re );

    return result;
}


int main()
{   
    int filedes = open("testhtml.txt", O_RDONLY);

    void* buffer = mmap(NULL, FILE_SIZE, PROT_READ, MAP_PRIVATE, filedes, 0); 

    char* str_result;
    char* temp_buffer = strdup((char*)buffer);
    while(str_result = reg_strstr(temp_buffer, "<div"))
    {
        char* temp_print = strndup(str_result, 30);
        fprintf(stderr, "reg_strstr result: '%s' ..\n\n", temp_print);
        free(temp_print);
        free(temp_buffer);
        temp_buffer = strdup(str_result + 1);
        free( str_result) ;
    }

    fprintf(stderr, "Total Matches: %d\n", total_matches);

    return 0;
}

Просто используя простое совпадение для "<div", если я запускаю его на всем исходном HTML-коде для страницы, подобной этой здесь, на Bloomberg , я получаю в общей сложности 87 совпадений и что-то получаю это эквивалентно тому, что вы получите при повторном вызове стандарта strstr(). Например, пример выходных данных выглядит следующим образом (примечание: я обрезал совпадение возвращаемой строки после 30 символов для здравого смысла):

@@@@@@ Match from 5321 to 5325 @@@@@@@@@
reg_strstr result: '<div id="noir_dialog" class="p' ..

@@@@@@ Match from 362 to 366 @@@@@@@@@
reg_strstr result: '<div id="container" class="mod' ..

Соответствующие индексы меняются конечно, поскольку новая входная строка короче, чем предыдущая входная строка, поэтому вы видите совпадение, которое начинается с 5321, но затем следующее совпадение - с 362 ... общее смещение будет на 5683 в исходной строке. Я уверен, что с другим регулярным выражением вы получите другие результаты, но в целом кажется, что ваша концепция работает или, по крайней мере, работает так, как strstr() будет работать, то есть она возвращает всю строку, начиная с совпадения, к подстрока полностью до конца строки.

Если вы не получаете ожидаемых результатов (я не уверен, что именно вы не получаете), то я бы сказал, что проблема либо в самом регулярном выражении, либо в цикле, в что у вас может быть отключено индексирование (т. е. с помощью totok-- вы можете создать цикл для себя, который просто будет возвращать совпадение в той же точке строки).

0 голосов
/ 05 мая 2011

Почему бы вам просто не использовать конечный автомат, который обрабатывает один символ за раз и анализирует HTML-теги.Таким образом, вы также избавитесь от проблемы комментариев HTML.Подумайте о следующих случаях:

Тег привязки в комментарии HTML:

<!-- <my anchortag="foo"> -->

Комментарий в атрибуте HTML:

<some tag="<!--"> <my anchortag="foo"> </some tag="-->">

С регулярными выражениями у вас будетТрудно разобраться с этими случаями.

Некоторые люди, сталкиваясь с проблемой, думают: «Я знаю, я буду использовать регулярные выражения».Теперь у них две проблемы.(Джейми Завински)

0 голосов
/ 05 мая 2011

Убедитесь, что загружаемые вами данные имеют нулевое окончание.Аргумент 2 регулярного выражения должен быть строкой с нулевым символом в конце.

...