我需要找到一种方法来获取指向大字符串的多个子指针(模式)的子字符串(如strstr,首次出现)的指针。 C的标准strstr()只支持一根针,我需要2根针甚至3根针。
为什么要这样?我需要能够将html文档“标记化”为多个部分,以进一步解析这些“代码段”。我需要标记的“锚”可能会有所不同,例如<div class="blub"><span id="bla,用作标记的html标签可能在id / class属性值中包含数字(因此,我可以使用\d+或例如进行过滤)。

所以我想写一个使用posix regex的函数。

该函数如下所示:

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文档使用该功能无法正常工作。它找不到模式。在内存映射的字符串上使用此功能(在解析HTML文档数据时,我将mmap'ed文件用作tmp存储的缓存)。

编辑:

在这样的循环中:

变量:parse_tag->firsttokenparse_tag->nexttoken是我尝试匹配的html锚,就像上面说明的那样。 doc是输入文档,它是从mmap缓存中分配的且以'\ 0'结尾的字符串(带有strndup())。
下面的代码按预期与strstr()一起使用。如果我发现了问题,使用regex strstr的想法对我来说确实有效,我可以重写循环,并可能从reg_strstr返回所有匹配项(以字符串列表等形式)。所以现在我只是在尝试...


...
char *tokfrom = NULL, *tokto = NULL;
char *listend = NULL;

/* 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; }

/* 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);


}
...


编辑结束

我在这里想念什么吗?就像我说的那样,这完全有可能实现吗?正则表达式模式错误吗?

欢迎提出建议!

安德烈亚斯

最佳答案

我尝试过在一个测试HTML文件上编码,我只是通过重定向通过stdin从文本文件输入了文本文件,并且在重复读取fgets()时似乎还可以。然后,我会怀疑问题出在内存映射文件中字符串数据的格式中。我的怀疑是您的内存映射文件中某处有一个以零结尾的字符,因此,如果您只是将内存映射文件本身用作char缓冲区,则它比预期的要早得多的结束字符串。

其次,您只返回第一个匹配项加上字符串的其余部分,如果您使用指向内存映射文件的指针作为str参数,则意味着从第一个匹配项开始的整个文件。如果您要标记文件,我怀疑您的“真实”实现有点不同吗?



编辑

我一直在看您的概念代码,它似乎确实可以正常工作。我做了一些修改,只是为了帮助我打印出来,但这是我正在编译的内容(对于文件内存映射,它很脏,只是为了检查正则表达式代码是否正常工作):

#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"的简单匹配项,如果我在Bloomberg这样的页面的整个HTML源代码上运行它,我将总共获得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 in原始字符串。使用不同的正则表达式,我确定您会得到不同的结果,但是总的来说,您的概念似乎有效,或者至少像strstr()一样有效,即从匹配开始返回整个字符串。子字符串一直到字符串末尾。

如果您没有获得预期的结果(我不确定您到底没有得到什么),那么我会说问题出在正则表达式本身或循环中,因为您可能取消索引编制(即,使用totok--,您可能会为自己创建一个循环,该循环只是不断在字符串的同一点返回匹配项)。

10-06 13:14