2

そのため、私の現在の宿題は C で DBMS のようなシステムを実装することであり、Linux POSIX 正規表現マッチング ライブラリを使用することにしました。私は何かを学ぶためにおもちゃのコードを書いています。これは、DBMS の「コマンド解析」部分のテストです。少し長文になりますが、ご容赦ください。

したがって、この例に基づく完全なプログラム コードは次のとおりです。

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

正規表現 101によると、正規表現パターンは問題ないように見えますが、完全な表現の最後に「 ! 」が紛れ込んでいることに注意してください。キャプチャ グループは正しく解析されますが、異常な文字は (これまでのところ) 完全な式の範囲で発生し、これまでに使用されたテスト ケースでは、正確に 24 バイトの長さの場合にのみ発生します。それはおそらく非常にばかげた間違いであり、申し訳ありません。

また、C で正規表現をより適切かつおそらくよりエレガントな方法で処理する方法についての提案は大歓迎です。よろしくお願いします。

編集

したがって、応答によると、それは の内部オフセット エラーでしたtoSubstring。現在は修正されており、出力はスムーズです。コメントで提案されているように、コードも少しクリーンアップしました。

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)

迅速な対応をありがとうございました。私はこれから多くのことを学びました。

4

1 に答える 1

3

toSubstring関数にオフバイワン エラーがあります。endは排他的境界であるため、部分文字列の長さは ですlen = end - start。終端の null 文字を格納するには、それよりも 1 つ多く割り当てる必要がありますが、len文字のみをコピーする必要があります。さらに重要なことに、終端の null を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)

于 2014-11-11T08:09:35.297 に答える