Expressions regulars

Les expressions regulars són una part important del llenguatge Perl. És essencial conèixer-les i utilitzar-les quan es processa text. I això és en el que Perl és bo.

Breument, les expressions regulars són patrons especials que quan s'apliquen a les cadenes poden casar o no, capturar diverses subcadenes, modificar la subcadena inicial substituint-ne algunes parts, etc.

L'ús més habitual de les expressions regulars és trobar una cadena concreta dins d'una altra cadena.

my $cadena = 'Hola món!';
if ($cadena =~ m/Hola/) {
    say 'He trobat Hola!';
}

Aquí teniu un parell de coses noves: =~ i m// (m ve de l'anglès match). També existeix l'operador invers !~ que s'avalua a cert quan l'expressió regular no casa.

my $cadena = 'Hola món!';
if ($cadena !~ m/Adéu/) {
    say 'No he trobat Adéu';
}

Conjunts de caràcters

Els conjunts de caràcters són construccions d'expressions regulars que casen amb exactament un caràcter entre molts. Podem crear conjunts de caràcters amb els operadors []. Per exemple:

my $string = 'Adéu';
if ($string =~ m/[aeiou]/) {
    say 'He trobat una vocal';
}

En aquest cas, el condicional és veritat perquè un dels caràcters dins de la classe de caràcters s'ha trobat a la cadena.

També podem indicar un rang en comptes d'escriure tots els caràcters que volem fer casar:

my $string = 'hola';
if ($string =~ m/[a-g]/) {
    say 'He trobat una lletra entre la «a» i la «g» a la cadena';
}

En aquest exemple, hem indicat [a-g] en comptes de [abcdefg]. Passa el mateix amb números ([2-5]) i lletres majúscules ([A-Z]).

Quan s'utilitzen rangs cal recordar que sempre cal indicar el rang en ordre alfabètic o numèric. Per exemple, [8-3] no funcionarà com un rang (buscarà coincidir amb els caràcters 8, - i 3).

Exercici

Modifica aquesta expressió regular per detectar si hi ha cap 'x', 'y' o 'z' en la frase:

my $string = "Estem buscant les lletres x, y o z";
if ($string =~ ) {
    say "He trobat una x, y o z";
}

Altres conjunts de caràcters s'escriuen sense els []. En comptes d'això consisteixen en una barra invertida \ i una lletra indicant un conjunt de caràcters. Per exemple, per descobrir si una cadena conté com a mínim un dígit, podem utilitzar \d:

my $string = 'El març té 31 dies';
if ($string =~ m/\d/) {
    say 'He trobat un dígit!';
}

