2008-11-01 22 views
5

J'ai la ligne suivante:Comment puis-je corriger mon regex pour ne pas trop correspondre avec un quantificateur gourmand?

"14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)" 

Je parse cela en utilisant une expression rationnelle simple:

if($line =~ /(\d+:\d+)\ssay;(.*);(.*);(.*);(.*)/) { 
    my($ts, $hash, $pid, $handle, $quote) = ($1, $2, $3, $4, $5); 
} 

Mais; à la fin mess les choses et je ne sais pas pourquoi. L'opérateur gourmand ne devrait-il pas gérer "tout"?

Répondre

18

L'opérateur avide essaie de saisir autant de choses qu'il peut et correspondre encore la chaîne. Qu'est-ce qui se passe est le premier (après "dire") saisit "0ed673079715c343281355c2a1fde843; 2", le second prend "laka", le troisième trouve "bonjour" et le quatrième correspond à la parenthèse.

Ce que vous devez faire est de tous, mais le dernier non gourmand, donc ils saisissent aussi peu que possible et correspondent encore la chaîne:

(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*) 
+0

C'est génial! Pouvez-vous me dire rapidement la différence entre. *? og. * Merci! :) –

+1

La différence est que. *?s'arrête à la première instance de tout ce qui suit, tandis que. * s'arrête à la dernière instance de ce qui suit. – eyelidlessness

+0

Ah, super amis! Appréciez-le! :-) –

3

Essayez de faire les 3 premiers (.*) ungreedy (.*?)

7
(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*) 

devrait mieux fonctionner

+0

Je pense que vous avez un extra ([^;] *); Je pense que la dernière partie est un commentaire avec un smily "Bonjour;" " – Ady

+0

Ady: Droite: la dernière partie peut être aussi simple que (. *) Pour obtenir le reste de la ligne. Fixe – VonC

2

Vous pourriez faire * non gourmand en annexant une point d'interrogation:

$line =~ /(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)/ 

ou vous pouvez tout correspondre à l'exception d'un point-virgule dans chaque partie, sauf la dernière:

$line =~ /(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)/ 
7

Bien qu'une regex puisse facilement faire cela, je ne suis pas sûr que ce soit l'approche la plus directe. C'est probablement le plus court, mais cela ne le rend pas vraiment le plus facile à maintenir.

Au lieu de cela, je vous suggère quelque chose comme ceci:

$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 { "[$_]" } $ts,$command,$hash,$pid,$handle,$quote 
} 

Il en résulte:

[14:48],[say],[0ed673079715c343281355c2a1fde843],[2],[laka],[hello ;)] 

Je pense que cela est juste un peu plus lisible. Non seulement cela, je pense que c'est aussi plus facile à déboguer et à maintenir, parce que c'est plus proche de la façon dont vous le feriez si un humain essayait la même chose avec un stylo et du papier. Brisez la chaîne en morceaux que vous pouvez ensuite analyser plus facilement - demandez à l'ordinateur de faire exactement ce que vous feriez. Quand viendra le temps d'apporter des modifications, je pense que celui-ci ira mieux. YMMV.

3

Si les valeurs dans votre liste délimitée par des points-virgules ne peuvent pas inclure les points-virgules eux-mêmes, vous obtiendrez l'expression régulière la plus simple et efficace simplement en épelant cela. Si certaines valeurs ne peuvent être, disons, qu'une chaîne de caractères hexadécimaux, épelez cela. Les solutions utilisant un point paresseux ou gourmand conduiront toujours à beaucoup de retours en arrière inutiles lorsque la regex ne correspond pas à la chaîne du sujet.

(\d+:\d+)\ssay;([a-f0-9]+);(\d+);(\w+);([^;\r\n]+) 
+0

Jan, si vous voulez que quelque chose soit marqué comme code source, chaque ligne doit commencer par quatre espaces. Et bienvenue à SO. –