Espressioni regolari: matchare un soggetto che non contiene esattamente una determinata parola (o pattern)

Il titolo probabilmente non risulta molto chiaro, ecco una spiegazione più dettagliata: vogliamo che la nostra espressione regolare matchi un soggetto (o frase, testo o come lo volete chiamare) che non contenga esattamente una determinata parola (stringa) o un ben determinato pattern. Perciò:
• "" (stringa vuota) valida
p (e così via) valida
cane valida :)
parol valida
parolaccia valida
parola non valida

La soluzione a questa richiesta ovviamente non è offerta dalla negazione di una classe di caratteri:
[^parola]

O eventuali varianti come:
[^p][^a][^r][^o][^l][^a]


Dopo qualche scapocciante tentativo, l'espressione regolare che più si avvicina (forse) alla nostra necessità è qualcosa del tipo:
^(?!parola).*|parola.+$

Analisi:
• c'è la necessità di usare le ancore di inizio e fine riga, altrimenti i quantificatori ci fregano; ovviamente nulla ci obbliga ad avere il nulla prima e dopo la regex di interesse (intendo subito dopo ^ e subito prima $).
• possiamo vedere la regex come l'alternativa di due subregex: la prima subregex ci permette di matchare tutte le parole che non iniziano per parola ma esclude tutti i soggetti che iniziano con parola e che hanno altri caratteri a seguire (noi vogliamo che ad esempio parolaccia venga matchata), la seconda subregex corregge proprio quest'ultimo difetto.
• la prima subregex usa un lookahead negato (che non consuma il soggetto!).
• non si è tenuto conto di eventuali spazi (o newline e altri metacaratteri) prima e dopo la parola. Si potrebbe pensare (per gli spazi) a qualcosa del genere:
^(?!\s*parola\s*).*|[^\s]*parola[^\s]+$


Punti deboli? Alternative?


• 6 commenti • Inserisci un commento • Pubblicato il 27 agosto 2009 • Ultima modifica 30 agosto 2009 @ 4:18 •
1. bogomips_16443 - 30 agosto 2009 @ 3:02
>Punti deboli?

C'e' un po' di confusione. Qualche esempio sembra non c'entrare molto con le dichiarazioni di intenti. Vediamo se riesco a capire piu' o meno.

Con egrep dovrebbe funzionare questo:

"^(.* )?parol(a[[:alpha:]]+)?([^a].*)?$"

L'espressione dovrebbe essere una riga intera dove ci sia una parola che inizi con "parol". Dovrebbe rifiutare "parola" come parola.

Il seguente, invece, e' uno script di Perl:

#! /usr/bin/perl

"parola parol, parole, parolaccia, paroliere; straparoloni" =~ m{
parol(?!a[^[:alpha:]])[[:alpha:]]*
(?{ print "matched: $`<$&>$'\n"})
(?!)
}x;

{ print "\n"};

exit 0;

##################### FINE ##################

Non viene tenuto conto di inizio e fine riga, dato che il soggetto e' una stringa. Ma ti fa vedere tutti i riscontri che potrebbero essere validi (che normalmente non esegue). Inoltre dovrebbe avvicinarsi di piu' ad una soluzione per il PHP. In questo caso il riscontro e' positivo anche se "parol" non e' inizio di parola ("straparoloni"). I riscontri sono contenuti tra "<>".
2. ercoppa - 30 agosto 2009 @ 4:06
Ciao Bogomips :)

Come stai? Spero tutto bene.

>Qualche esempio sembra non c'entrare molto con le dichiarazioni di intenti.

Il primo e secondo esempio, come detto, sono sbagliati (li ho pescati in giro per il web da gente che tentava di ottenere una soluzione). Il terzo è la mia soluzione, il quarto invece è un abbozzo (si non è correttissimo in effetti) per espandere la portata del terzo esempio.

>C'e' un po' di confusione.

Non ho capito se con questa affermazioni intendi dire che la mia soluzione è sbagliata o meno.

> "^(.* )?parol(a[[:alpha:]]+)?([^a].*)?$"

Mi piace l'idea che c'è dietro a questa regex, però non è corretta: non matcha stringhe come "pa" o più semplicemente "cane" (si, hai ragione, non mi so spiegare, ora modifico un po' il post per chiarire un po' meglio la mia richiesta). Io voglio matchare una stringa che non contenga esattamente "parola", quindi "cane" deve essere accettata.

> Il seguente, invece, e' uno script di Perl:

