Biểu thức chính quy trong C: ví dụ?


171

Tôi sau một số ví dụ đơn giản và thực hành tốt nhất về cách sử dụng các biểu thức chính quy trong ANSI C. man regex.hkhông cung cấp nhiều trợ giúp.


6
Không có hỗ trợ tích hợp nào cho regex trong ANSI C. Bạn đang sử dụng thư viện regex nào?
Joe

7
Rob Pike đã viết một hàm tìm kiếm chuỗi biểu thức chính quy nhỏ chấp nhận một tập hợp con rất hữu ích của các biểu thức chính quy cho cuốn sách Thực hành lập trình mà ông và Brian Kernighan đồng tác giả. Xem cuộc thảo luận này, Trình so khớp
Richard Chambers

Câu trả lời:


232

Các biểu thức chính quy thực sự không phải là một phần của ANSI C. Có vẻ như bạn có thể đang nói về thư viện biểu thức chính quy POSIX, đi kèm với hầu hết (tất cả?) * Nixes. Đây là một ví dụ về việc sử dụng biểu thức POSIX trong C (dựa trên điều này ):

#include <regex.h>        
regex_t regex;
int reti;
char msgbuf[100];

/* Compile regular expression */
reti = regcomp(&regex, "^a[[:alnum:]]", 0);
if (reti) {
    fprintf(stderr, "Could not compile regex\n");
    exit(1);
}

/* Execute regular expression */
reti = regexec(&regex, "abc", 0, NULL, 0);
if (!reti) {
    puts("Match");
}
else if (reti == REG_NOMATCH) {
    puts("No match");
}
else {
    regerror(reti, &regex, msgbuf, sizeof(msgbuf));
    fprintf(stderr, "Regex match failed: %s\n", msgbuf);
    exit(1);
}

/* Free memory allocated to the pattern buffer by regcomp() */
regfree(&regex);

Ngoài ra, bạn có thể muốn kiểm tra PCRE , thư viện cho các biểu thức chính quy tương thích Perl trong C. Cú pháp Perl khá giống với cú pháp được sử dụng trong Java, Python và một số ngôn ngữ khác. Cú pháp POSIX là cú pháp được sử dụng bởi grep, sed, vivv


7
Trừ khi bạn cần tránh sự phụ thuộc vào PCRE thứ hai, nó có một số cải tiến cú pháp hay và rất ổn định. Ít nhất là với một số phiên bản Linux cũ hơn, thư viện biểu thức chính quy "tích hợp" không quá khó để đưa ra một số chuỗi đầu vào nhất định và một số biểu thức chính quy phù hợp "gần như" khớp với nhiều ký tự đặc biệt
bdk

@Laurence Ý nghĩa của việc chuyển 0 sang regcomp là gì? regcomp chỉ lấy bốn giá trị nguyên 1, 2, 4 và 8 để biểu diễn 4 chế độ khác nhau.
lixiang

2
@lixiang Tham số cuối cùng regcomp, cflagslà một bitmask. Từ pubs.opengroup.org/onlinepub/009695399/fifts/regcomp.html : "Đối số cflags là bao gồm bitwise HOẶC của 0 hoặc nhiều cờ sau ...". Nếu bạn OR-zero zero, bạn sẽ nhận được 0. Tôi thấy rằng trang web Linux cho regcompbiết "cflags có thể là bitwise - hoặc của một hoặc nhiều điều sau đây", có vẻ sai lệch.
Laurence Gonsalves

2
Bạn có thể trích xuất văn bản từ các nhóm khớp với một cái gì đó như: regmatch_t matches[MAX_MATCHES]; if (regexec(&exp, sz, MAX_MATCHES, matches, 0) == 0) { memcpy(buff, sz + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); printf("group1: %s\n", buff); }lưu ý rằng các kết quả khớp nhóm bắt đầu từ 1, nhóm 0 là toàn bộ chuỗi. Thêm kiểm tra lỗi để vượt ra ngoài giới hạn, v.v.
BurnsBA 7/2/2016

2
Về việc regfreecó cần thiết sau khi thất bại hay không regcomp, mặc dù nó thực sự chưa được chỉ định rõ ràng, điều này cho thấy không nên thực hiện: redhat.com/archives/libvir-list/2013-September/msg00276.html
Daniel Jour

12

Đây có thể không phải là những gì bạn muốn, nhưng một công cụ như re2c có thể biên dịch các biểu thức chính quy POSIX (-ish) thành ANSI C. Nó được viết để thay thế lex, nhưng cách tiếp cận này cho phép bạn hy sinh tính linh hoạt và mức độ dễ đọc cho tốc độ cuối cùng, nếu bạn thực sự cần nó


