Veuillez noter - Je ne cherche pas la "bonne" façon d'ouvrir/lire un fichier, ou la façon dont je dois ouvrir/lire un fichier à chaque fois. Je suis simplement intéressé de découvrir comment la plupart des gens utilisent, et peut-être d'apprendre quelques nouvelles méthodes en même temps:) *
Un bloc de code très courant dans mes programmes Perl consiste à ouvrir un fichier et à le lire ou à y écrire. J'ai vu tellement de façons de le faire, et mon style d'exécution de cette tâche a changé au fil des ans à quelques reprises. Je me demande simplement quelle est la méthode meilleure (s'il existe une meilleure façon) de faire cela?
J'avais l'habitude d'ouvrir un fichier comme celui-ci:
my $input_file = "/path/to/my/file";
open INPUT_FILE, "<$input_file" || die "Can't open $input_file: $!\n";
Mais je pense que cela a des problèmes avec le piégeage d'erreur.
L'ajout d'une parenthèse semble corriger l'erreur de recouvrement:
open (INPUT_FILE, "<$input_file") || die "Can't open $input_file: $!\n";
Je sais que vous pouvez également affecter un descripteur de fichier à une variable, donc au lieu d'utiliser "INPUT_FILE" comme je l'ai fait ci-dessus, j'aurais pu utiliser $ input_filehandle - est-ce mieux?
Pour lire un fichier, s'il est petit, y a-t-il un problème avec la globalisation, comme ça?
my @array = <INPUT_FILE>;
ou
my $file_contents = join( "\n", <INPUT_FILE> );
ou devez-vous toujours parcourir, comme ceci:
my @array;
while (<INPUT_FILE>) {
Push(@array, $_);
}
Je sais qu'il y a tellement de façons d'accomplir des choses en Perl, je me demande simplement s'il existe des méthodes préférées/standard d'ouverture et de lecture dans un fichier?
Il n'y a pas de normes universelles, mais il y a des raisons de préférer l'une ou l'autre. Ma forme préférée est la suivante:
open( my $input_fh, "<", $input_file ) || die "Can't open $input_file: $!";
Les raisons sont les suivantes:
Ce qui suit est idéal si le fichier est petit et que vous savez que vous voulez toutes les lignes:
my @lines = <$input_fh>;
Vous pouvez même le faire si vous avez besoin de traiter toutes les lignes en une seule chaîne:
my $text = join('', <$input_fh>);
Pour les fichiers longs, vous voudrez parcourir les lignes avec while ou utiliser la lecture.
Si vous souhaitez que le fichier entier soit une chaîne unique, il n'est pas nécessaire de le parcourir.
use strict;
use warnings;
use Carp;
use English qw( -no_match_vars );
my $data = q{};
{
local $RS = undef; # This makes it just read the whole thing,
my $fh;
croak "Can't open $input_file: $!\n" if not open $fh, '<', $input_file;
$data = <$fh>;
croak 'Some Error During Close :/ ' if not close $fh;
}
Ce qui précède satisfait perlcritic --brutal
, qui est un bon moyen de tester les "meilleures pratiques" :). $input_file
n'est toujours pas défini ici, mais le reste est casher.
Devoir écrire "ou mourir" partout me rend fou. Ma façon préférée d'ouvrir un fichier ressemble à ceci:
use autodie;
open(my $image_fh, '<', $filename);
Bien que ce soit très peu de frappe, il y a beaucoup de choses importantes à noter qui se passent:
Nous utilisons le pragma autodie , ce qui signifie que toutes les fonctionnalités intégrées de Perl lèveront une exception en cas de problème. Il élimine le besoin d'écrire or die ...
dans votre code, il génère des messages d'erreur conviviaux et lisibles par l'homme, et a une portée lexicale. Il est disponible auprès du CPAN.
Nous utilisons la version à trois arguments de open. Cela signifie que même si nous avons un nom de fichier amusant contenant des caractères tels que <
, >
ou |
, Perl fera toujours la bonne chose. Dans mon tutoriel sur la sécurité Perl à OSCON, j'ai montré un certain nombre de façons d'obtenir un mauvais comportement de open
à 2 arguments. Les notes de ce tutoriel sont disponibles pour téléchargement gratuit depuis Perl Training Australia .
Nous utilisons un descripteur de fichier scalaire. Cela signifie que nous n'allons pas fermer par coïncidence le descripteur de fichier de quelqu'un d'autre du même nom, ce qui peut se produire si nous utilisons des descripteurs de fichier de package. Cela signifie également que strict
peut repérer les fautes de frappe et que notre descripteur de fichier sera nettoyé automatiquement s'il sort de la portée.
Nous utilisons un descripteur de fichier significatif . Dans ce cas, il semble que nous allons écrire sur une image.
Le descripteur de fichier se termine par _fh
. Si nous nous voyons l'utiliser comme un scalaire normal, alors nous savons que c'est probablement une erreur.
Si vos fichiers sont suffisamment petits pour que la lecture du tout en mémoire soit possible, utilisez File :: Slurp . Il lit et écrit des fichiers complets avec une API très simple, en plus il vérifie toutes les erreurs pour que vous n'ayez pas à le faire.
Il n'y a pas de meilleur moyen d'ouvrir et de lire un fichier. Ce n'est pas la bonne question à poser. Qu'y a-t-il dans le dossier? De combien de données avez-vous besoin à tout moment? Avez-vous besoin de toutes les données à la fois? Que devez-vous faire avec les données? Vous devez les comprendre avant de réfléchir à la façon dont vous devez ouvrir et lire le fichier.
Est-ce que quelque chose que vous faites maintenant vous pose des problèmes? Sinon, n'avez-vous pas de meilleurs problèmes à résoudre? :)
La plupart de vos questions ne sont que de la syntaxe, et toutes les réponses figurent dans la documentation Perl (en particulier ( perlopentut ). Vous aimerez peut-être aussi prendre Learning Perl , qui répond à la plupart des les problèmes que vous avez dans votre question.
Bonne chance, :)
Pour OO, j'aime:
use FileHandle;
...
my $handle = FileHandle->new( "< $file_to_read" );
croak( "Could not open '$file_to_read'" ) unless $handle;
...
my $line1 = <$handle>;
my $line2 = $handle->getline;
my @lines = $handle->getlines;
$handle->close;
Il est vrai qu'il existe autant de meilleures façons d'ouvrir un fichier en Perl qu'il y en a
$files_in_the_known_universe * $Perl_programmers
... mais il est toujours intéressant de voir qui le fait habituellement de quelle façon. Ma forme préférée de slurping (lire tout le fichier à la fois) est:
use strict;
use warnings;
use IO::File;
my $file = shift @ARGV or die "what file?";
my $fh = IO::File->new( $file, '<' ) or die "$file: $!";
my $data = do { local $/; <$fh> };
$fh->close();
# If you didn't just run out of memory, you have:
printf "%d characters (possibly bytes)\n", length($data);
Et lorsque vous procédez ligne par ligne:
my $fh = IO::File->new( $file, '<' ) or die "$file: $!";
while ( my $line = <$fh> ) {
print "Better than cat: $line";
}
$fh->close();
Attention, bien sûr: ce ne sont que les approches que j'ai engagées pour la mémoire musculaire pour le travail quotidien, et elles peuvent être radicalement inadaptées au problème que vous essayez de résoudre.
J'ai déjà utilisé le
open (FILEIN, "<", $inputfile) or die "...";
my @FileContents = <FILEIN>;
close FILEIN;
passe-partout régulièrement. Aujourd'hui, j'utilise File::Slurp
pour les petits fichiers que je souhaite conserver complètement en mémoire, et Tie::File
pour les gros fichiers que je souhaite adresser de manière évolutive et/ou les fichiers que je souhaite modifier sur place.
Si ces programmes sont juste pour votre productivité, tout fonctionne! Intégrez autant de gestion des erreurs que vous pensez en avoir besoin.
Lire un fichier entier s'il est volumineux n'est peut-être pas la meilleure façon de faire les choses à long terme, vous pouvez donc vouloir traiter les lignes au fur et à mesure qu'elles arrivent plutôt que de les charger dans un tableau.
Une astuce que j'ai tirée de l'un des chapitres du programmeur pragmatique (Hunt & Thomas) est que vous souhaiterez peut-être que le script enregistre une sauvegarde du fichier pour vous avant qu'il ne commence à travailler.
Le ||
L'opérateur a une priorité plus élevée, il est donc évalué avant d'envoyer le résultat à "open" ... Dans le code que vous avez mentionné, utilisez plutôt l'opérateur "ou", et vous n'aurez pas ce problème.
open INPUT_FILE, "<$input_file"
or die "Can't open $input_file: $!\n";
Damian Conway le fait de cette façon:
$data = readline!open(!((*{!$_},$/)=\$_)) for "filename";
Mais je ne vous le recommande pas.