【问题标题】:How to do regex string replacements in pure C?如何在纯 C 中进行正则表达式字符串替换?
【发布时间】:2011-11-07 23:21:05
【问题描述】:

我查看了 POSIX 正则表达式库和 PCRE 库中的正则表达式函数,但它们似乎都没有字符串替换功能。我不想使用 C++,最好不需要链接另一个库(但如果必须的话我可以)。我需要手动替换字符串吗?如果是这样,我该如何使用捕获组?

【问题讨论】:

  • 您指的是哪些捕获组?没有用于正则表达式的内置 C 库,您必须使用 pcre 或类似的东西。
  • 一般来说,你不能在 C 字符串中进行字符串替换——你可能没有可用的内存。你能澄清一下你到底想做什么吗?
  • 所以你要我说的具体一点吗?我有一个 HTTP 请求标头,我想使用正则表达式更改并转发到服务器。
  • 这可能会有所帮助:linuxquestions.org/questions/programming-9/… -
  • 感谢 Alvin 的链接。有没有办法用正则表达式进行搜索和替换?

标签: c regex string


【解决方案1】:

regex.h 不提供对字符串替换的本机支持,但它确实提供了子表达式/捕获组,这使它更容易。我假设您熟悉正则表达式编译并跳到正则表达式执行和子表达式。

regexec()在regex.h(/usr/include/)中定义如下:

extern int regexec (const regex_t *__restrict __preg,
        const char *__restrict __string, size_t __nmatch,
        regmatch_t __pmatch[__restrict_arr],
        int __eflags);

第一个、第二个和最后一个参数分别是正则表达式、要执行的字符串和执行标志。第三个和第四个参数用于指定一个 regmatch_t 的数组。 regmatch_t 由两个字段组成:rm_so 和 rm_eo,它们分别是匹配区域的开始和结束的索引或偏移量。然后可以将这些索引与memcpy()memset()memmove()from string.h 一起使用来执行字符串替换。

我会做一个小例子,稍后再发布。

祝你好运,我希望这会有所帮助。

