0 votes

Besoin d'aide avec un quantificateur gourmand

Je fais une simple recherche et un remplacement en Perl, mais j'ai besoin d'aide. Il s'agit de lignes dans un fichier :

1001(seperator could be "anything")john-1001(seperator could be "anything")mark
1001(seperator could be "anything")mark-1001(seperator could be "anything")john

Je veux attribuer un nouveau nom d'utilisateur à John, comme 2001. Voici donc le résultat que je souhaite obtenir :

2001($1)john-1001-mark
1001-mark-2001($1)john

Ma regex fonctionne correctement lorsque John est en premier, mais lorsque Mark est en premier, elle est perturbée.

3voto

Michael Carman Points 21983

Il est pratiquement impossible de répondre à cette question sans avoir une idée de ce que peut être le séparateur - quels caractères, combien de caractères, etc. Un séparateur arbitraire non gourmand ressemblerait à ceci :

s/\b1001\b(?=.*?\bjohn\b)/2001/

Il remplace "1001" lorsqu'il est suivi de "john" tout en respectant le nombre minimum de caractères intermédiaires. .*? est la version non gourmande de .* . Cependant, les expressions rationnelles correspondent toujours à ce qui est possible, donc ceci correspondrait toujours à ce qui suit

1001-mark-1001-john

En d'autres termes, il ne s'agit pas seulement d'un problème d'avidité. Nous devons définir au moins l'une des trois choses suivantes :

  • Les caractères que le séparateur peut contenir.
  • Les caractères le séparateur ne peut pas contenir.
  • Le nombre de caractères du séparateur.

Si nous supposons que le séparateur ne peut pas contenir de caractères "word" (a-z, 0-9, et underscore), nous pouvons obtenir quelque chose de réalisable :

s/\b1001\b(?=\W+?\bjohn\b)/2001/

Les parties connues ("1001" et "john") sont bornées pour éviter qu'elles ne correspondent à d'autres chaînes avec ces sous-chaînes. (Merci à Chas d'avoir remarqué ce cas particulier).

3voto

Chas. Owens Points 40887

Essayez ceci :

#!/usr/bin/perl

use strict;
use warnings;

while (<DATA>) {
    s/\b1001-john\b/2001-john/;
    print;
}

__DATA__
1001-john-1001-mark
1001-mark-1001-john
11001-john
1001-johnny

En \b l'empêche de faire correspondre des éléments autres que "1001-john" . Voir la section "Assertions" de perldoc perlre pour plus d'informations.


Hmmm, il semble que vous ayez besoin d'une sexeger :

#!/usr/bin/perl

use strict;
use warnings;

while (<DATA>) {
    my $s = reverse;
    $s =~ s/\bnhoj(.*?)1001\b/nhoj${1}1002/;
    $s = reverse $s;
    print $s;
}

__DATA__
1001-john-1001-mark
1001-mark-1001-john
11001-john
1001-johnny

L'idée de base d'un sexeger est d'inverser la chaîne, d'utiliser une regex inversée, puis d'inverser le résultat. Le problème est que .*? vous donne la chaîne la plus courte à partir de la première correspondance, et non la chaîne la plus courte possible. Bien sûr, cela pose toujours un problème avec "1001-mark-2001-john" en tant que .*? correspondra à "-mark-2001-" . Il est probablement préférable de déterminer le format du fichier et de l'analyser plutôt que d'essayer d'utiliser une expression rationnelle.

0voto

Michael Myers Points 82361

Je suppose, d'après vos commentaires, que le séparateur n'est pas toujours un trait d'union et qu'il peut en fait être composé de plus d'un caractère.

Dans ce cas, essayez :

s/\d+([^\d]*)john/2001$1john/

Le séparateur entre "1001" et "john" restera ainsi intact lors du remplacement. Notez qu'aucun chiffre n'est autorisé dans le séparateur, donc cela fonctionnera même si "john" apparaît après "mark" (parce que "-mark-1001-" n'est pas un séparateur valide).

-1voto

動靜能量 Points 33008

Il peut s'agir de quelque chose comme

$s = '1001-mark-1001-john';
$s =~ s/(\d+)(-john)/2001$2/i;
print $s;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X