Comment puis-je ouvrir un tableau de fichier en Perl?
En perl, j'ai lu dans les fichiers d'un répertoire, et je veux les ouvrir tous en même temps (mais ligne par ligne) afin que je puisse accomplir une fonction qui utilise la totalité de leur nième ensemble de lignes (par exemple, la concaténation).
my $text = `ls | grep ".txt"`;
my @temps = split(/\n/,$text);
my @files;
for my $i (0..$#temps) {
my $file;
open($file,"<",$temps[$i]);
push(@files,$file);
}
my $concat;
for my $i (0..$#files) {
my @blah = <$files[$i]>;
$concat.=$blah;
}
print $concat;
J'ai tout un tas d'erreurs, l'utilisation de la valeur non initialisée, et GLOB(..) des erreurs. Alors, comment puis-je faire ce travail?
Always
au - use strict; use warnings;
au début d'un programme en Perl. Le faire jusqu'à ce que vous savez exactement pourquoi vous devriez le faire."Le faire jusqu'à ce que vous savez exactement pourquoi vous devriez le faire." Et puis continuer à le faire, sauf si vous avez une raison particulière de ne pas savoir exactement ce qui va se passer à cause de cela.
OriginalL'auteur Jeremy | 2009-09-30
Vous devez vous connecter pour publier un commentaire.
Beaucoup de questions. En commençant par l'appel à "ls | grep" 🙂
Nous allons commencer avec un peu de code:
Tout d'abord, nous allons obtenir la liste des fichiers:
Mais il serait mieux de tester si le nom se rapporte à un fichier ou répertoire:
Maintenant, nous allons ouvrir ces fichiers pour les lire:
Mais, nous avons besoin d'une façon de gérer les erreurs, à mon avis, la meilleure façon est d'ajouter:
Au début de script (et l'installation de autodie, si vous ne l'avez pas encore). Alternativement, vous pouvez:
Maintenant, que nous avons, nous allons obtenir la première ligne (comme vous l'avez montré dans votre exemple) à partir de toutes les entrées, et les concaténer c':
Qui est parfaitement bien, et lisible, mais encore peut être raccourcie, tout en maintenant (à mon avis) la lisibilité, à:
Effet est le même - $concaténées contient premières lignes de tous les fichiers.
Donc, pour l'ensemble du programme devrait ressembler à ceci:
Maintenant, il pourrait être que vous voulez concaténer pas seulement les premières lignes, mais tous. Dans cette situation, au lieu de
$concatenated = ...
code, vous auriez besoin de quelque chose comme ceci:glob()
est considéré comme un peu dangereux de la fonction, et peut ne pas fonctionner de manière universelle. Je ne peux pas trouver une référence pour qui (vous pouvez rechercher StackOverflow et voir si vous pouvez trouver quelque chose à ce sujet - je me souviens d'un commentaire, mais vous ne savez pas où chercher à ce point).Hmm .. jamais entendu parler, mais c'est possible. Dans ce cas - fonctions opendir, readdir + grep, closedir devrait être suffisant.
Je pense que les plaintes au sujet de
glob
se référer aux anciennes versions de la fonction. (Il l'habitude d'utiliser le C-shell?) Néanmoins, voici un programmeur Perl qui ne l'aime pas et pourquoi: sial.org/blog/2008/01/many_small_errors.htmlglob, avec un argument de type chaîne, sur Perl >= 5.6, est tout à fait sûr. Le seul argument convaincant dans ce billet de blog est que les espaces peuvent causer des problèmes, mais
"*.txt"
est connu pour ne pas contenir d'espaces. 🙂Avez-vous essayé "readdir" au lieu de glob?
OriginalL'auteur
Ici est votre problème:
D'abord,
<$files[$i]>
n'est pas un descripteur de fichier valide lire. C'est la source de votre GLOB(...) des erreurs. Voir mobrule réponse pour lesquelles c'est le cas. Donc le changer à cela:Deuxième problème, Vous êtes de mélange
@blah
(un tableau nomméblah
) et$blah
(un scalaire nomméblah
). C'est la source de votre "valeur non initialisée" erreurs -$blah
(scalaires) n'a pas été initialisée, mais vous l'utilisez. Si vous voulez le$n
-ème ligne de@blah
, utilisez ceci:Je ne veux pas continuer de battre un cheval mort, mais je tiens à parler d'une meilleure façon de faire quelque chose:
Ce lit une liste de tous les fichiers du répertoire en cours qui ont un ".txt" extension en eux. Cela fonctionne et est efficace, mais il peut être assez lent, nous avons appeler le shell, qui a pour bifurquer à exécuter
ls
etgrep
, et qui engage un peu de surcharge. En outre,ls
etgrep
sont des programmes simples et courants, mais pas tout à fait portable. Il y a certainement une meilleure façon de le faire:Simple, bref, de la pure Perl, pas de fourches, de non-portable coques, et nous n'avons pas à lire dans la chaîne et puis split - il est possible de stocker uniquement les entrées, nous avons vraiment besoin. De Plus, il devient facile de modifier les conditions pour les fichiers qui passent le test. Dites-nous nous retrouver accidentellement de la lecture du fichier
test.txt.gz
parce que notre regex matches: on peut facilement changer cette ligne:Nous pouvons faire qu'un avec
grep
(je crois), mais pourquoi se contenter degrep
's limité d'expressions régulières lorsque Perl possède l'un des plus puissants de la regex n'importe où des bibliothèques intégrées?OriginalL'auteur Chris Lutz
Utiliser des accolades autour de
$files[$i]
à l'intérieur de la<>
opérateurSinon Perl interprète
<>
que le fichier glob opérateur au lieu de les lire à partir du descripteur de l'opérateur.<$files[$i]>
était mauvais. Mais ce n'est pas le seul problème dans cette boucle.OriginalL'auteur mob
Vous avez quelques bonnes réponses déjà. Une autre façon d'aborder le problème est de créer une liste de listes contenant toutes les lignes du fichiers (
@content
). Ensuite, utilisez laeach_arrayref
fonction de Liste::MoreUtils, ce qui permettra de créer un itérateur qui donne la ligne 1 à partir de tous les fichiers, puis ligne 2, etc.map { [<$_>] }
travail?Il pourrait, mais c'est un peu cryptique.
OriginalL'auteur FMc