Espressioni regolari in C: esempi?


171

Sto cercando alcuni semplici esempi e le migliori pratiche su come utilizzare le espressioni regolari in ANSI C. man regex.hnon fornisce così tanto aiuto.


6
Non esiste alcun supporto integrato per regex in ANSI C. Quale libreria regex stai usando?
Joe,

7
Rob Pike ha scritto una piccola funzione di ricerca di stringhe di espressioni regolari che ha accettato un sottoinsieme molto utile di espressioni regolari per il libro The Practice of Programming che lui e Brian Kernighan hanno scritto insieme. Vedi questa discussione, A Regular Expression Matcher, del Dr. Kernighan cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
Richard Chambers

Risposte:


233

Le espressioni regolari in realtà non fanno parte di ANSI C. Sembra che tu stia parlando della libreria di espressioni regolari POSIX, che viene fornita con la maggior parte (tutti?) * Nix. Ecco un esempio dell'uso di regex POSIX in C (basato su questo ):

#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);

In alternativa, potresti voler controllare PCRE , una libreria per espressioni regolari compatibili con Perl in C. La sintassi Perl è praticamente la stessa sintassi usata in Java, Python e in un numero di altre lingue. La sintassi POSIX è la sintassi utilizzata da grep, sed, vi, etc.


7
A meno che non sia necessario evitare la dipendenza I secondo PCRE, ha alcuni miglioramenti della sintassi ed è molto stabile. Almeno con alcune versioni precedenti di Linux, la libreria di espressioni regolari "incorporata" non è troppo difficile da bloccare a causa di alcune stringhe di input e di alcune espressioni regolari che "quasi" corrispondono o coinvolgono molti caratteri speciali
bdk,

@Laurence Che significato ha passare 0 a regcomp? regcomp accetta solo quattro valori interi 1, 2, 4 e 8 per rappresentare 4 diverse modalità.
Lixiang,

2
@lixiang L'ultimo parametro regcomp, cflagsè una maschera di bit. Da pubs.opengroup.org/onlinepubs/009695399/functions/regcomp.html : "L'argomento cflags è l'OR inclusivo per bit di zero o più dei seguenti flag ...". Se OR-insieme zero, otterrai 0. Vedo che la manpage di Linux regcompdice "cflags può essere bit a bit o di uno o più dei seguenti", che sembra fuorviante.
Laurence Gonsalves,

2
Puoi estrarre il testo dai gruppi corrispondenti con qualcosa del genere: 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); }nota che le corrispondenze di gruppo iniziano da 1, il gruppo 0 è l'intera stringa. Aggiungi controlli errori per limiti non
consentiti

2
Per quanto riguarda se regfreeè necessario dopo un fallimento regcomp, anche se in realtà è piuttosto sotto-specificato, questo suggerisce che non dovrebbe essere fatto: redhat.com/archives/libvir-list/2013-September/msg00276.html
Daniel Jour

12

Probabilmente non è quello che vuoi, ma uno strumento come re2c può compilare espressioni regolari POSIX (-ish) in ANSI C. È scritto come sostituto di lex, ma questo approccio ti consente di sacrificare la flessibilità e la leggibilità per l'ultimo bit di velocità, se ne hai davvero bisogno.


9

man regex.hsegnala che non esiste alcuna voce manuale per regex.h, ma man 3 regex fornisce una pagina che spiega le funzioni POSIX per la corrispondenza dei modelli.
Le stesse funzioni sono descritte in The GNU C Library: Regular Expression Matching , che spiega che la libreria GNU C supporta sia l'interfaccia POSIX.2 sia l'interfaccia che la libreria GNU C ha avuto per molti anni.

Ad esempio, per un programma ipotetico che stampa quale delle stringhe passate come argomento corrisponde al modello passato come primo argomento, è possibile utilizzare un codice simile al seguente.

#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);
}

L'ultimo argomento regcomp()deve essere almeno REG_EXTENDED, o le funzioni useranno espressioni regolari di base , il che significa che (per esempio) dovresti usare a\{3\}invece di a{3}usare espressioni regolari estese , che è probabilmente quello che ti aspetti di usare.

POSIX.2 ha anche un'altra funzione per la corrispondenza jolly: fnmatch(). Non consente di compilare l'espressione regolare o di ottenere che le sottostringhe corrispondano a un'espressione secondaria, ma è molto specifica per verificare quando un nome file corrisponde a un carattere jolly (ad esempio, utilizza il FNM_PATHNAMEflag).


6

Questo è un esempio dell'uso di REG_EXTENDED. Questa espressione regolare

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

Ti permette di catturare numeri decimali nel sistema spagnolo e internazionale. :)

#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

Mentre la risposta sopra è buona, ti consiglio di usare PCRE2 . Questo significa che puoi letteralmente usare tutti gli esempi di regex là fuori ora e non devi tradurre da alcuni regex antichi.

Ho già risposto a questa domanda, ma penso che possa essere d'aiuto anche qui.

Regex in C per cercare i numeri di carta di credito

// 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;

}

Installa PCRE usando:

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

Compilare usando:

gcc foo.c -lpcre2-8 -o foo

Controlla la mia risposta per maggiori dettagli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.