【讨论】:

  • 我会做一个小例子,稍后再发布。什么时候?你在 5 年前做出了这个承诺。 ;( Here are 到目前为止我发现的几个例子。
【解决方案2】:

PCRE 库本身不提供替换功能,但 PCRE 下载页面上有一个包装器功能,它接受 perl 样式 =~ s/pattern/replace/ 语法,然后使用 PCRE 本机函数为您执行替换/替换。转到http://www.pcre.org/,然后单击下载链接:ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/,然后单击Contrib 目录。你想要的包/项目是:pcrs-0.0.3-src.tar.gz.

请注意,我自己没有使用过它,所以我无法证明它的效果如何。然而,它是一段相当小而简单的代码,因此它可以很好地满足您的目的。

【讨论】:

【解决方案3】:
/* regex_replace.c
   :w | !gcc % -o .%<
   :w | !gcc % -o .%< && ./.%<
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <regex.h>

void  // *str MUST can be freed, i.e. obtainde by strdup, malloc, ...
regex_replace(char **str, const char *pattern, const char *replace) {
    regex_t reg;
    // if regex can't commpile pattern, do nothing
    if(!regcomp(&reg, pattern, REG_EXTENDED)) {
    size_t nmatch = reg.re_nsub; 
    regmatch_t m[nmatch + 1];
    const char *rpl, *p;
    // count back references in replace
    int br = 0;
    p = replace;
    while(1) { 
        while(*++p > 31); 
        if(*p) br++; 
        else break;
    } // if br is not equal to nmatch, leave
    if(br != nmatch) return;
    // look for matches and replace
    char *new;
    while(!regexec(&reg, *str, nmatch + 1, m, REG_NOTBOL)) {
        // make enough room
        new = (char *)malloc(strlen(*str) + strlen(rpl));
        if(!new) exit(EXIT_FAILURE);
        *new = 0;
        p = rpl = replace;
        int c;
        strncat(new, *str, m[0].rm_so); // test before pattern
        for(int k=0; k<nmatch; k++) {
        while(*++p > 16); // skip printable char
        c = *p;  // back referenc (e.g. \1, \2, ...)
        strncat(new, rpl, p - rpl); // add head of rpl
        // concat match
        strncat(new, *str + m[c].rm_so, m[c].rm_eo - m[c].rm_so);
        rpl = p++; // skip back reference, next match
        }
        strcat(new, p ); // trailing of rpl
        strcat(new, *str + m[0].rm_eo); // trainling text in *str
        free(*str);
        *str = strdup(new);
        free(new);
    }
    // ajust size
    *str = (char *)realloc(*str, strlen(*str) + 1);
    } else
    printf("Could not compile regex: %s\n", replace);
}

int main(int argc, char *argv[]) 
{
    char *pattern = "\\[([^-]+)->([^]]+)\\]";
    char *str = strdup("before [link->address] some text [link2->addr2] trail");
    char rpl[] = "<a href=\"\2\">\1</a>";
    puts(str);
    regex_replace(&str, pattern, rpl);
    puts(str);
    free(str);
}

【讨论】:

    【解决方案4】:

    我接受了@marnout 的帖子并修复了它,解决了一些错误和错别字。修复:内存泄漏、如果替换包含模式则无限替换、在函数中打印替换为返回值、返回参考值实际上最多 31、文档、更多测试示例。

    /* regex_replace.c
    :w | !gcc % -o .%<
    :w | !gcc % -o .%< && ./.%<
    :w | !gcc % -o .%< && valgrind -v ./.%<
    */
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <regex.h>
    
    int regex_replace(char **str, const char *pattern, const char *replace) {
        // replaces regex in pattern with replacement observing capture groups
        // *str MUST be free-able, i.e. obtained by strdup, malloc, ...
        // back references are indicated by char codes 1-31 and none of those chars can be used in the replacement string such as a tab.
        // will not search for matches within replaced text, this will begin searching for the next match after the end of prev match
        // returns:
        //   -1 if pattern cannot be compiled
        //   -2 if count of back references and capture groups don't match
        //   otherwise returns number of matches that were found and replaced
        //
        regex_t reg;
        unsigned int replacements = 0;
        // if regex can't commpile pattern, do nothing
        if(!regcomp(&reg, pattern, REG_EXTENDED)) {
            size_t nmatch = reg.re_nsub;
            regmatch_t m[nmatch + 1];
            const char *rpl, *p;
            // count back references in replace
            int br = 0;
            p = replace;
            while(1) {
                while(*++p > 31);
                if(*p) br++;
                else break;
            } // if br is not equal to nmatch, leave
            if(br != nmatch) {
                regfree(&reg);
                return -2;
            }
            // look for matches and replace
            char *new;
            char *search_start = *str;
            while(!regexec(&reg, search_start, nmatch + 1, m, REG_NOTBOL)) {
                // make enough room
                new = (char *)malloc(strlen(*str) + strlen(replace));
                if(!new) exit(EXIT_FAILURE);
                *new = '\0';
                strncat(new, *str, search_start - *str);
                p = rpl = replace;
                int c;
                strncat(new, search_start, m[0].rm_so); // test before pattern
                for(int k=0; k<nmatch; k++) {
                    while(*++p > 31); // skip printable char
                    c = *p;  // back reference (e.g. \1, \2, ...)
                    strncat(new, rpl, p - rpl); // add head of rpl
                    // concat match
                    strncat(new, search_start + m[c].rm_so, m[c].rm_eo - m[c].rm_so);
                    rpl = p++; // skip back reference, next match
                }
                strcat(new, p ); // trailing of rpl
                unsigned int new_start_offset = strlen(new);
                strcat(new, search_start + m[0].rm_eo); // trailing text in *str
                free(*str);
                *str = (char *)malloc(strlen(new)+1);
                strcpy(*str,new);
                search_start = *str + new_start_offset;
                free(new);
                replacements++;
            }
            regfree(&reg);
            // ajust size
            *str = (char *)realloc(*str, strlen(*str) + 1);
            return replacements;
        } else {
            return -1;
        }
    }
    
    const char test1[] = "before [link->address] some text [link2->addr2] trail[a->[b->c]]";
    const char *pattern1 = "\\[([^-]+)->([^]]+)\\]";
    const char replace1[] = "<a href=\"\2\">\1</a>";
    
    const char test2[] = "abcabcdefghijklmnopqurstuvwxyzabc";
    const char *pattern2 = "abc";
    const char replace2[] = "!abc";
    
    const char test3[] = "a1a1a1a2ba1";
    const char *pattern3 = "a";
    const char replace3[] = "aa";
    int main(int argc, char *argv[])
    {
        char *str1 = (char *)malloc(strlen(test1)+1);
        strcpy(str1,test1);
        puts(str1);
        printf("test 1 Before: [%s], ",str1);
        unsigned int repl_count1 = regex_replace(&str1, pattern1, replace1);
        printf("After replacing %d matches: [%s]\n",repl_count1,str1);
        free(str1);
    
        char *str2 = (char *)malloc(strlen(test2)+1);
        strcpy(str2,test2);
        puts(str2);
        printf("test 2 Before: [%s], ",str2);
        unsigned int repl_count2 = regex_replace(&str2, pattern2, replace2);
        printf("After replacing %d matches: [%s]\n",repl_count2,str2);
        free(str2);
    
        char *str3 = (char *)malloc(strlen(test3)+1);
        strcpy(str3,test3);
        puts(str3);
        printf("test 3 Before: [%s], ",str3);
        unsigned int repl_count3 = regex_replace(&str3, pattern3, replace3);
        printf("After replacing %d matches: [%s]\n",repl_count3,str3);
        free(str3);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-11
      • 1970-01-01
      • 2018-07-13
      • 2011-12-14
      • 2010-09-16
      • 2017-02-04
      相关资源
      最近更新 更多