it-swarm.com.de

Wie kann ich in Perl eine ganze Datei in einen String einlesen?

Ich versuche, eine .html-Datei als eine große lange Zeichenfolge zu öffnen. Das ist was ich habe:

open(FILE, 'index.html') or die "Can't read file 'filename' [$!]\n";  
$document = <FILE>; 
close (FILE);  
print $document;

was in ... resultiert: 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN

Ich möchte jedoch, dass das Ergebnis so aussieht: 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

Auf diese Weise kann ich das gesamte Dokument einfacher durchsuchen.

108
goddamnyouryan

Hinzufügen:

 local $/;

vor dem Lesen aus dem Datei-Handle. Siehe Wie kann ich eine ganze Datei gleichzeitig einlesen?, oder 

perldoc perlvar und perldoc -f local .

Wenn Sie Ihr Skript auf dem Server ablegen können, können Sie übrigens alle gewünschten Module haben. Siehe Wie behalte ich mein eigenes Modul Bibliotheksverzeichnis? -/.

Außerdem können Sie mit Path :: Class :: FileSlurp und spew .

Pfad :: Tiny bietet noch mehr Komfortmethoden wie Slurp, Slurp_raw, Slurp_utf8 sowie deren spew Gegenstücke.

75
Sinan Ünür

Ich würde es so machen:

my $file = "index.html";
my $document = do {
    local $/ = undef;
    open my $fh, "<", $file
        or die "could not open $file: $!";
    <$fh>;
};

Beachten Sie die Verwendung der Version mit drei Argumenten von open. Es ist viel sicherer als die Versionen mit zwei (oder einem) Argumenten. Beachten Sie auch die Verwendung eines lexikalischen Dateihandles. Lexikalische Dateihandles sind aus vielen Gründen schöner als die alten Bareword-Varianten. Wir nutzen hier einen von ihnen: Sie schließen sich, wenn sie den Rahmen verlassen.

93
Chas. Owens

Mit Datei :: Slurp :

use File::Slurp;
my $text = read_file('index.html');

Ja, auch Sie können CPAN verwenden .

74
Quentin

Alle Beiträge sind etwas nicht idiomatisch. Die Sprache ist:

open my $fh, '<', $filename or die "error opening $filename: $!";
my $data = do { local $/; <$fh> };

Meistens ist es nicht notwendig, $/auf undef zu setzen.

48
jrockway

Von perlfaq5: Wie kann ich eine ganze Datei auf einmal einlesen? :


Sie können das File :: Slurp-Modul verwenden, um dies in einem Schritt durchzuführen.

use File::Slurp;

$all_of_it = read_file($filename); # entire file in scalar
@all_lines = read_file($filename); # one line per element

Der übliche Perl-Ansatz für die Verarbeitung aller Zeilen in einer Datei besteht darin, Zeile für Zeile zu machen:

open (INPUT, $file)     || die "can't open $file: $!";
while (<INPUT>) {
    chomp;
    # do something with $_
    }
close(INPUT)            || die "can't close $file: $!";

Dies ist enorm effizienter, als die gesamte Datei als Zeilenarray in den Speicher zu lesen und sie dann Element für Element zu verarbeiten, was oft - wenn nicht fast immer - der falsche Ansatz ist. Wann immer Sie jemanden sehen, der dies tut:

@lines = <INPUT>;

sie sollten lange und gründlich darüber nachdenken, warum Sie alles auf einmal laden müssen. Es ist einfach keine skalierbare Lösung. Möglicherweise macht es auch mehr Spaß, das Standardmodul Tie :: File oder die $ DB_RECNO-Bindungen des DB_File-Moduls zu verwenden, mit denen Sie ein Array an eine Datei binden können, sodass das Array auf ein Element zugreift, das auf die entsprechende Zeile in der Datei zugreift .

Sie können den gesamten Inhalt des Dateihandles in einen Skalar einlesen.

{
local(*INPUT, $/);
open (INPUT, $file)     || die "can't open $file: $!";
$var = <INPUT>;
}

Dadurch wird Ihr Datensatztrenner vorübergehend nicht mehr erkannt, und die Datei wird beim Beenden des Blocks automatisch geschlossen. Wenn die Datei bereits geöffnet ist, verwenden Sie einfach Folgendes:

$var = do { local $/; <INPUT> };

Für normale Dateien können Sie auch die Lesefunktion verwenden.

read( INPUT, $var, -s INPUT );

Das dritte Argument testet die Byte-Größe der Daten im INPUT-Dateihandle und liest diese Anzahl von Bytes in den Puffer $ var.

18
brian d foy

Setzen Sie entweder $/ auf undef (siehe Antwort von jrockway) oder verketten Sie alle Zeilen der Datei:

$content = join('', <$fh>);

Es wird empfohlen, Skalare für Dateihandles in jeder Perl-Version zu verwenden, die dies unterstützt.

7
kixx

Ein einfacher Weg ist:

while (<FILE>) { $document .= $_ }

Eine andere Möglichkeit besteht darin, das Eingabedatentrennzeichen "$ /" zu ändern. Sie können dies lokal in einem leeren Block tun, um zu verhindern, dass der globale Datensatztrenner geändert wird.

