【问题标题】:Posix regex matching in C... Full match anomaly?C中的Posix正则表达式匹配......完全匹配异常?
【发布时间】:2015-01-07 17:15:28
【问题描述】:

所以我目前的作业是在 C 中实现一个类似 DBMS 的系统,我决定使用 linux POSIX 正则表达式匹配库。我正在编写玩具代码来学习东西,这是我对 DBMS 的“命令解析”部分的测试。文章有点长,请多多包涵。

所以,完整的程序代码,基于this example

char *toSubstring(const char *str, int start, int end)
{
  char *buffer = malloc((end - start + 1) * sizeof *buffer);
  memcpy(buffer, str + start, end - start);
  *(buffer + (end - start)) = '\0';

  return buffer;
}

int compile_regex(regex_t *r, const char *r_pattern)
{
  int status = regcomp(r, r_pattern, REG_EXTENDED|REG_NEWLINE);

  if (status != 0) {
    char err_msg[MAX_ERR_MSG];
    regerror(status, r, err_msg, MAX_ERR_MSG);
    printf("REGEX error compiling '%s': %s\n", r_pattern, err_msg);

    return status;
  }
  return 0;
}

int match_regex(regex_t *r, const char *r_text, char ***m_array, int n_matches)
{
  int i, nomatch;
  regmatch_t m_osets[n_matches];
  *m_array = malloc(n_matches * sizeof **m_array);

  nomatch = regexec(r, r_text, n_matches, m_osets, 0);

  if (nomatch) {
    printf("No matches\n");

    return nomatch;
  }

  for (i = 0; i < n_matches ; i++) {
    int start = (int) m_osets[i].rm_so;
    int stop = (int) m_osets[i].rm_eo;

    if (start==-1 || stop==-1) {
      *(*(m_array)+i) = NULL;

      printf("WARNING: Match block %d is void!\n", i);
    } else {
      *(*(m_array)+i) = toSubstring(r_text, start, stop);

      printf("Match block %d @bytes %d:%d\n", i, start, stop);
    }
  }
  return 0;
}

void chafree(char **c, int n)
{
  int i;

  for (i = 0; i < n; i++) {
    if (*(c+i)!=NULL)
      free(*(c+i));
  }
  free(c);
}

int main(int argc, char **argv)
{
  int i, m;
  regex_t r;
  const char * r_text = *(argv+1);
  const char * r_pattern = *(argv+2);

  char **matches = NULL;

  if (argc != 4) {
    printf("Usage: ./program_name \"r_text\" \"r_pattern\" n_matches\n");

    exit(1);
  }
  printf("Sweeping '%s' for '%s'\n", r_text, r_pattern);

  compile_regex(&r, r_pattern);
  match_regex(&r, r_text, &matches, atoi(*(argv+3)));

  if (matches != NULL) {
    for(i=0;i<atoi(*(argv+3));i++){

      if(*(matches+i)!=NULL)
        printf("$%d --> %s\n", i, *(matches+i));
      else printf("$%d --> %p\n", i, *(matches+i));
    }
    chafree(matches, atoi(*(argv+3)));
  }
  regfree(&r);

  return 0;

与我提供的示例略有不同的是,我将匹配项和捕获组存储在字符串向量中。

现在,当我运行程序时:

./regex_drills "insert whatever bananana" "([[:alpha:]]+)[[:blank:]]*([^\0]*)" 3

我收到的输出是:

Sweeping 'insert whatever bananana' for '([[:alpha:]]+)[[:blank:]]*([^\0]*)'
Match block 0 @bytes 0:23
Match block 1 @bytes 0:5
Match block 2 @bytes 7:23
$& --> insert whatever bananana!
$1 --> insert
$2 --> whatever bananana

根据regex 101,正则表达式模式似乎没问题,但请注意完整表达式末尾的杂散“”。尽管正确解析了捕获组,但异常字符(到目前为止)出现在整个表达式范围内,并且对于迄今为止使用的测试用例,仅当它恰好是 24 个字节长时。这可能是一个非常愚蠢的错误,我很抱歉。

此外,任何关于如何在 C 中以更好、或许更优雅的方式处理正则表达式的建议都将非常受欢迎。 在此先感谢

编辑

因此,根据回复,这是toSubstring 内部的偏移错误。它现在已修复,并且输出很流畅,因为它们应该如此。按照 cmets 中的建议,我还稍微清理了代码。

与之前发生的情况不同,使用 valgrind 进行的有点侵入性的运行没有发现错误或未定义的行为:

$ valgrind --leak-check=full --track-origins=yes --show-reachable=yes ./regex_drills "insert whatever bananana" "([[:alpha:]]+)[[:blank:]]*([^\0]*)" 3

==7051== Memcheck, a memory error detector
==7051== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7051== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==7051== Command: ./regex_drills insert\ whatever\ bananana ([[:alpha:]]+)[[:blank:]]*([^\\0]*) 3
==7051== 
Sweeping 'insert whatever bananana' for '([[:alpha:]]+)[[:blank:]]*([^\0]*)'
Match block 0 @bytes 0:24
Match block 1 @bytes 0:6
Match block 2 @bytes 7:24
$0 --> insert whatever bananana
$1 --> insert
$2 --> whatever bananana
==7051== 
==7051== HEAP SUMMARY:
==7051==     in use at exit: 0 bytes in 0 blocks
==7051==   total heap usage: 167 allocs, 167 frees, 18,458 bytes allocated
==7051== 
==7051== All heap blocks were freed -- no leaks are possible
==7051== 
==7051== For counts of detected and suppressed errors, rerun with: -v
==7051== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

感谢大家的快速回复。我从中学到了很多东西。

【问题讨论】:

  • 只是一个小问题:你为什么要使用指针算法来访问argv?通常为此使用普通的数组索引语法,例如r_text = argv[1]。与其他指针相同。
  • 请去掉C++标签,你的代码不是 C++,C++解决方案看起来非常不同,因为C和C++非常不同的语言。
  • 另外,in C you should not cast the result of mallocsizeof(char) 由 C 规范定义为始终为 1
  • 当你写超出分配内存的限制时,你的代码中有undefined behavior。在toSubstring 函数中,您分配end-start+1 字节,这意味着您可以在结果内存中访问的顶部索引是end-start,但您将字符串终止符写入位置end-start+1,它比末尾多一个字节分配的内存。
  • 至于不投malloc,请点击链接阅读答案。关于指针运算,它不由预处理器处理,使用数组索引通常也更容易阅读(并且更少编写)。

标签: c regex posix-ere


【解决方案1】:

您的 toSubstring 函数有一个差一错误。 end 是一个排他边界,所以子串的长度是len = end - start。您必须多分配一个来存储终止空字符,但您应该只复制len 字符,更重要的是,您应该将终止空字符写入buffer[len],而不是buffer[len + 1]

char *toSubstring(const char *str, int start, int end)
{
  char *buffer = malloc(end - start + 1);
  memcpy(buffer, str + start, end - start);
  *(buffer + (end - start)) = '\0';

  return buffer;
}

也许您不打算将end 排他性,因为您在调用toSubstring 时对其进行了调整。您可以保留这些语义并分配 end - start + 2 字符(并保留其余代码),但在 C 中,上限通常是独占的,因此我建议使用上述函数并像这样调用它:

*(*(m_array) + i) = toSubstring(r_text, start, stop);

而不是(..., stop - 1)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-27
    相关资源
    最近更新 更多