Newsgroup: it.comp.software.newsreader
Titolo: regex con/senza ordinamento

Esiste una maniera per scrivere una regex che selezioni i testi che
contengono almeno due parole di un set predeterminato in un ordine
qualsiasi?
 
-- 
Lurkos
 

"Lurkos" [by XPN/1.2.6 (Street Spirit ; Linux)] on Wed, 5 Oct 2011 10:32:15

Dovresti postare un esempio, inoltre intendi proprio "selezionare", o
solo "trovare"?
-- 
    * b *
  *   y   *
*   Piero   *
#v+
-panem et circenses-
#v-
 


di getto, con pcre:
 
(questa|parola).*(?!\1)(questa|parola)
 
-- 
  np: no song


È quello che ho pensato anch'io, ma pensavo si potesse evitare di
ripetere due volte la stessa lista.
 
-- 
Lurkos
 

 solo "trovare"?
 
Intendo "trovare".
Nel caso specifico mi interessa un set di regole per filtrare lo spam su
comp.lang.fortran. Se provi a fare un giro anche con Google Groups ti
accorgi della situazione.
Pensavo di implementare un filtro sul subject e per evitare i falsi
positivi di avere almeno due parole incriminate prima di scartare un
articolo.
 
-- 
Lurkos
 

 ripetere due volte la stessa lista.
 
(?:(\b(?:una|lunga|lista|di|parole)\b).*(?!\1)){2}
 
-- 
I am fortune's fool!

 articolo.
 
Ti posto un esempio semplice-semplice.
Copia/incolla e salva come file .htm:
 
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type="text/javascript">
var strHtml = "Tanto va la gatta al lardo che ci lascia lo zampino.\nLa gatta ha lasciato lo zampino dopo essere andata al lardo!";
alert(strHtml);
alert(strHtml.replace(/.(gatta.|lardo.|zampino.)*/g,'$1'));
</script>
</head>
<body>
</body>
</html>
 
N.B. Il punto che segue la parola, serve a salvare quello che la segue.
L'ho aggiunto per separare le parole, se non ti serve puoi toglierlo.
 
-- 
    * b *
  *   y   *
*   Piero   *
#v+
Alzhaimer e... cammina.
#v-
 

 (?:(\b(?:una|lunga|lista|di|parole)\b).*(?!\1)){2}
 
Le due regexp che hai postato non potranno matchare niente.
Non esiste mettere un quantificatore all'inizio "?", questo deve seguire
quel che dovrebbe (o non) trovare.
 
"?" all'inizio si usa solo nella sintassi del lookahead e del lookbehind,
ma in un contesto ben preciso e sempre con altri caratteri, mai da solo,
poi abbinarlo, come hai fatto, ad una backreference "\1" non ha senso.
 
-- 
    * b *
  *   y   *
*   Piero   *
#v+
-deus ex machina-
#v- 
 

 alert(strHtml.replace(/.(gatta.|lardo.|zampino.)*/g,'$1'));
 
fa il match anche di una sola parola in lista, o sbaglio?


 quel che dovrebbe (o non) trovare.
 
le hai provate? funzionano, eccome. il `?:' ha la funzione di evitare di
catturare il contenuto della parentesi, economizzando gli sforzi del
motore da un lato, della mia testa nel contare la backreference da usare
nella lookahead dall'altro.


 (?:(\b(?:una|lunga|lista|di|parole)\b).*(?!\1)){2}
Uhm... interessante... devo capire come funziona esattamente.
-- 
Lurkos

 fa il match anche di una sola parola in lista, o sbaglio?
Non sbagli, matcha le parole che trova.
-- 
    * b *
  *   y   *
*   Piero   *
#v+
-errare humanum est-
#v-
 

 [...]



Si le ho provate, ma i browser davano errore.
Forse sono io che non ho saputo utilizzarle, per� potresti postare un
esempio delle due regexp, magari da fare un copia/incolla per vederle
funzionare con un bell'alert.
In tal caso (anche se la regexp richiesta dall'OP � molto pi� semplice
di quella che hai scritta) non mancher� di ringraziarti per avermi fatto
conoscere qualcosa di nuovo.
-- 
    * b *
  *   y   *
*   Piero   *
#v+
-beati monocoli in terra caecorum-
#v-
 

 Non sbagli, matcha le parole che trova.
 
non mi pare sia ciò che serviva a Lurkos.


 nuovo.
 
hth


 di `?:' e `\d' ho già scritto.
              ^
              b


 qualsiasi ordine.