9

man regex.hbáo cáo không có mục nhập thủ công cho regex.h, nhưng man 3 regex cung cấp cho bạn một trang giải thích các chức năng POSIX để khớp mẫu.
Các chức năng tương tự được mô tả trong Thư viện GNU C: Kết hợp biểu thức chính quy , giải thích rằng Thư viện GNU C hỗ trợ cả giao diện POSIX.2 và giao diện mà Thư viện GNU C đã có trong nhiều năm.

Ví dụ, đối với một chương trình giả thuyết in ra chuỗi nào được truyền dưới dạng đối số khớp với mẫu được truyền dưới dạng đối số đầu tiên, bạn có thể sử dụng mã tương tự như chuỗi sau.

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

void print_regerror (int errcode, size_t length, regex_t *compiled);

int
main (int argc, char *argv[])
{
  regex_t regex;
  int result;

  if (argc < 3)
    {
      // The number of passed arguments is lower than the number of
      // expected arguments.
      fputs ("Missing command line arguments\n", stderr);
      return EXIT_FAILURE;
    }

  result = regcomp (&regex, argv[1], REG_EXTENDED);
  if (result)
    {
      // Any value different from 0 means it was not possible to 
      // compile the regular expression, either for memory problems
      // or problems with the regular expression syntax.
      if (result == REG_ESPACE)
        fprintf (stderr, "%s\n", strerror(ENOMEM));
      else
        fputs ("Syntax error in the regular expression passed as first argument\n", stderr);
      return EXIT_FAILURE;               
    }
  for (int i = 2; i < argc; i++)
    {
      result = regexec (&regex, argv[i], 0, NULL, 0);
      if (!result)
        {
          printf ("'%s' matches the regular expression\n", argv[i]);
        }
      else if (result == REG_NOMATCH)
        {
          printf ("'%s' doesn't the regular expression\n", argv[i]);
        }
      else
        {
          // The function returned an error; print the string 
          // describing it.
          // Get the size of the buffer required for the error message.
          size_t length = regerror (result, &regex, NULL, 0);
          print_regerror (result, length, &regex);       
          return EXIT_FAILURE;
        }
    }

  /* Free the memory allocated from regcomp(). */
  regfree (&regex);
  return EXIT_SUCCESS;
}

void
print_regerror (int errcode, size_t length, regex_t *compiled)
{
  char buffer[length];
  (void) regerror (errcode, compiled, buffer, length);
  fprintf(stderr, "Regex match failed: %s\n", buffer);
}

Đối số cuối cùng của regcomp()nhu cầu ít nhất là REG_EXTENDED, hoặc các hàm sẽ sử dụng các biểu thức chính quy cơ bản , có nghĩa là (ví dụ) bạn sẽ cần sử dụng a\{3\}thay vì a{3}sử dụng từ các biểu thức chính quy mở rộng , có thể là những gì bạn muốn sử dụng.

POSIX.2 cũng có một chức năng khác để khớp ký tự đại diện : fnmatch(). Nó không cho phép biên dịch biểu thức chính quy hoặc lấy các chuỗi con khớp với biểu thức phụ, nhưng nó rất cụ thể để kiểm tra khi tên tệp khớp với ký tự đại diện (ví dụ: nó sử dụng FNM_PATHNAMEcờ).


6

Đây là một ví dụ về việc sử dụng REG_EXTENDED. Biểu thức chính quy này

"^(-)?([0-9]+)((,|.)([0-9]+))?\n$"

Cho phép bạn bắt số thập phân trong hệ thống Tây Ban Nha và quốc tế. :)

#include <regex.h>
#include <stdlib.h>
#include <stdio.h>
regex_t regex;
int reti;
char msgbuf[100];

int main(int argc, char const *argv[])
{
    while(1){
        fgets( msgbuf, 100, stdin );
        reti = regcomp(&regex, "^(-)?([0-9]+)((,|.)([0-9]+))?\n$", REG_EXTENDED);
        if (reti) {
            fprintf(stderr, "Could not compile regex\n");
            exit(1);
        }

        /* Execute regular expression */
        printf("%s\n", msgbuf);
        reti = regexec(&regex, msgbuf, 0, NULL, 0);
        if (!reti) {
            puts("Match");
        }
        else if (reti == REG_NOMATCH) {
            puts("No match");
        }
        else {
            regerror(reti, &regex, msgbuf, sizeof(msgbuf));
            fprintf(stderr, "Regex match failed: %s\n", msgbuf);
            exit(1);
        }

        /* Free memory allocated to the pattern buffer by regcomp() */
        regfree(&regex);
    }

}