Amb \d estem dient que volem trobar digits. També podem utilitzar \w (de l'anlgès, word) per trobar qualsevol caràcter que s'utilitzi en paraules (lletres, números i el subratllat _):

my $string = 'Aquesta frase té 5 paraules';
if ($string =~ m/\w/) {
    say 'He trobat un caràcter de paraula';
}

I si volem saber si una cadena conté espais en blanc podem utilitzar \s:

my $string = 'Espai en blanc';
if ($string =~ m/\s/) {
    say 'Hi ha un espai en blanc a la cadena';
}

Els espais en blanc inclouen tabulacions, espais, salts de linia i retorns de carro.

Podem utilitzar la versió en majúscules d'un conjunt de caràcters per fer coincidir el contrari. Per exemple, amb \S casem qualsevol caràcter que no és un espai o una cadena buida:

my $string = '    ';
if ($string =~ m/\S/) {
    say 'Hi ha com a minim un caràcter que no és un espai en blanc';
}
else {
    say 'No hi ha caràcters de tipus espai en blanc a la cadena';
}

O amb \D fem coincidir qualsevol caràcter que no sigui un dígit:

my $string = '42';
if ($string =~ m/\D/) {
    say 'Hi ha com a minim un caràcter que no és un dígit';
}
else {
    say 'No hi ha caràcters que no siguin dígits a la cadena';
}

Finalment, si volem casar qualsevol caràcter, utilitzem el punt:

my $string = 'Hola, Món!';
if ($string =~ m/./) {
    say 'He trobat un caràcter';
}

L'única situació en la que un punt no coincideix amb alguna cosa és quan la cadena està buida o només conté un salt de linia.

Metacaràcters

Les expressions regulars poden ser realment sofisticades. Per exemple, volem comprovar si una cadena té els caràcters a o o:

my $cadena = 'Casa';
if ($cadena =~ m/a|o/) {
    say 'He trobat «a» o «o»';
}

En aquest cas utilitzem el metacaràcter | per indicar que volem fer coincidir la lletra a o la lletra b. Un altre metacaràcter és +, que ens ajuda a trobar més d'una ocurrència del mateix caràcter a la cadena:

my $string = 'Perl en català';
if ($string =~ m/a+/) {
    say 'He trobat com a mínim una «a»';
}

De forma similar, * s'utilitza per indicar que pot haver-hi 0 o més ocurrències del caràcter:

my $string = 'Hola, Món!';
if ($string =~ m/l*/) {
    say 'Pot haver una lletra «l» o més a la cadena';
}

Com que retornarà cert si tampoc hi ha cap coincidència, això també funcionarà:

my $string = 'Hola, Món!';
if ($string =~ m/j*/) {
    say 'Pot haver una lletra «j» o més a la cadena';
}

Finalment, amb ? indiquem si el caràcter es troba una o cap vegada:

my $string = 'Hola, Món!';
if ($string =~ m/j?/) {
    say 'Pot haver-hi o no una lletra «j» a la cadena';
}

Aquests metacaràcters són més utils en expressions regulars una mica més complexes. Per exemple, si volem saber si un usuari ha escrit el seu nom, podem assegurar-nos que com a mínim hi hagi una lletra escrita:

my $string = 'Larry';
if ($string =~ m/[a-z]+/) {
    say 'La cadena té com a mínim una lletra, pel que pot ser un nom'
}

Substitucions

Fins ara, hem utilitzat les expressions regulars per fer coincidir caràcters amb la m al principi de l'expressió. Però un altre ús molt habitual és la substitució amb s:

my $string = 'Hola, Món!';
$string =~ s/Hola/Adéu/;
say $string;

En aquest cas, primer indiquem que volem fer una substitució amb la s, després mostrem quina paraula o lletra volem canviar i finalment escrivim la paraula o lletra que l'ha de substituir.

Exercici

El lema de Perl és "Hi ha més d'una manera de fer-ho". En aquest exercici, canvia la frase per tal que imprimeixi el lema:

my $string = "Hi ha només una manera de fer-ho";
$string =~
say $string;

Modificadors

Els modificadors són lletres que s'escriuen al final de l'expressió regular i que influencien el resultat. Per exemple, podem utilitzar i per fer correspondències tant en majúscules com amb minúscules:

my $string = 'Hola, Món!';
if ($string =~ m/h/i) {
    say 'Hi ha una «h» o una «H» a la cadena';
}

Sense la i al final, no hi hauria coincidència, ja que només hauria buscat la h en minúscules. Un altre modificador molt comú és la g:

my $string = 'Perl en català!';
$string =~ s/a/A/g;
say $string;

Amb la g estem indicant a l'expressió regular que sigui cobdiciosa (de l'anglès, greedy) i que substitueixi totes les ocurrències de a per A. Si no indiquem la g, el resultat és Perl en cAtalà!, ja que només substitueix la primera coincidència que troba.

Els modificadors es poden utilitzar tant en les substitucions com en les coincidències.

Àncores

Les àncores són caràcters especials en expressions regulars que ens ajuden a fixar el que estem buscant al principi (^) o al final ($) de la cadena. Per exemple:

my $string = 'Hola, Món!';
if ($string =~ m/^H/) {
    say 'Hi ha una «H» al principi de la cadena';
}

Aixo és cert perquè estem buscant una H al principi. En canvi:

my $string = 'Hola, Món!';
if ($string =~ m/^o/) {
    say 'Hi ha una «o» al principi de la cadena';
} else {
    say 'No hi ha una «o» al principi de la cadena';
}

Fixeu-vos com no hi ha coincidència en aquest exemple perquè, tot i que hi ha una o a la cadena, no està al principi de tot. Passa el mateix quan ancorem l'expressió regular al final de la cadena:

my $string = 'Hola, Món!';
if ($string =~ m/!$/) {
    say 'Hi ha una «!» al final de la cadena';
}

En aquest cas hi ha coincidència perquè l'últim caràcter de la cadena és un símbol d'exclamació.

Exercici

En aquest exercici, crea una expressió regular de forma que la frase sigui certa, utilitzant conjunts de caràcters i àncores:

my $string = "Perl va néixer el 1987";
if ($string =~ ) {
    say "La frase acaba amb un any";
}
like($code, qr/\$\W/, "L'expressió hauria d'utilitzar una àncora");
like($stdout, qr/La frase acaba amb un any/, "La condició hauria de ser certa");