_Firefox_
Errore: syntax error
File sorgente: Riga: 11, Colonna: 21
Codice sorgente:
alert(strHtml.match((?:(\b(?:zampino|gatta|lardo)\b).*(?!$1)){2}));
---------------------^
_Opera_
Inline script compilation
Syntax error at line 7 while loading:
lert(strHtml.match((?:(\b(?:zampino|gatt
--------------------^
expected expression, got '?'
_IE_
Messaggio: Errore di sintassi
Linea: 11
Carattere: 22
Codice: 0
    ..._skipped!_
Adesso non ho il tempo di esaminare quel che segue.
-- 
    * b *
  *   y   *
*   Piero   *
#v+
-audaces fortuna iuvat-
#v- 

 non mi pare sia ci� che serviva a Lurkos.
Ho pensato che gli bastasse trovare le parole che voleva cercare, non ho
pensato al fatto che ne voleva _almeno due_.
Si hai ragione, non avevo pensato a quello, appena ho un po' di tempo ci
provo, sapendo gi� che dovr� "complicare" la regexp.
-- 
    * b *
  *   y   *
*   Piero   *
#v+
Da noi le perle del pensiero sono prodotte dalle cozze.
#v- 

 ci provo, sapendo gi� che dovr� "complicare" la regexp.
Eccola, molto semplice, basta un copia/incolla per vederla funzionare:
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type="text/javascript">
var strHtml_0 = "tre parole, ma ne matcha due\nTanto va la gatta al lardo che ci lascia lo zampino.";
alert(strHtml_0);
alert(strHtml_0.replace(/.((gatta|lardo|zampino).*(gatta|lardo|zampino))*/g,'$2 $3'));
var strHtml_1 = "due parole, e due matchate\nTanto va la gatta al lardo che ci lascia la zampina.";
alert(strHtml_1);
alert(strHtml_1.replace(/.((gatta|lardo|zampino).*(gatta|lardo|zampino))*/g,'$2 $3'));
var strHtml_2 = "una parola non viene matchata\nTanto va il gatto al lardo che ci lascia le zampine.";
alert(strHtml_2);
alert(strHtml_2.replace(/.((gatta|lardo|zampino).*(gatta|lardo|zampino))*/g,'$2 $3'));
</script>
</head>
<body>
</body>
</html>
-- 
    * b *
  *   y   *
*   Piero   *
#v+
-in medio stat virtus-
#v-
 

 hth
-- 
    * b *
  *   y   *
*   Piero   *
#v+
-carpe diem-
#v-
 

 Ok, vedila e provala copiandola dal post più sopra.
 
visto. non è quello che chiedeva lui: se nella stringa di test ripeti una
parola in lista hai il match.


 Adesso non ho il tempo di esaminare quel che segue.
 
ti consiglio 
http://www.regextester.com/
 per renderti facili le cose; un
paio di esempi:
 
La regex funzionante, versione "compatta" e greedy:
http://img522.imageshack.us/img522/9739/regextexter01.jpg
 
 
La regex funzionante, versione "compatta" e non greedy:
http://img404.imageshack.us/img404/511/regextexter02.jpg
 
 
La regex funzionante, versione estesa e non greedy:
http://img843.imageshack.us/img843/8426/regextexter03.jpg
 
 
Come si nota, solo quest'ultima dà il primo match intermedio, che nella
forma compatta va perduto per l'uso della stessa backreference in sequenza
(ma il match della prima parola avviene comunque, le due forme non greedy
danno lo stesso match finale (0))


 `----
leggo ora che per funzionare correttamente, anche con js la backreference
deve essere `\1'. il segno del dollaro serve solo per fare riferimento a
ciò che si è catturato, al di fuori della regex.


 alert(strHtml_0.replace(/.((gatta|lardo|zampino).*(gatta|lardo|zampino))*/g,'$2 $3'));
il punto iniziale è superfluo; l'asterisco finale va tolto, non va. ai
fini dell'uso che la regex può avere qui, non dovrebbe servire fare il
test col flag "g".
anche aggiustando così l'espressione, si fa il match anche di una sola
parola in lista, se ripetuta nella stringa analizzata
(es. `(b3st|v14gr4|c14l1s)' filtra un post dal subject "V14gr4, v14gr4
delle mie brame", che invece potrebbe essere divertente); si fa il match
di parole che contengono le stringhe in lista (es. "gattabuia" e
"Gallardo"); la lista è ripetuta (immagino che per leggibilità e facile
manutenzione se ne voglia avere solo una).


 facile manutenzione se ne voglia avere solo una).
 
Se trova una sola parola inserita nella lista, ripetuta due volte, queste
vengono matchate entrambe.
È quello che deve fare la regexp, due ne vuoi e due ne trova.
-- 
    * b *
  *   y   *
*   Piero   *
#v+


 forme non greedy danno lo stesso match finale (0))
 
Io non posso sapere come usi le regexp, io le uso in javascript, e per
testarle, la maniera più rapida e sicura è un bell'alert.
Quando me ne farai vedere uno funzionante, come hai predicato in tutto il
thread, sono pronto a riparlarne.
-- 
    * b *
  *   y   *
*   Piero   *
#v+
-errare humanum est-
#v-
 

 C'è abbastanza confusione nella regexp, visto il risultato.
 
nella regexp?
 
penso di avere offerto degli spunti interessanti; finora tu li hai presi
in considerazione superficialmente, travisati o ignorati. mi auguro solo
che chi inciamperà in questo thread non ne esca confuso


 Prova a toglierli e vedi cosa succede.
 
succede che almeno non dà falsi positivi, pur non ottenendo quello che
a mio modo di vedere è il risultato voluto.
 
ti mostro che la tua regexp filtrerebbe ogni subject che non contiene
alcun termine in lista, tanto per dirne una, se hai pazienza|tempo|voglia
di capire come lavora un motore:
 
Welcome to the Emacs shell
 
~ $ pcretest
PCRE version 8.12 2011-01-15
 
  re> /.((gatta|lardo|zampino).*(gatta|lardo|zampino))*/C
data> niente felini lipidi diminutivi
--->niente felini lipidi diminutivi
 +0 ^                                   .
 +1 ^^                                  ((gatta|lardo|zampino).*(gatta|lardo|zampino))*
 +2 ^^                                  (gatta|lardo|zampino)
 +3 ^^                                  g
 +9 ^^                                  l
+15 ^^                                  z
+48 ^^                                  
 0: n
data> 
  re> interruzione
~ $ exit
 
quel +48, se conti le posizioni della regexp partendo dallo 0, è
l'asterisco. anche se togliessi il punto iniziale si avrebbe il match (non
più della "n" ma di una stringa vuota, alla fine ci sarebbe "0: "). questo
perché l'asterisco fa match se ci sono 0 o più occorrenze di ciò che lo
precede; prende tutto insomma, anche il niente catturato dalle parentesi
esterne


 Welcome to the Emacs shell
Sorry non ti seguo, mi � totalmente sconosciuta.
    ..._skipped!_
So che ci sono applicazioni in cui le regexp danno diversi risultati, o
che hanno linguaggi proprietari. Per farti un esempio:
quella che uso in Fidolook nell'header The-Day-After � fatta da ben 4627
caratteri, una faticaccia, e pensa che calcola il giorno dopo, solo fino
al 2020, perch� usa espressioni proprietarie, mentre con quelle normali
sono riuscito a validare tutte le date dall'inizio del calendario
Gregoriano (15/10/1582) fino al 31/12/2999, con soli 343 caratteri.
-- 
    * b *
  *   y   *
*   Piero   *
#v+


 auguro solo che chi inciamper� in questo thread non ne esca confuso
Per me � stato utile, ho scoperto che si possono usare i word boundary
anche in quel tipo di stringa.
Sai che a volte non esiste una sola regexp per fare quanto richiesto, ci
sono altre alternative a seconda della strategia prevista, che pu�
essere pi� o meno complicata.
Questo esempio (che ho corretto seguendo la tua strada) fa il match
delle parole in forma negativa, cio� le toglie lasciando il resto,
dimostrando che matcha quelle parole, anche se non so se serva a Lurkos:
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type="text/javascript">
var strHtml_0 = "Questa (lookahead positiva) elimina le parole in lista\nTanto va la gattara al lardo che ci lascia lo zampino.";
alert(strHtml_0);
alert(strHtml_0.replace(/(.*)\b(gatta|lardo|zampino)\b(?=.)\1/g," "));
var strHtml_1 = "Questa (lookahead negativa) elimina sempre le parole in lista\nTanto va la gatta al lardone che ci lascia lo zampino.";
alert(strHtml_1);
alert(strHtml_1.replace(/(.*)\b(gatta|lardo|zampino)\b(?!\x7f)\1/g," "));
</script>
</head>
<body>
</body>
</html>
-- 
    * b *
  *   y   *
*   Piero   *
#v+
-ad audiendum verbum-
#v-

 alert(strHtml_0.replace(/.(\b(gatta|lardo|zampino)\b.*\b(gatta|lardo|zampino)\b)*/g,'$2 $3'));
ripeto, non funziona. dato che hai saltato la spiega, riformulo.
qui tenti di riportare ciò che catturano le parentesi, invece quello che
conta è l'overall match, cioè una corrispondenza generale tra ciò che la
regexp prevede e la stringa analizzata.
se tu prendi:
         .(\b(gatta|lardo|zampino)\b.*\b(gatta|lardo|zampino)\b)*
e la usi per filtrare delle righe non vuote, passeranno tutte a causa del
punto iniziale e/o dell'asterisco finale. la cosa può essere buona col
metodo `replace' di javascript, dato che senza overall match non ti
verrebbero restituiti neppure `$2' ed `$3', ma rende la regexp inutile se
usata acriticamente nei filtri di un reader o di un server.


 D'accordo, io ancora non ci sono riuscito, ma neanche tu.
ho provato a spiegarti, ma tu ti sforzi pochino pochino. se rileggi il
thread trovi delle regexp funzionanti con gli avvisi javascript (se usati
bene), col comodo sito che ho indicato e con pcretest. una l'hai provata
anche tu, riportando il match con il risultato giusto, che non hai saputo
interpretare.
invece di continuare a scrivere che qualcosa non ha senso o non funziona,
salvo poi doverti ricredere, ti suggerisco di studiare un po', partendo
dalle basi.
riporto qua il concetto che risolve il problema, in una forma spero più
intellegibile:
              (\b(?:uno|due|tre)\b).*(?!\1)\b(uno|due|tre)\b
la versione "compatta" ed ottimizzata è questa:
                  (?:(\b(?:uno|due|tre)\b).*?(?!\1)){2}
non ho la pretesa di aver avuto un'idea geniale: resto aperto a critiche
circostanziate e ad alternative


 alert(strHtml_0.replace(/.(\b(gatta|lardo|zampino)\b.*\b(gatta|lardo|zampino)\b)*/g,'$2 $3'));
ripeto, non funziona. dato che hai saltato la spiega, riformulo.
qui tenti di riportare ciò che catturano le parentesi, invece quello che
conta è l'overall match, cioè una corrispondenza generale tra ciò che la
regexp prevede e la stringa analizzata.
se tu prendi:
         .(\b(gatta|lardo|zampino)\b.*\b(gatta|lardo|zampino)\b)*
e la usi per filtrare delle righe non vuote, passeranno tutte il test a
causa del punto iniziale e/o dell'asterisco finale, quindi le bloccheresti
tutte. questo comportamento può essere utile col metodo `replace' di
javascript, dato che senza overall match non ti verrebbero restituiti
neppure `$2' ed `$3', ma rende la regexp "dannosa" se usata acriticamente
nei filtri di un reader o di un server.
-- 


 server.
 
Io non posso sapere come ed in quale applicazione viene usata, la regexp
così formulata fa quello che ho già scritto sopra.
-- 
    * b *
  *   y   *
*   Piero   *
#v+
-rebus sic stantibus-
#v-
 
 

 critiche circostanziate e ad alternative
 
E ti credo, ne funzionasse almeno una!
Va be', ora veramente riprenderò il thread quando vedrò una regexp
funzionare almeno come la mia.
-- 
    * b *
  *   y   *
*   Piero   *
#v+


 Uhm... interessante... devo capire come funziona esattamente.
 
altra soluzione, meno generale ma più elegante:
 
(\b(una|lunga|lista|di|parole)\b).*(?!\1)(?1)
 
es. "qui hai due parole della lista nera"
 
va avanti fino a trovare la parola "parole", poi con `.*' salta a fine
riga e inizia a tornare indietro controllando che non sia presente
"parole" contenuta tra due word boundary tramite la lookahead e poi che ci
sia almeno una parola della lista, per mezzo di `(?1)' - chiamata
ricorsiva al subpattern attivato dal match della parola
"parole". chiaramente, se subito prima ha controllato che "parole" non è
presente, verifica la presenza di tutte le altre parole in lista, trovando
la parola "lista" e facendo match (l'overall è quindi "parole della
lista"), dato che il motore è eager.
 
la regex non è per tutti, perché `(?n)' è stata introdotta col perl 5.10
(a cui si sono allineate le librerie pcre v. 4.00 se non sbaglio). quindi
xnews (sicuramente la stabile) o hamster pg non possono usarla. non penso
ci sia problema per python recenti o altri software che si appoggiano a
librerie di sistema aggiornate