5

Mặc dù câu trả lời ở trên là tốt, tôi khuyên bạn nên sử dụng PCRE2 . Điều này có nghĩa là bạn có thể sử dụng tất cả các ví dụ regex hiện có và không phải dịch từ một số regex cổ đại.

Tôi đã trả lời cho điều này rồi, nhưng tôi nghĩ nó cũng có thể giúp ở đây ..

Regex In C để tìm kiếm số thẻ tín dụng

// YOU MUST SPECIFY THE UNIT WIDTH BEFORE THE INCLUDE OF THE pcre.h

#define PCRE2_CODE_UNIT_WIDTH 8
#include <stdio.h>
#include <string.h>
#include <pcre2.h>
#include <stdbool.h>

int main(){

bool Debug = true;
bool Found = false;
pcre2_code *re;
PCRE2_SPTR pattern;
PCRE2_SPTR subject;
int errornumber;
int i;
int rc;
PCRE2_SIZE erroroffset;
PCRE2_SIZE *ovector;
size_t subject_length;
pcre2_match_data *match_data;


char * RegexStr = "(?:\\D|^)(5[1-5][0-9]{2}(?:\\ |\\-|)[0-9]{4}(?:\\ |\\-|)[0-9]{4}(?:\\ |\\-|)[0-9]{4})(?:\\D|$)";
char * source = "5111 2222 3333 4444";

pattern = (PCRE2_SPTR)RegexStr;// <<<<< This is where you pass your REGEX 
subject = (PCRE2_SPTR)source;// <<<<< This is where you pass your bufer that will be checked. 
subject_length = strlen((char *)subject);




  re = pcre2_compile(
  pattern,               /* the pattern */
  PCRE2_ZERO_TERMINATED, /* indicates pattern is zero-terminated */
  0,                     /* default options */
  &errornumber,          /* for error number */
  &erroroffset,          /* for error offset */
  NULL);                 /* use default compile context */

/* Compilation failed: print the error message and exit. */
if (re == NULL)
  {
  PCRE2_UCHAR buffer[256];
  pcre2_get_error_message(errornumber, buffer, sizeof(buffer));
  printf("PCRE2 compilation failed at offset %d: %s\n", (int)erroroffset,buffer);
  return 1;
  }


match_data = pcre2_match_data_create_from_pattern(re, NULL);

rc = pcre2_match(
  re,
  subject,              /* the subject string */
  subject_length,       /* the length of the subject */
  0,                    /* start at offset 0 in the subject */
  0,                    /* default options */
  match_data,           /* block for storing the result */
  NULL);

if (rc < 0)
  {
  switch(rc)
    {
    case PCRE2_ERROR_NOMATCH: //printf("No match\n"); //
    pcre2_match_data_free(match_data);
    pcre2_code_free(re);
    Found = 0;
    return Found;
    //  break;
    /*
    Handle other special cases if you like
    */
    default: printf("Matching error %d\n", rc); //break;
    }
  pcre2_match_data_free(match_data);   /* Release memory used for the match */
  pcre2_code_free(re);
  Found = 0;                /* data and the compiled pattern. */
  return Found;
  }


if (Debug){
ovector = pcre2_get_ovector_pointer(match_data);
printf("Match succeeded at offset %d\n", (int)ovector[0]);

if (rc == 0)
  printf("ovector was not big enough for all the captured substrings\n");


if (ovector[0] > ovector[1])
  {
  printf("\\K was used in an assertion to set the match start after its end.\n"
    "From end to start the match was: %.*s\n", (int)(ovector[0] - ovector[1]),
      (char *)(subject + ovector[1]));
  printf("Run abandoned\n");
  pcre2_match_data_free(match_data);
  pcre2_code_free(re);
  return 0;
}

for (i = 0; i < rc; i++)
  {
  PCRE2_SPTR substring_start = subject + ovector[2*i];
  size_t substring_length = ovector[2*i+1] - ovector[2*i];
  printf("%2d: %.*s\n", i, (int)substring_length, (char *)substring_start);
  }
}

else{
  if(rc > 0){
    Found = true;

    } 
} 
pcre2_match_data_free(match_data);
pcre2_code_free(re);
return Found;

}

Cài đặt PCRE bằng:

wget https://ftp.pcre.org/pub/pcre/pcre2-10.31.zip
make 
sudo make install 
sudo ldconfig

Biên dịch bằng cách sử dụng:

gcc foo.c -lpcre2-8 -o foo

Kiểm tra câu trả lời của tôi để biết thêm chi tiết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.