28 votes

Quelle est la différence entre itérer sur un fichier avec foreach ou en Perl?

J'ai un descripteur de fichier FILE en Perl, et je veux parcourir toutes les lignes du fichier. Y a-t-il une différence entre les éléments suivants?

 while (<FILE>) {
    # do something
}
 

et

 foreach (<FILE>) {
    # do something
}
 

34voto

Alex Reynolds Points 45039

Dans la plupart des cas, vous n'aurez probablement pas remarqué une différence. Toutefois, foreach lit chaque ligne dans une liste (un tableau pas) avant d'aller ligne par ligne, alors que l' while lit une ligne à la fois. En tant que foreach utilisera plus de mémoire et nécessitent un temps de traitement initial, il est généralement recommandé d'utiliser while pour itérer sur les lignes d'un fichier.

MODIFIER (via Schwern): L' foreach boucle est équivalent à ceci:

my @lines = <$fh>;
for my $line (@lines) {
    ...
}

Il est malheureux que Perl n'est pas optimiser ce cas spécial, comme il le fait avec l'opérateur d'intervalle (1..10).

Par exemple, si je lis le fichier /usr/share/dict/words avec un for boucle et un while boucle et avoir le sommeil quand ils ont terminé, je peux utiliser ps de mémoire pour voir le processus est long. Comme un contrôle, j'ai inclus un programme qui ouvre le fichier, mais ne fait rien avec elle.

USER       PID %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
schwern  73019   0.0  1.6   625552  33688 s000  S     2:47PM   0:00.24 perl -wle open my $fh, shift; for(<$fh>) { 1 } print "Done";  sleep 999 /usr/share/dict/words
schwern  73018   0.0  0.1   601096   1236 s000  S     2:46PM   0:00.09 perl -wle open my $fh, shift; while(<$fh>) { 1 } print "Done";  sleep 999 /usr/share/dict/words
schwern  73081   0.0  0.1   601096   1168 s000  S     2:55PM   0:00.00 perl -wle open my $fh, shift; print "Done";  sleep 999 /usr/share/dict/words

L' for programme consomme près de 32 mo de mémoire réelle (l' RSS de la colonne) pour stocker le contenu de mon 2.4 meg /usr/share/dict/words. L' while boucle stocke uniquement une ligne à un temps juste 70k pour la ligne de mise en mémoire tampon.

19voto

kmkaplan Points 10338

Dans un contexte scalaire (i.e. while) <FILE> retours chaque ligne à son tour.

Dans un contexte de liste (c'est à dire foreach) <FILE> renvoie une liste constituée de chaque ligne du fichier.

Vous devez utiliser l' while construire.

Voir perlop - I/O Opérateurs pour plus d'.

Edit: j_random_hacker dit à juste titre que

while (<FILE>) { … }

piétine $_ alors que foreach n'est pas (foreach localise $_ en premier). C'est sûrement la plus importante différence comportementale!

10voto

Ovid Points 7256

En plus des réponses précédentes, un autre avantage de l'utilisation de while est que vous pouvez utiliser la variable $. . Il s'agit du numéro de ligne actuel du dernier descripteur de fichier consulté (voir perldoc perlvar ).

 while ( my $line = <FILE> ) {
    if ( $line =~ /some_target/ ) {
        print "Found some_target at line $.\n";
    }
}
 

3voto

brian d foy Points 71781

J'ai ajouté un exemple traitant de cela à la prochaine édition de la programmation Perl efficace .

Avec un while , vous pouvez arrêter le traitement FILE et toujours obtenir les lignes non traitées:

  while( <FILE> ) {  # scalar context
      last if ...;
      }
 my $line = <FILE>; # still lines left
 

Si vous utilisez un foreach , vous consommez toutes les lignes dans le foreach même si vous arrêtez de les traiter:

  foreach( <FILE> ) { # list context
      last if ...;
      }
 my $line = <FILE>; # no lines left!
 

2voto

Ken Fox Points 1459

Mise à jour: j random hacker le souligne dans un commentaire que Perl cas particulier, la fausseté de test dans une boucle while lors de la lecture à partir d'un descripteur de fichier. J'ai juste vérifié que la lecture d'une valeur fausse de ne pas interrompre la boucle -- au moins sur moderne perls. Désolé pour la direction vous avez tout faux. Après 15 années passées à écrire en Perl, je suis encore un noob. ;)

Tout le monde ci-dessus est droite: utilisation de l' while boucle parce qu'elle sera plus efficace en terme de mémoire et de vous donner plus de contrôle.

Une chose drôle au sujet qu' while boucle est bien qu'il se ferme lorsque la lecture est faux. Généralement en fin de fichier, mais que si elle renvoie une chaîne vide ou un 0? Oups! Votre programme juste de sortir trop tôt. Cela peut se produire sur n'importe quel fichier de la poignée si la dernière ligne du fichier ne dispose pas d'un retour à la ligne. Il peut aussi arriver avec le fichier de personnalisation d'objets qui ont une méthode de lecture qui ne permet pas de traiter les retours à la ligne de la même manière régulière Perl objets de fichier.

Voici comment résoudre le problème. Vérifier la présence d'une valeur indéfinie de lire ce qui indique la fin du fichier:

while (defined(my $line = <FILE>)) {
    print $line;
}

L' foreach boucle n'ont pas ce problème par la voie, et c'est exact, même si inefficace.

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