簡體   English   中英

如果 getline 的 `size` 參數未重置為 0,則 Gnu 正則表達式會失敗

[英]Gnu regex fails if `size` parameter of getline is not reset to 0

下面的代碼由讀取一堆文本文件的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 regex\n");
        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: %s\n", 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 %s\n", 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: %zu\n", 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 match\n");
    }
}

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

所以模式匹配失敗,字符串“jack”實際上匹配。

您可以看到,在完成讀取第一個文件后, len設置為 240,因此當getline再次為第二個文件執行時,它將讀取緩沖區大小為240的文件,但這由於某種原因導致正則表達式匹配失敗。

如果我在讀取第一個文件后將len重置為 0 參數,則代碼按預期工作(正則表達式匹配工作正常)。

那么為什么getline() len參數會影響 gnu 正則表達式的行為呢?

那么為什么 getline() len 參數會影響 gnu 正則表達式的行為呢?

正如瑪麗安評論的那樣,您使用getline不正確,導致它損壞堆。 您可以通過使用-fsanitize=address標志編譯程序並運行它來觀察這一點。 請參閱Address Sanitizer 手冊以了解錯誤。

這是未定義的行為,您的程序可以做任何事情 在這里,它恰好導致 GNU 正則表達式庫停止正常工作。 SIGSEGV是另一個可能的結果。

要解決此問題,您應該將free調用移出循環,並且僅在完成讀取行后才釋放內存。

free后在循環中設置line = NULL是另一種可能(但效率較低)的修復方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM