我需要找到一种方法来获取指向子串的指针(如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()
一起使用。如果我发现,使用正则表达式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);
}
...
编辑结束
我在这里想念一下吗?如上所述,这是否有可能实现我的目标?正则表达式模式是否错误?
欢迎提出建议!
安德烈亚斯
答案 0 :(得分:1)
我尝试在一个测试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。使用不同的正则表达式我相信你会得到不同的结果,但总的来说,似乎你的概念正在运行,或者至少像strstr()
一样工作,也就是说它从匹配开始返回整个字符串到子串一直到字符串的结尾。
如果你没有得到你期望的结果(我不确定你到底得到了什么),那么我会说问题出在正则表达式本身或循环中,您可以关闭索引(例如,使用totok--
,您可以为自己创建一个循环,只需在字符串中的同一点返回匹配项。)
答案 1 :(得分:0)
确保您加载的数据以空值终止。 regexec的Arg 2必须是以空字符结尾的字符串。
答案 2 :(得分:0)
为什么不简单地使用一次处理一个字符并解析HTML标记的有限状态机。这样,您还可以摆脱HTML注释的问题。想想以下情况:
HTML评论中的锚标记:
<!-- <my anchortag="foo"> -->
HTML属性中的注释:
<some tag="<!--"> <my anchortag="foo"> </some tag="-->">
使用正则表达式,您将很难处理这些情况。
有些人在面对问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。 (Jamie Zawinski)