Come posso risolvere il mio regex in modo che non corrisponda troppo a un avido quantificatore? [duplicare]
-
05-07-2019 - |
Domanda
Questa domanda ha già una risposta qui:
Ho la seguente riga:
"14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)"
Analizzo questo usando un semplice regexp:
if($line =~ /(\d+:\d+)\ssay;(.*);(.*);(.*);(.*)/) {
my($ts, $hash, $pid, $handle, $quote) = ($1, $2, $3, $4, $5);
}
Ma il; alla fine rovina tutto e non so perché. L'operatore avido non dovrebbe gestire "tutto"?
Soluzione
L'operatore avido cerca di afferrare quante più cose possibile e ancora abbinare la stringa. Quello che sta succedendo è il primo (dopo "dire") che afferra "0ed673079715c343281355c2a1fde843; 2", il secondo prende "laka", il terzo trova "ciao"; e il quarto corrisponde alla parentesi.
Quello che devi fare è rendere tutto tranne l'ultimo non avido, quindi afferrano il meno possibile e continuano a corrispondere alla stringa:
(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)
Altri suggerimenti
(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)
dovrebbe funzionare meglio
Anche se un regex può facilmente farlo, non sono sicuro che sia l'approccio più diretto. È probabilmente il più breve, ma ciò non lo rende effettivamente il più mantenibile.
Invece, suggerirei qualcosa del genere:
$x="14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)";
if (($ts,$rest) = $x =~ /(\d+:\d+)\s+(.*)/)
{
my($command,$hash,$pid,$handle,$quote) = split /;/, $rest, 5;
print join ",", map { "[ Anche se un regex può facilmente farlo, non sono sicuro che sia l'approccio più diretto. È probabilmente il più breve, ma ciò non lo rende effettivamente il più mantenibile.
Invece, suggerirei qualcosa del genere:
[14:48],[say],[0ed673079715c343281355c2a1fde843],[2],[laka],[hello ;)]
Ciò si traduce in:
<*>
Penso che questo sia solo un po 'più leggibile. Non solo, penso che sia anche più facile eseguire il debug e la manutenzione, perché è più vicino al modo in cui lo faresti se un umano tentasse la stessa cosa con carta e penna. Suddividi la stringa in blocchi che puoi analizzare più facilmente: chiedi al computer di fare esattamente quello che faresti. Quando arriva il momento di apportare modifiche, penso che questo andrà meglio. YMMV.
]" } $ts,$command,$hash,$pid,$handle,$quote
}
Ciò si traduce in:
<*>Penso che questo sia solo un po 'più leggibile. Non solo, penso che sia anche più facile eseguire il debug e la manutenzione, perché è più vicino al modo in cui lo faresti se un umano tentasse la stessa cosa con carta e penna. Suddividi la stringa in blocchi che puoi analizzare più facilmente: chiedi al computer di fare esattamente quello che faresti. Quando arriva il momento di apportare modifiche, penso che questo andrà meglio. YMMV.
Prova a rendere i primi 3 (. *)
ungreedy (.*?)
Se i valori nel tuo elenco delimitato da punti e virgola non possono includere alcun punto e virgola, otterrai l'espressione regolare più efficiente e semplice semplicemente spiegandola. Se alcuni valori possono essere, per esempio, solo una stringa di caratteri esadecimali, precisalo. Le soluzioni che usano un punto pigro o avido porteranno sempre a molti inutili backtracking quando il regex non corrisponde alla stringa del soggetto.
(\d+:\d+)\ssay;([a-f0-9]+);(\d+);(\w+);([^;\r\n]+)
Puoi rendere * non avido aggiungendo un punto interrogativo:
$line =~ /(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)/
oppure puoi abbinare tutto tranne un punto e virgola in ogni parte tranne l'ultimo:
$line =~ /(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)/