Ok, qui ci devo un po' ragionare sopra perchè non conosco il Perl e quindi devo capire cosa fai.

Saluti.
3. bogomips_16443 - 30 agosto 2009 @ 17:38
>>C'e' un po' di confusione.
>Non ho capito se con questa affermazioni intendi dire che la mia soluzione è >sbagliata o meno.

No, intendo dire che proprio non capivo. Adesso, invece, sono fuso.

Un passo alla volta. Se l'obbiezione e' questa:

>non matcha stringhe come "pa"

e' una cosa.

Stiamo parlando di esempi e gli esempi non vogliono dare una soluzione definitiva di uno specifico caso. Servono, invece, a far capire come funziona la cosa. Questa obbiezione l'avevo anche ipotizzata, ma immaginavo che una cosa simile non fosse tanto complicata. Metti in un file di testo le righe seguenti:

-
pa1ol
par2l
paro3
parol4
-

E dai il comando:

egrep --color=always "pa(r(o(l)?)?)?" file.txt

Quuesta non e' la soluzione: e' solo un'illustrazione del meccanismo.

Se pero' poi mi dici quest'altra cosa:

>o più semplicemente "cane"

Allora vado letteralmente in crisi perche' quello che capisco e' che deve essere "considerato valido" ogni soggetto in cui "parola" (ad esempio) non compare. Ma "considerare valido" il soggetto non e' compito del meccanismo di riscontro. Il meccanismo verifica il ricontro e ne fornisce il risultato al programma. Cosa fare del soggetto, in base al risultato fornito dal meccanismo di riscontro, e' compito del programma, non del meccanismo.

Metti queste righe in un file:

pa1ol
par2l
paro3
parol4
cane

e dai il comando:

grep -v "pa" file.txt

L'opzione "-v" dice al programma di riscontrare e scartare le righe che contengono "pa". Il meccanismo cerchera' QUESTE righe, le fornira' al programma che le scartera' e tratterra' tutte le altre.

Dunque, quello che capisco a questo punto, sarebbe di riscontrare tutti i soggetti che contengono "parola" come parola effettiva (di conseguenza i soggetti che contengono "cane" o "parolaccia" sono esclusi), scartarli e considerare validi tutti gli altri soggetti. Se e' cosi' il problema e' sapere qual e' il comando opportuno del PHP per invertire la "validita' del soggetto" (e non del riscontro). Un simile comando esiste dappertutto (grep , sed, AWK ...) e deve esistere anche nel PHP.

Ma non puo' essere una cosa simile: deve esserci sotto qualcosa d'altro che continua a sfuggirmi.

.
4. ercoppa - 30 agosto 2009 @ 21:03
>Se e' cosi' il problema e' sapere qual e' il comando opportuno del PHP per invertire la "validita' del soggetto" (e non del riscontro). Un simile comando esiste dappertutto (grep , sed, AWK ...) e deve esistere anche nel PHP.

Se ho capito bene quello che dici: no non mi risulta che in PHP esiste una cosa del genere. Ho visto ad esempio dell'opzione di grep, leggendo in giro dicono che in PHP non c'è un qualcosa di analago.

Comunque tutto parte da una discussione letta altrove nel web in cui si dice "ho una espressione regolare, come posso negarla?". Così io sono partito da un caso base: ho una stringa, voglio un espressione regolare che non accetti esattamente (e solo quella) un soggetto con quella stringa (stringa vuota valida; soggetto, che contiene al suo interno la stringa, valido; praticamente qualsiasi soggetto che non è "esattamente" la stringa deve essere accettato). Ovviamente l'idea è quella di trovare qualcosa da cui partire per fare qualcosa di più potente.
5. ercoppa - 30 agosto 2009 @ 21:27
Mi correggo un strumento c'è (preg_grep [1] con la flag PREG_GREP_INVERT), ma non è quello che uno si aspetta. Quello che voglio dire è che non c'è una flag analaga (che io sappia) per le altre funzioni PHP-PCRE [2] (in particolare preg_match).

[1] http://us2.php.net/manual/en/function.preg-grep.php
[2] http://us2.php.net/manual/en/book.pcre.php
6. bogomips_16443 - 31 agosto 2009 @ 17:25
>praticamente qualsiasi soggetto che non è "esattamente" la stringa deve essere accettato

Cosi' e' molto chiaro ed e' anche molto chiaro cosa devi fare: negare un'espressione che riscontri la stringa come soggetto.

Inserisci un commento Info sui commenti

Nome:      Email:

Sito web / Pagina personale / Blog: