C正则表达式:提取实际匹配

时间:2013-03-06 03:31:39

标签: c regex char pattern-matching

我在C中使用正则表达式(使用“regex.h”库)。在设置regcomp(...)和regexec(...)的标准调用(和检查)之后,我只能设法打印与我编译的正则表达式匹配的实际子字符串。 根据手册页,使用regexec意味着将子串匹配存储在称为“regmatch_t”的结构中。结构只包含rm_so和rm_eo来引用我所理解的内存中匹配子字符串的字符地址,但我的问题是如何使用这些来抵消和两个指针来提取实际的子字符串并将其存储到一个数组(理想情况下是一个2D字符串数组)?

当您只是打印到标准输出时它会起作用,但是每当您尝试使用相同的设置但将其存储在字符串/字符数组中时,它会存储最初用于匹配表达式的整个字符串。 此外,打印声明中的“%。* s”是什么?我想这是一个正则表达式,可以正确地读取指向字符数组的指针。我只想将匹配的子串存储在一个集合中,这样我就可以在我的软件中的其他地方使用它们了。

背景:在下面的代码中输入while循环之前,p和p2都设置为指向要匹配的字符串开头的指针: [编辑:“匹配”是一个2D数组,意味着最终存储子串匹配,并在下面看到的主循环之前进行了预分配/初始化]

int ind = 0;
while(1){
    regExErr1 = regexec(&r, p, 10, m, 0);
    //printf("Did match regular expr, value %i\n", regExErr1);
    if( regExErr1 != 0 ){ 
        fprintf(stderr, "No more matches with the inherent regular expression!\n"); 
        break; 
    }   
    printf("What was found was: ");
    int i = 0;
    while(1){
        if(m[i].rm_so == -1){
            break;
        }
        int start = m[i].rm_so + (p - p2);
        int finish = m[i].rm_eo + (p - p2);
        strcpy(matches[ind], ("%.*s\n", (finish - start), p2 + start));
        printf("Storing:  %.*s", matches[ind]);
        ind++;
        printf("%.*s\n", (finish - start), p2 + start);
        i++;
    }
    p += m[0].rm_eo; // this will move the pointer p to the end of last matched pattern and on to the start of a new one
}
printf("We have in [0]:  %s\n", temp);

2 个答案:

答案 0 :(得分:7)

有很多正则表达式包,但你的似乎与POSIX中的那个匹配:regcomp()等。

它在<regex.h>中定义的两个结构是:

  • regex_t至少包含size_t re_nsub,带括号的子表达式的数量。

  • regmatch_t至少包含regoff_t rm_so,从字符串开始到子字符串开始的字节偏移量,以及regoff_t rm_eo,第一个字符的字符串开头的字节偏移量在子串结束后。

请注意,'offsets'不是指针,而是索引到字符数组中。

执行功能是:

  • int regexec(const regex_t *restrict preg, const char *restrict string, size_t nmatch, regmatch_t pmatch[restrict], int eflags);

您的打印代码应为:

for (int i = 0; i < r.re_nsub; i++)
{
    int start = m[i].rm_so;
    int finish = m[i].rm_eo;
    strcpy(matches[ind], ("%.*s\n", (finish - start), p + start));
    printf("Storing:  %.*s\n", (finish - start), matches[ind]);
    ind++;
    printf("%.*s\n", (finish - start), p + start);
}

请注意,应升级此代码以确保字符串副本不会溢出目标字符串。标记字符串的开头和结尾也是一个好主意,例如:

    printf("<<%.*s>>\n", (finish - start), p + start);

这使得整个堆更容易看到空格等。

[将来,请尝试提供SSCCE(Short, Self-Contained, Correct Example),以便人们可以更轻松地提供帮助。]

这是我创建的SSCCE,可能是为了回应2010年的另一个SO问题。这是我保留的一些程序之一,我称之为“小插曲”;显示某些功能本质的小程序(例如POSIX正则表达式,在本例中)。我觉得它们很适合作为记忆慢跑者。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <regex.h>

#define tofind    "^DAEMONS=\\(([^)]*)\\)[ \t]*$"

int main(int argc, char **argv)
{
    FILE *fp;
    char line[1024];
    int retval = 0;
    regex_t re;
    regmatch_t rm[2];
    //this file has this line "DAEMONS=(sysklogd network sshd !netfs !crond)"
    const char *filename = "/etc/rc.conf";

    if (argc > 1)
        filename = argv[1];

    if (regcomp(&re, tofind, REG_EXTENDED) != 0)
    {
        fprintf(stderr, "Failed to compile regex '%s'\n", tofind);
        return EXIT_FAILURE;
    }

    fp = fopen(filename, "r");
    if (fp == 0)
    {
        fprintf(stderr, "Failed to open file %s (%d: %s)\n", filename, errno, strerror(errno));
        return EXIT_FAILURE;
    }

    while ((fgets(line, 1024, fp)) != NULL)
    {
        line[strlen(line)-1] = '\0';
        if ((retval = regexec(&re, line, 2, rm, 0)) == 0)
        {
            printf("<<%s>>\n", line);
            printf("Line: <<%.*s>>\n", (int)(rm[0].rm_eo - rm[0].rm_so), line + rm[0].rm_so);
            printf("Text: <<%.*s>>\n", (int)(rm[1].rm_eo - rm[1].rm_so), line + rm[1].rm_so);
            char *src = line + rm[1].rm_so;
            char *end = line + rm[1].rm_eo;
            while (src < end)
            {
                size_t len = strcspn(src, " ");
                if (src + len > end)
                    len = end - src;
                printf("Name: <<%.*s>>\n", (int)len, src);
                src += len;
                src += strspn(src, " ");
            }
        }
    } 
    return EXIT_SUCCESS;
}

这旨在查找文件DAEMONS=中以/etc/rc.conf开头的特定行。你可以很容易地适应你的目的。

答案 1 :(得分:0)

由于g ++正则表达式被窃听,直到谁知道什么时候,你可以使用我的代码(许可证:AGPL,没有保证,你自己的风险,......)

/**
 * regexp (License: AGPL3 or higher)
 * @param re extended POSIX regular expression
 * @param nmatch maximum number of matches
 * @param str string to match
 * @return An array of char pointers. You have to free() the first element (string storage). the second element is the string matching the full regex, then come the submatches.
*/
char **regexp(char *re, int nmatch, char *str) {
  char **result;
  char *string;
  regex_t regex;
  regmatch_t *match;
  int i;

  match=malloc(nmatch*sizeof(*match));
  if (!result) {
    fprintf(stderr, "Out of memory !");
    return NULL;
  }

  if (regcomp(&regex, re, REG_EXTENDED)!=0) {
    fprintf(stderr, "Failed to compile regex '%s'\n", re);
    return NULL;
  }

  string=strdup(str);
  if (regexec(&regex,string,nmatch,match,0)) {
#ifdef DEBUG
    fprintf(stderr, "String '%s' does not match regex '%s'\n",str,re);
#endif
    free(string);
    return NULL;
  }

  result=malloc(sizeof(*result));
  if (!result) {
    fprintf(stderr, "Out of memory !");
    free(string);
    return NULL;
  }

  for (i=0; i<nmatch; ++i) {
    if (match[i].rm_so>=0) {
      string[match[i].rm_eo]=0;
      ((char**)result)[i]=string+match[i].rm_so;
#ifdef DEBUG
      printf("%s\n",string+match[i].rm_so);
#endif                                                                                                                                                                                                                                                   
    } else {                             
      ((char**)result)[i]="";            
    }
  }

  result[0]=string;                      

  return result;                         

}