如果getline的"size"参数未重置为0,c-Gnu regex将失败



下面的代码由读取一堆文本文件的read_files()和使用gnu-regex库根据模式进行字符串匹配的match()函数组成。

read_files()中,我使用getline()size参数设置为0,这样getline()将从默认的120大小开始,然后根据需要增加

#include <limits.h> // for PATH_MAX
#include <regex.h>  // for regcomp, regerror, regexec, regfree, size_t, REG...
#include <stdio.h>  // for printf, fprintf, NULL, fclose, fopen, getline
#include <stdlib.h> // for exit, free, EXIT_FAILURE
int match(const char *regex_str, const char *str) {
regex_t regex;
int reti;
char msgbuf[100];
/* Compile regular expression */
reti = regcomp(&regex, regex_str, REG_EXTENDED);
if (reti) {
fprintf(stderr, "Could not compile regexn");
exit(1);
}
/* Execute regular expression */
reti = regexec(&regex, str, 0, NULL, 0);
if (!reti) {
return 1;
} else if (reti == REG_NOMATCH) {
return 0;
} else {
regerror(reti, &regex, msgbuf, sizeof(msgbuf));
fprintf(stderr, "Regex match failed: %sn", msgbuf);
exit(1);
}
/* Free memory allocated to the pattern buffer by regcomp() */
regfree(&regex);
}
void read_files() {
size_t path_count = 2;
char pathnames[2][PATH_MAX] = {"./tmp/test0.conf", "./tmp/test1.conf"};
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read_count;
for (int i = 0; i < path_count; i++) {
printf("opening file %sn", pathnames[i]);
fp = fopen(pathnames[i], "r");
if (fp == NULL) {
printf("internal error,couldn't open file %s"}", pathnames[i]);
exit(EXIT_FAILURE);
}
int linenum=1;
while ((read_count = getline(&line, &len, fp)) != -1) {
printf("%d: %s",linenum,line);
linenum++;
}
printf("len: %zun", len);
fclose(fp);
// len=0; // this is the line that fixes the bug, if i reset len to 0 after reading the first file then everything works as expected, if i don't reset it then regex matching fails
if (line)
free(line);
}
}
int main(int argc, char *argv[]) {
read_files();
if (!match("^[a-zA-Z0-9]+$", "jack")) {
printf("input don't matchn");
}
}

test0.conf 的内容

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

test1.conf 的内容

testing123

当运行上面的代码时,我得到了这个输出:

opening file ./tmp/test0.conf
1: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
len: 240
opening file ./tmp/test1.conf
1: testing123
len: 240
input don't match

因此模式匹配失败;千斤顶";这实际上是匹配的。

您可以看到,在读取完第一个文件后,len被设置为240,因此当第二个文件再次执行getline时,它将读取具有240缓冲区大小的文件,但由于某种原因,这会导致正则表达式匹配失败。

如果我在读取第一个文件后将len参数重置为0,那么代码将按预期工作(正则表达式匹配工作正常)。

那么,为什么getline()len参数会影响gnu-regex的行为呢?

那么,为什么getline()len参数会影响gnu-regex的行为呢?

正如Marian所评论的,您错误地使用了getline,导致它损坏了堆。您可以通过编译带有-fsanitize=address标志的程序并运行它来观察这一点。请参阅地址消毒器手册来了解错误。

这是未定义的行为,您的程序可以执行任何操作。在这里,它只是碰巧导致GNU正则表达式库停止正常工作。SIGSEGV是另一种可能的结果。

要解决这个问题,您应该将free调用移出循环,并且只在读取完行之后释放内存。

free之后在循环中设置line = NULL是另一种可能的(但效率较低)修复方法。

最新更新