Saya mencari beberapa contoh sederhana dan praktik terbaik tentang cara menggunakan ekspresi reguler di ANSI C. man regex.htidak memberikan banyak bantuan.
Tidak ada dukungan bawaan untuk regex di ANSI C. Pustaka regex apa yang Anda gunakan?
Joe
7
Rob Pike menulis fungsi pencarian string ekspresi reguler kecil yang menerima subset yang sangat berguna dari ekspresi reguler untuk buku The Practice of Programming yang ia dan Brian Kernighan ikut tulis bersama. Lihat diskusi ini, Pencocokan Ekspresi Reguler, oleh Dr. Kernighan cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
Richard Chambers
Jawaban:
233
Ekspresi reguler sebenarnya bukan bagian dari ANSI C. Sepertinya Anda mungkin berbicara tentang pustaka ekspresi reguler POSIX, yang dilengkapi dengan sebagian besar (semua?) * Nixes. Berikut adalah contoh menggunakan regex POSIX di C (berdasarkan ini ):
#include<regex.h>regex_t regex;int reti;char msgbuf[100];/* Compile regular expression */
reti = regcomp(®ex,"^a[[:alnum:]]",0);if(reti){
fprintf(stderr,"Could not compile regex\n");
exit(1);}/* Execute regular expression */
reti = regexec(®ex,"abc",0, NULL,0);if(!reti){
puts("Match");}elseif(reti == REG_NOMATCH){
puts("No match");}else{
regerror(reti,®ex, msgbuf,sizeof(msgbuf));
fprintf(stderr,"Regex match failed: %s\n", msgbuf);
exit(1);}/* Free memory allocated to the pattern buffer by regcomp() */
regfree(®ex);
Atau, Anda mungkin ingin memeriksa PCRE , pustaka untuk ekspresi reguler yang kompatibel dengan Perl di C. Sintaks Perl cukup banyak dengan sintaks yang sama yang digunakan di Jawa, Python, dan sejumlah bahasa lainnya. Sintaks POSIX sintaks yang digunakan oleh grep, sed, vi, dll
Kecuali Anda perlu menghindari ketergantungan PCRE kedua, ia memiliki beberapa peningkatan sintaksis yang bagus dan sangat stabil. Setidaknya dengan beberapa versi Linux yang lebih lama, pustaka ekspresi reguler "built in" tidak terlalu sulit untuk crash karena diberikan string input tertentu dan ekspresi reguler tertentu yang "hampir" cocok atau melibatkan banyak karakter khusus
bdk
@Laurence Apa arti dari lulus 0 ke regcomp? regcomp hanya membutuhkan empat nilai integer 1, 2, 4 dan 8 untuk mewakili 4 mode yang berbeda.
lixiang
2
@lixiang Parameter terakhir untuk regcomp, cflags, adalah bitmask a. Dari pubs.opengroup.org/onlinepubs/009695399/functions/regcomp.html : "Argumen cflags adalah inklusif bitwise ATAU nol atau lebih dari bendera berikut ...". Jika Anda ATAU-bersama-sama nol, Anda akan mendapatkan 0. Saya melihat bahwa manual Linux untuk regcompmengatakan "cflags mungkin bitwise-atau dari satu atau lebih dari yang berikut", yang tampaknya menyesatkan.
Laurence Gonsalves
2
Anda dapat mengekstraksi teks dari grup yang cocok dengan sesuatu seperti: 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); }perhatikan bahwa kecocokan grup mulai dari 1, grup 0 adalah seluruh string. Tambahkan cek kesalahan di luar batas, dll.
Ini mungkin bukan yang Anda inginkan, tetapi alat seperti re2c dapat mengkompilasi POSIX (-ish) ekspresi reguler ke ANSI C. Ini ditulis sebagai pengganti lex, tetapi pendekatan ini memungkinkan Anda untuk mengorbankan fleksibilitas dan keterbacaan untuk sedikit kecepatan terakhir, jika kamu benar-benar membutuhkannya.
man regex.hmelaporkan tidak ada entri manual untuk regex.h, tetapi man 3 regex memberi Anda halaman yang menjelaskan fungsi POSIX untuk pencocokan pola.
Fungsi yang sama dijelaskan di Perpustakaan GNU C: Pencocokan Ekspresi Reguler , yang menjelaskan bahwa Perpustakaan GNU C mendukung antarmuka POSIX.2 dan antarmuka yang dimiliki Perpustakaan GNU C selama bertahun-tahun.
Misalnya, untuk program hipotetis yang mencetak string mana yang diteruskan sebagai argumen yang cocok dengan pola yang dilewati sebagai argumen pertama, Anda bisa menggunakan kode yang mirip dengan yang berikut.
#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 (®ex, 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 (®ex, argv[i],0, NULL,0);if(!result){
printf ("'%s' matches the regular expression\n", argv[i]);}elseif(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,®ex, NULL,0);
print_regerror (result, length,®ex);return EXIT_FAILURE;}}/* Free the memory allocated from regcomp(). */
regfree (®ex);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);}
Argumen terakhir regcomp()harus setidaknya REG_EXTENDED, atau fungsi akan menggunakan ekspresi reguler dasar , yang berarti bahwa (misalnya) Anda perlu menggunakan a\{3\}alih-alih a{3}digunakan dari ekspresi reguler yang diperluas , yang mungkin adalah apa yang Anda harapkan untuk digunakan.
POSIX.2 juga memiliki fungsi lain untuk pencocokan wildcard: fnmatch(). Itu tidak memungkinkan untuk mengkompilasi ekspresi reguler, atau mendapatkan substring yang cocok dengan sub-ekspresi, tetapi sangat spesifik untuk memeriksa kapan nama file cocok dengan wildcard (misalnya menggunakan FNM_PATHNAMEflag).
Meskipun jawaban di atas bagus, saya sarankan menggunakan PCRE2 . Ini berarti Anda benar-benar dapat menggunakan semua contoh regex di luar sana sekarang dan tidak harus menerjemahkan dari beberapa regex kuno.
Saya sudah membuat jawaban untuk ini, tapi saya pikir itu bisa membantu di sini juga ..
// 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(){boolDebug=true;boolFound=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);return1;}
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;returnFound;// 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. */returnFound;}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);return0;}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);returnFound;}
Instal PCRE menggunakan:
wget https://ftp.pcre.org/pub/pcre/pcre2-10.31.zip
make
sudo make install
sudo ldconfig
Jawaban:
Ekspresi reguler sebenarnya bukan bagian dari ANSI C. Sepertinya Anda mungkin berbicara tentang pustaka ekspresi reguler POSIX, yang dilengkapi dengan sebagian besar (semua?) * Nixes. Berikut adalah contoh menggunakan regex POSIX di C (berdasarkan ini ):
Atau, Anda mungkin ingin memeriksa PCRE , pustaka untuk ekspresi reguler yang kompatibel dengan Perl di C. Sintaks Perl cukup banyak dengan sintaks yang sama yang digunakan di Jawa, Python, dan sejumlah bahasa lainnya. Sintaks POSIX sintaks yang digunakan oleh
grep
,sed
,vi
, dllsumber
regcomp
,cflags
, adalah bitmask a. Dari pubs.opengroup.org/onlinepubs/009695399/functions/regcomp.html : "Argumen cflags adalah inklusif bitwise ATAU nol atau lebih dari bendera berikut ...". Jika Anda ATAU-bersama-sama nol, Anda akan mendapatkan 0. Saya melihat bahwa manual Linux untukregcomp
mengatakan "cflags mungkin bitwise-atau dari satu atau lebih dari yang berikut", yang tampaknya menyesatkan.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); }
perhatikan bahwa kecocokan grup mulai dari 1, grup 0 adalah seluruh string. Tambahkan cek kesalahan di luar batas, dll.regfree
perlu setelah gagalregcomp
, meskipun itu agak kurang spesifik, ini menyarankan bahwa itu tidak boleh dilakukan: redhat.com/archives/libvir-list/2013-September/msg00276.htmlIni mungkin bukan yang Anda inginkan, tetapi alat seperti re2c dapat mengkompilasi POSIX (-ish) ekspresi reguler ke ANSI C. Ini ditulis sebagai pengganti
lex
, tetapi pendekatan ini memungkinkan Anda untuk mengorbankan fleksibilitas dan keterbacaan untuk sedikit kecepatan terakhir, jika kamu benar-benar membutuhkannya.sumber
man regex.h
melaporkan tidak ada entri manual untuk regex.h, tetapiman 3 regex
memberi Anda halaman yang menjelaskan fungsi POSIX untuk pencocokan pola.Fungsi yang sama dijelaskan di Perpustakaan GNU C: Pencocokan Ekspresi Reguler , yang menjelaskan bahwa Perpustakaan GNU C mendukung antarmuka POSIX.2 dan antarmuka yang dimiliki Perpustakaan GNU C selama bertahun-tahun.
Misalnya, untuk program hipotetis yang mencetak string mana yang diteruskan sebagai argumen yang cocok dengan pola yang dilewati sebagai argumen pertama, Anda bisa menggunakan kode yang mirip dengan yang berikut.
Argumen terakhir
regcomp()
harus setidaknyaREG_EXTENDED
, atau fungsi akan menggunakan ekspresi reguler dasar , yang berarti bahwa (misalnya) Anda perlu menggunakana\{3\}
alih-aliha{3}
digunakan dari ekspresi reguler yang diperluas , yang mungkin adalah apa yang Anda harapkan untuk digunakan.POSIX.2 juga memiliki fungsi lain untuk pencocokan wildcard:
fnmatch()
. Itu tidak memungkinkan untuk mengkompilasi ekspresi reguler, atau mendapatkan substring yang cocok dengan sub-ekspresi, tetapi sangat spesifik untuk memeriksa kapan nama file cocok dengan wildcard (misalnya menggunakanFNM_PATHNAME
flag).sumber
Ini adalah contoh penggunaan REG_EXTENDED. Ekspresi reguler ini
Memungkinkan Anda untuk menangkap angka desimal dalam sistem Spanyol dan internasional. :)
sumber
Meskipun jawaban di atas bagus, saya sarankan menggunakan PCRE2 . Ini berarti Anda benar-benar dapat menggunakan semua contoh regex di luar sana sekarang dan tidak harus menerjemahkan dari beberapa regex kuno.
Saya sudah membuat jawaban untuk ini, tapi saya pikir itu bisa membantu di sini juga ..
Regex In C Untuk Mencari Nomor Kartu Kredit
Instal PCRE menggunakan:
Kompilasi menggunakan:
Periksa jawaban saya untuk lebih jelasnya.
sumber