8

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

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

背景:p 和 p2 都是指向要匹配的字符串开头的指针,然后在下面的代码中进入 while 循环:[编辑:“matches”是一个 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);
4

2 回答 2

10

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

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

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

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

请注意,“偏移量”不是指针,而是字符数组的索引。

执行函数为:

  • 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));  // Based on question
    sprintf(matches[ind], "%.*s\n", (finish - start), p + start);   // More plausible code
    printf("Storing:  %.*s\n", (finish - start), matches[ind]);     // Print once
    ind++;
    printf("%.*s\n", (finish - start), p + start);                  // Why print twice?
}

请注意,应升级代码以确保字符串副本 (via sprintf()) 不会溢出目标字符串 — 可能通过使用snprintf()而不是sprintf(). 在打印中标记字符串的开始和结束也是一个好主意。例如:

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

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

[将来,请尝试提供 MCVE(最小、完整、可验证的示例)或 SSCCE(简短、自包含、正确的示例),以便人们可以更轻松地提供帮助。]

这是我创建的 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;
    }
    printf("Regex: %s\n", tofind);
    printf("Number of captured expressions: %zu\n", re.re_nsub);

    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[strcspn(line, "\n")] = '\0';
        if ((retval = regexec(&re, line, 2, rm, 0)) == 0)
        {
            printf("<<%s>>\n", line);
            // Complete match
            printf("Line: <<%.*s>>\n", (int)(rm[0].rm_eo - rm[0].rm_so), line + rm[0].rm_so);
            // Match captured in (...) - the \( and \) match literal parenthesis
            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(但您可以在命令行上指定替代文件名)。您可以轻松地将其调整为适合您的目的。

于 2013-03-06T04:05:29.630 回答
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;                         

}
于 2014-06-05T08:05:09.480 回答