{
    open(F, "filename");
    local $/ = undef;
    $d = <F>;
}
7
user100177

Ein anderer möglicher Weg:

open my $fh, '<', "filename";
read $fh, my $string, -s $fh;
close $fh;
4
echo

Sie erhalten nur die erste Zeile des Diamantoperators <FILE>, da Sie sie im skalaren Kontext auswerten:

$document = <FILE>; 

Im Listen-/Array-Kontext gibt der Diamantoperator alle Zeilen der Datei zurück. 

@lines = <FILE>;
print @lines;
3
Nathan

Ich würde es auf die einfachste Weise tun, damit jeder verstehen kann, was passiert, auch wenn es intelligentere Wege gibt:

my $text = "";
while (my $line = <FILE>) {
    $text .= $line;
}
2
open f, "test.txt"
$file = join '', <f>

<f> - gibt ein Array von Zeilen aus unserer Datei zurück (wenn $/ den Standardwert "\n" hat), und join '' wird dieses Array in dieses Feld einfügen.

Dies ist eher ein Vorschlag, wieNICHTes zu tun. Ich hatte gerade eine schlechte Zeit, einen Fehler in einer ziemlich großen Perl-Anwendung zu finden. Die meisten Module hatten eigene Konfigurationsdateien. Um die Konfigurationsdateien als Ganzes zu lesen, fand ich diese einzelne Zeile von Perl irgendwo im Internet:

# Bad! Don't do that!
my $content = do{local(@ARGV,$/)=$filename;<>};

Das Zeilentrennzeichen wird wie zuvor beschrieben neu zugewiesen. Es weist aber auch die STDIN neu zu.

Dies hatte mindestens einen Nebeneffekt, der mich Stunden in Anspruch nehmen musste: Es schließt das implizite Dateihandle nicht ordnungsgemäß (da es nicht closeat all aufgerufen wird).

Zum Beispiel:

use strict;
use warnings;

my $filename = 'some-file.txt';

my $content = do{local(@ARGV,$/)=$filename;<>};
my $content2 = do{local(@ARGV,$/)=$filename;<>};
my $content3 = do{local(@ARGV,$/)=$filename;<>};

print "After reading a file 3 times redirecting to STDIN: $.\n";

open (FILE, "<", $filename) or die $!;

print "After opening a file using dedicated file handle: $.\n";

while (<FILE>) {
    print "read line: $.\n";
}

print "before close: $.\n";
close FILE;
print "after close: $.\n";

ergebnisse in:

After reading a file 3 times redirecting to STDIN: 3
After opening a file using dedicated file handle: 3
read line: 1
read line: 2
(...)
read line: 46
before close: 46
after close: 0

Das Merkwürdige ist, dass der Zeilenzähler $. für jede Datei um eins erhöht wird. Es wird nicht zurückgesetzt und enthält nicht die Anzahl der Zeilen. Sie wird beim Öffnen einer anderen Datei erst dann auf Null zurückgesetzt, wenn mindestens eine Zeile gelesen wird. In meinem Fall habe ich so etwas gemacht:

while($. < $skipLines) {<FILE>};

Aufgrund dieses Problems war die Bedingung falsch, da der Zeilenzähler nicht ordnungsgemäß zurückgesetzt wurde. Ich weiß nicht, ob es sich um einen Fehler oder einfach um einen falschen Code handelt ... Auch das Aufrufen von close; oder close STDIN; hilft nicht.

Ich habe diesen unlesbaren Code durch Öffnen, String-Verkettung und Schließen ersetzt. Die von Brad Gilbert veröffentlichte Lösung funktioniert jedoch auch, da sie stattdessen eine explizite Dateihandlung verwendet.

Die drei Zeilen am Anfang können ersetzt werden durch:

my $content = do{local $/; open(my $f1, '<', $filename) or die $!; my $tmp1 = <$f1>; close $f1 or die $!; $tmp1};
my $content2 = do{local $/; open(my $f2, '<', $filename) or die $!; my $tmp2 = <$f2>; close $f2 or die $!; $tmp2};
my $content3 = do{local $/; open(my $f3, '<', $filename) or die $!; my $tmp3 = <$f3>; close $f3 or die $!; $tmp3};

was den Datei-Handle richtig schließt.

2
jaw

Benutzen

 $/ = undef;

vor $document = <FILE>;. $/ ist der Eingabedatentrenner , der standardmäßig ein Zeilenumbruch ist. Durch die Neudefinition in undef sagen Sie, dass es kein Feldtrennzeichen gibt. Dies wird als "Slurp" -Modus bezeichnet.

Andere Lösungen wie undef $/ und local $/ (aber nicht my $/) deklarieren $/und erzeugen somit den gleichen Effekt.

1
Geremia

Ich weiß nicht, ob es eine gute Praxis ist, aber ich habe dies verwendet:

($a=<F>);
0
zawy

Sie können einfach eine Subroutine erstellen:

#Get File Contents
sub gfc
{
    open FC, @_[0];
    join '', <FC>;
}
0
Sheldon Juncker