Je dois mettre en œuvre un programme pour compter la survenue d'une sous-chaîne dans une chaîne de Perl. Je l'ai mis en œuvre comme suit maintenant c'est ce que je ferais normalement. Cependant, dans la mise en œuvre ci-dessus, je souhaite compter sur l'occurrence de «AA» dans «AAA». Ici, je reçois une réponse comme 1 qui semble raisonnable, mais j'ai besoin d'envisager les cas qui se chevauchent également. Par conséquent, le cas ci-dessus devrait donner une réponse comme 2 car il y a deux 'd'AA si nous envisageons de se chevaucher. p> Quelqu'un peut-il suggérer comment implémenter une telle fonction ?? p> p>
6 Réponses :
voir La réponse de YSTH ... Je n'ai pas réalisé que Le motif pourrait consister uniquement d'une affirmation de largeur zéro et de travailler encore à cet effet.
Vous pouvez utiliser Lookahead positif comme suggéré par d'autres, et écrivez la fonction comme: p> Vous pouvez également utiliser POS code>
Pour ajuster l'endroit où la recherche suivante prend à partir de: p>
C:\Temp> t
2
Oui, un peu de malade là-bas; vous attendriez naïvement quelque chose comme / \ B / G pour correspondre à la même position un nombre infini de fois, mais il existe une règle spéciale que les matchs de largeur zéro ne sont pas autorisés deux fois à la même position de départ (même sur scalaire séparé Contexte M // G).
Vous pouvez utiliser un Assertion Lookahead dans l'expression régulière:
sub countnmstr { my @matches = $_[0] =~ /(?=($_[1]))/g; return scalar @matches; }
Vous pouvez essayer cela, pas plus de regex que nécessaire.
$ ./perl.pl Total count: 4
Il n'y a pas besoin de substr code> ici;
Index CODE> prend un paramètre 3ème paramètre en option pour le dire où commencer la recherche.
Tout le monde se complique assez compliqué dans leurs réponses (d'oh! daoToad aurait dû faire son commentaire une réponse!), peut-être parce qu'ils ont peur de l'opérateur de goaSse. Je ne l'ai pas nommé, c'est ce que les gens l'appellent. Il utilise le truc que le résultat d'une affectation de liste est le nombre d'éléments de la liste des droitstres.
Le Perl Idiom pour compter les correspondances est alors: p>
my $count = () = 'aaa' =~ /((?<=a)a)/g;
Si vous ne savez pas pourquoi cela s'appelle «Opérateur Goatse», do pas i> Essayez de déterminer ce que cela signifie. Certaines choses que vous feriez mieux de ne pas savoir.
"Certaines choses ne peuvent pas être invisibles"
La peur vient du nom;). Je ne l'ai pas posté comme une réponse, parce que je ne pensais pas à l'idée de Lookahead / Lookbehind, qui est la vraie réponse. L'idée d'utiliser une «lookedehind» avec l'opérateur de goatiser rend une certaine sorte de sens horrible.
sub countnmstr { my ($string, $substr) = @_; return scalar( $string =~ s/(?=\Q$substr\E)//g ); }
Si la vitesse est une question, l'approche code> indice code> suggéré par GhostDog74 (avec l'amélioration de CJM) est susceptible d'être considérablement plus rapide que les solutions REGEX.
use strict; use warnings; sub countnmstr_regex { my ($haystack, $needle) = @_; return scalar( () = $haystack =~ /(?=\Q$needle\E)/g ); } sub countnmstr_index { my ($haystack, $needle) = @_; my $i = 0; my $tally = 0; while (1){ $i = index($haystack, $needle, $i); last if $i == -1; $tally ++; $i ++; } return $tally; } use Benchmark qw(cmpthese); my $size = 1; my $h = 'aaa aaaaaa' x $size; my $n = 'aa'; cmpthese( -2, { countnmstr_regex => sub { countnmstr_regex($h, $n) }, countnmstr_index => sub { countnmstr_index($h, $n) }, } ); __END__ # Benchmarks run on Windows. # Result using a small haystack ($size = 1). Rate countnmstr_regex countnmstr_index countnmstr_regex 93701/s -- -66% countnmstr_index 271893/s 190% -- # Result using a large haystack ($size = 100). Rate countnmstr_regex countnmstr_index countnmstr_regex 929/s -- -81% countnmstr_index 4960/s 434% --
Cette référence a un problème dans la mesure où il ne concerne qu'un exemple très spécifique où l'indice est sûr de gagner. Maintenant, essayez-le avec un motif non littéral: l'indice perd parce qu'il ne peut même pas fonctionner. Faites très attention aux points de repère spéciaux.
@brian sûr, index code> ne fonctionne que avec du texte littéral - je pense que nous savions déjà cela. L'OP n'est pas clair s'il doit rechercher un texte littéral ou un modèle. Étant donné que la plupart des réponses se sont concentrées sur REGEX, il semble raisonnable d'illustrer une approche alternative qui a également un avantage rapide.
@brian: l'OP n'a jamais dit quoi que ce soit sur la correspondance d'une regex; Vous avez dit que lorsque vous avez édité le titre. Il a simplement utilisé une regex dans sa mise en œuvre initiale.
Assez juste et j'ai élargi le titre. Cependant, "jamais dit quoi que ce soit" est un peu fort puisqu'il l'a utilisé lui-même. :)
Vous pouvez utiliser l'appelant "opérateur de goatse" (ce n'est pas vraiment un opérateur) pour obtenir un compte en une seule passe:
mon compte $ = () = $ string = ~ / $ substring / g; code>. Cela ne corrige pas votre détection de sous-chaînes qui se chevauchent. Je vais aller dans le prochain commentaire.
L'opérateur de goatiser ou aller fonctionne car lorsqu'il est évalué dans le contexte scalaire, l'affectation à une liste renvoie le nombre d'éléments fournis pour la mission. Considérez cet exemple:
$ foo = () = (1,3,2,9) '; code>, la liste des numéros est attribuée à la liste vide. Toutes les valeurs sont supprimées, mais l'affectation elle-même est évaluée pour obtenir la valeur pour
$ FOO code>. Étant donné que
$ FOO code> est un scalaire, nous évaluons l'affectation de la liste et obtenez
4 code>. Ainsi, Go peut être utilisé pour éliminer un tableau temporaire inutile.