我需要找到一种方法来获取指向大字符串的多个子指针(模式)的子字符串(如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->firsttoken
和parse_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--
,您可能会为自己创建一个循环,该循环只是不断在字符串的同一点返回匹配项)。