Dateiende in Array einlesen

Dateiende in Array einlesen

am 11.08.2006 13:57:15 von TorstenR

Hallo,

ich möchte die letzten 20 Zeilen einer ziemlich langen Textdatei (bis zu
300.000 Zeilen) möglichst effizient zeilenweise in ein Array einlesen.

Normalerweise wäre mein Ansatz:

open(Datei);
@array = ;
close(Datei);
usw.

Das scheint hier aber wenig Sinn zu machen, insbesondere da das Script
extrem oft (einige tausend mal täglich) auf verschiedene Dateien
angewendet wird.

Was ist hier der effizienteste Weg, um an die letzten paar Zeilen zu kommen?

Viele Grüße,
Torsten

Re: Dateiende in Array einlesen

am 11.08.2006 14:45:02 von Ingo Menger

TorstenR wrote:
> Hallo,
>
> ich möchte die letzten 20 Zeilen einer ziemlich langen Textdatei (bis zu
> 300.000 Zeilen) möglichst effizient zeilenweise in ein Array einlesen.
>
> Normalerweise wäre mein Ansatz:
>
> open(Datei);
> @array =3D ;
> close(Datei);
> usw.
>
> Das scheint hier aber wenig Sinn zu machen, insbesondere da das Script
> extrem oft (einige tausend mal täglich) auf verschiedene Dateien
> angewendet wird.
>
> Was ist hier der effizienteste Weg, um an die letzten paar Zeilen zu komm=
en?

Wahrscheinlich kommst Du dem Optimum recht nahe, wenn Du statt
$filename folgendes öffnest: "tail -20 $filename |"
Es soll aber auch CPAN-Module geben, die sich mit sowas beschäftigen.
Wenn Du es selbst programmierst, kannst Du so vorgehen:
Starte mit b =3D l * m,
wobei l die Anzahl der Zeilen ist, die du lesen möchtest, m die
erwartete maximale Zeilenlänge.
Sei s die Größe des Files in Byte.
Wenn s-b <=3D 0, dann lies das ganze File ein
sonst while true:
Positioniere das File b Zeichen vor dem Ende.
Lies den Rest des Files in das Array
Sind es weniger als l Zeilen? Setze b =3D 1.5 b und next.
last

Gib die letzten l Zeilen des Arrays aus.

Re: Dateiende in Array einlesen

am 11.08.2006 16:55:20 von Joerg Plate

perldoc File::ReadBackwards

Vermutlich hinreichend schnell.

Re: Dateiende in Array einlesen

am 12.08.2006 07:28:14 von Maluku

TorstenR schrieb:
> ich möchte die letzten 20 Zeilen einer ziemlich langen Textdatei (bis zu
> 300.000 Zeilen) möglichst effizient zeilenweise in ein Array einlesen.

perl -e 'print "$_\n" foreach (0 .. 200)' > filename
perl -e 'use Tie::File; tie @array, "Tie::File", "filename";@inhalt =
@array[-20 .. -1]; print @inhalt,"\n"'
181182183184185186187188189190191192193194195196197198199200

voila


--
$_='';s%%`^.*`s;.*;uhtnmo;;a>lha~a>inu~a>fmk~a>rou~a>duM~a>b tl~s;&&&&&&;
!d1!l2!b3!i4!f5!r6q(?);e;Z``}a>&&&`sub# "1#{#"_=shift#;s^"2^"3#^;``;~`
return #"_#}``^!&&`"1(#""2)#\.`Z%x;s~Z~print~g;s/#/\\/g;
s/`(.)(.+?)`(.+?)`/s$1$2$1$3$1g\;/gsx;s;&;(.);g;y^"^$^;print ;

Re: Dateiende in Array einlesen

am 12.08.2006 23:01:52 von hjp-usenet2

On Sat, 12 Aug 2006 07:28:14 +0200, Marc Lucksch wrote:

> TorstenR schrieb:
>> ich möchte die letzten 20 Zeilen einer ziemlich langen Textdatei (bis zu
>> 300.000 Zeilen) möglichst effizient zeilenweise in ein Array einlesen.
^^^^^^^^^^^^^^^^^^^

> perl -e 'print "$_\n" foreach (0 .. 200)' > filename
> perl -e 'use Tie::File; tie @array, "Tie::File", "filename";@inhalt =
> @array[-20 .. -1]; print @inhalt,"\n"'

Von "möglichst effizient" kann dabei aber keine Rede sein.

hp

--
_ | Peter J. Holzer | > Wieso sollte man etwas erfinden was nicht
|_|_) | Sysadmin WSR | > ist?
| | | hjp@hjp.at | Was sonst wäre der Sinn des Erfindens?
__/ | http://www.hjp.at/ | -- P. Einstein u. V. Gringmuth in desd

Re: Dateiende in Array einlesen

am 16.08.2006 15:44:51 von Ferry Bolhar

Torsten R:

> Was ist hier der effizienteste Weg, um an die letzten paar Zeilen zu
kommen?

@array = `tail -20 $datei`;

Das ist wesentlich schneller, als wenn du die Datei selber öffnest,
Satz für Satz einliest oder - noch schlimmer! - alles auf einmal in
ein Array einliest und dann nur die letzten 20 Elemente davon
verwendest.

Perl ist gut, aber manchmal sind die guten alten UNIX-Tools doch
um einiges schneller.

LG, Ferry

--
Ing. Ferry Bolhar
Municipality of Vienna, Department 14
A-1010 Vienna / AUSTRIA
E-mail: bol@adv.magwien.gv.at

Re: Dateiende in Array einlesen

am 16.08.2006 16:05:36 von KWittrock

> "Ingo Menger" schrieb im Newsbeitrag
> news:1155300302.324445.297140@b28g2000cwb.googlegroups.com.. .
>
> TorstenR wrote:
> > Hallo,
> >
> > ich möchte die letzten 20 Zeilen einer ziemlich langen Textdatei (bis zu
> > 300.000 Zeilen) möglichst effizient zeilenweise in ein Array einlesen.
> >
> ................
>
> Wenn Du es selbst programmierst, kannst Du so vorgehen:
> Starte mit b = l * m,
> wobei l die Anzahl der Zeilen ist, die du lesen möchtest, m die
> erwartete maximale Zeilenlänge.
> Sei s die Größe des Files in Byte.
> Wenn s-b <= 0, dann lies das ganze File ein
> sonst while true:
> Positioniere das File b Zeichen vor dem Ende.
> Lies den Rest des Files in das Array
> Sind es weniger als l Zeilen? Setze b = 1.5 b und next.
> last
>
> Gib die letzten l Zeilen des Arrays aus.

Man könnte diesen Ansatz noch etwas verfeinern: Wenn man zu wenig eingelesen
hat, hat man die maximale Zeilenlänge unterschätzt. Statt diese nun fest um
50% zu erhöhen, stellt man jetzt die maximale Zeilenlänge im eingelesenen
Schwanz fest. Dabei muss man die erste gelesene Zeile mit berücksichtigen.
Die ist zwar vermutlich unvollständig, könnte aber trotzdem sehr lang sein.
Außerdem könnte man vermeiden, den eingelesenen Schwanz noch einmal
einzulesen.

Und Vorsicht: Unicode-Dateien muss man für diese Leserei als Binärdateien
behandeln.

Gruß

Klaus

Re: Dateiende in Array einlesen

am 16.08.2006 18:10:43 von Ingo Menger

K Wittrock schrieb:

> > "Ingo Menger" schrieb im Newsbeitrag
> > news:1155300302.324445.297140@b28g2000cwb.googlegroups.com.. .
> >
> > TorstenR wrote:
> > > Hallo,
> > >
> > > ich möchte die letzten 20 Zeilen einer ziemlich langen Textdatei (b=
is zu
> > > 300.000 Zeilen) möglichst effizient zeilenweise in ein Array einl=
esen.
> > >
> > ................
> >
> > Wenn Du es selbst programmierst, kannst Du so vorgehen:
> > Starte mit b =3D l * m,
> > wobei l die Anzahl der Zeilen ist, die du lesen möchtest, m die
> > erwartete maximale Zeilenlänge.
> > Sei s die Größe des Files in Byte.
> > Wenn s-b <=3D 0, dann lies das ganze File ein
> > sonst while true:
> > Positioniere das File b Zeichen vor dem Ende.
> > Lies den Rest des Files in das Array
> > Sind es weniger als l Zeilen? Setze b =3D 1.5 b und next.
> > last
> >
> > Gib die letzten l Zeilen des Arrays aus.
>
> Man könnte diesen Ansatz noch etwas verfeinern: Wenn man zu wenig einge=
lesen
> hat, hat man die maximale Zeilenlänge unterschätzt. Statt diese nun f=
est um
> 50% zu erhöhen, stellt man jetzt die maximale Zeilenlänge im eingeles=
enen
> Schwanz fest. Dabei muss man die erste gelesene Zeile mit berücksichtig=
en.
> Die ist zwar vermutlich unvollständig, könnte aber trotzdem sehr lang=
sein.

Eigentlich ist das alles noch zu kompliziert.
Man lese einfach feste Blöcke ein, bis man die gewünschte
Zeilenanzahl hat und positioniere am Anfang auf Filegröße -
Blocklänge und nach jedem Read 2*Blocklänge zurück. Sollte man je
"über den Fileanfang hinauspositionieren", ist das File "zu klein",
dh., man hat dann alles und gibt es aus.

> Außerdem könnte man vermeiden, den eingelesenen Schwanz noch einmal
> einzulesen.

Bei genügend kleinem l dürfte der ohnehin noch im OS-Puffer stehen.
Aber wenn man, wie vorgeschlagen, Block um Block rückwärts liest, ist
auch doppeltes Lesen ausgeschlossen.

Re: Dateiende in Array einlesen

am 18.08.2006 06:15:04 von Christian Lackas

* Ferry Bolhar [2006-08-16]:

Hallo Ferry,

> @array = `tail -20 $datei`;
> Perl ist gut, aber manchmal sind die guten alten UNIX-Tools doch
> um einiges schneller.

im CPAN gibt es Module, die das ähnlich effizient und auch noch portabel
hinbekommen.

Gruß
Christian

--
Was lange zippt wird endlich kurz.
http://www.lackas.net/ Perl Delphi Linux MP3 Searchengines Domainchecker

Re: Dateiende in Array einlesen

am 18.08.2006 09:33:56 von Ferry Bolhar

Christian Lackas:

> im CPAN gibt es Module, die das ähnlich effizient und auch noch portabel
> hinbekommen.

Was ist an meiner Lösung nicht portabel? "tail" gibt es unter allen
UNIX-Derivaten, für Windows lässt es sich nachinstallieren.

Die Option, auf den langsameren, nur Perl-basierenden Ansatz
auszuweichen, ist ja ohnehin immer gegeben. Hier ging es eben
auch um Effiizienz.

Und von welchen Modulen sprichst du? Bitte um Beispiele.

LG, Ferry

--
Ing. Ferry Bolhar
Municipality of Vienna, Department 14
A-1010 Vienna / AUSTRIA
E-mail: bol@adv.magwien.gv.at

Re: Dateiende in Array einlesen

am 18.08.2006 18:15:13 von Joerg Plate

> Die Option, auf den langsameren, nur Perl-basierenden Ansatz auszuweichen,
> ist ja ohnehin immer gegeben. Hier ging es eben auch um Effiizienz.

Wie hast diese Effizienz gezeigt? Benchmark? Theoretischer Beweis? Geraten?
"Nur Perl" heisst nicht grundsaetzlich langsamer.

--
"I'm working on it."

Re: Dateiende in Array einlesen

am 19.08.2006 15:21:54 von hjp-usenet2

On Fri, 18 Aug 2006 18:15:13 +0200, Joerg Plate wrote:
>> Die Option, auf den langsameren, nur Perl-basierenden Ansatz auszuweichen,
>> ist ja ohnehin immer gegeben. Hier ging es eben auch um Effiizienz.
>
> Wie hast diese Effizienz gezeigt? Benchmark? Theoretischer Beweis? Geraten?
> "Nur Perl" heisst nicht grundsaetzlich langsamer.

#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
use Benchmark qw(cmpthese);
use File::ReadBackwards;

sub tail1 {
my ($filename, $n) = @_;
my @a = `tail -n $n '$filename'`;
#print "tail1:\n @a";
return @a;
}

sub tail2 {
my ($filename, $n) = @_;
open (my $fh, '-|', '/usr/bin/tail', '-n', $n, $filename) or return ();
my @a = <$fh>;
#print "tail2:\n @a";
return @a;
}

sub readbackwards {
my ($filename, $n) = @_;
my $bw = File::ReadBackwards->new($filename) or return ();
my @a;
while (--$n >= 0 && defined(my $line = $bw->readline())) {
unshift(@a, $line);
}
#print "readbackwards:\n @a";
return @a;
}

cmpthese($ARGV[0] || 1000,
{
tail1 => sub { tail1('foobar', $ARGV[1] || 20); },
tail2 => sub { tail2('foobar', $ARGV[1] || 20); },
readbackwards => sub { readbackwards('foobar', $ARGV[1] || 20); },
});
__END__

yoyo:~/tmp 15:09 140% ./foo 1000 100
Rate readbackwards tail1 tail2
readbackwards 407/s -- -46% -47%
tail1 758/s 86% -- -1%
tail2 763/s 88% 1% --

yoyo:~/tmp 15:10 141% ./foo 1000 1
Rate tail2 tail1 readbackwards
tail2 1220/s -- -15% -32%
tail1 1429/s 17% -- -20%
readbackwards 1786/s 46% 25% --

Kommt also darauf an, wieviele Zeilen man liest. Bei einer Zeile ist
File::ReadBackwards deutlich schneller, bei 100 Zeilen tail. Der
Break-Even-Point liegt auf meinem Laptop bei ca. 30 Zeilen. Je nach
Hardware und OS kann das aber auch ganz anders sein.

tail1 ist bei mir übrigens meistens schneller als tail2, und das
verstehe ich nicht: tail1 ruft nämlich tail indirekt über eine Shell
auf, was fast 200 Systemcalls mehr bedeutet - und perl-seitig sollte der
Aufwand ja wohl ziemlich gleich sein, oder ist `` so viel effizienter
implementiert als <>?

hp

--
_ | Peter J. Holzer | > Wieso sollte man etwas erfinden was nicht
|_|_) | Sysadmin WSR | > ist?
| | | hjp@hjp.at | Was sonst wäre der Sinn des Erfindens?
__/ | http://www.hjp.at/ | -- P. Einstein u. V. Gringmuth in desd

Re: Dateiende in Array einlesen

am 21.08.2006 10:52:56 von Ferry Bolhar

Joerg Plate:

> Wie hast diese Effizienz gezeigt? Benchmark? Theoretischer Beweis?
Geraten?
> "Nur Perl" heisst nicht grundsaetzlich langsamer.

Nein, natürlich nicht, allerdings habe ich die Erfahrung gemacht, dass
besonders
bei "größeren" Dateien (> 10k) die UNIX-Tools, die ja dafür optimiert
wurden,
schneller sind als vergleichbarer Perl-Code. Das ist zumindest bei "cat" und
"grep" definitiv der Fall. Ich gebe zu, dass ich mit "tail" noch keine
expliziten
Versuche gemacht habe, aber ich denke nicht, dass es da viel anders sein
wird.
Aber ich lasse mich auch gerne vom Gegenteil überzeugen.

LG, Ferry

--
Ing. Ferry Bolhar
Municipality of Vienna, Department 14
A-1010 Vienna / AUSTRIA
E-mail: bol@adv.magwien.gv.at

Re: Dateiende in Array einlesen

am 29.08.2006 13:50:21 von Ferry Bolhar

Stephan 'smg' Grein:

>>> @array = `tail -20 $datei`;
>
> Es sollte aber eher
> @Array = `tail -n 0 $datei`;
> heißen.

Ach so? Nun, ich habe es mit "-20" probiert und die letzten zwanzig
Sätze meiner Testdatei erhalten. Wofür steht "-n 0"?

LG, Ferry
--

Re: Dateiende in Array einlesen

am 29.08.2006 14:00:11 von Otto Lang

Ferry Bolhar wrote:
> Stephan 'smg' Grein:
>>>> @array = `tail -20 $datei`;
>> Es sollte aber eher
>> @Array = `tail -n 0 $datei`;
>> heißen.
> Ach so? Nun, ich habe es mit "-20" probiert und die letzten zwanzig
> Sätze meiner Testdatei erhalten. Wofür steht "-n 0"?

Lt. Manpage gibt es die Schreibweise "tail -" nicht, sondern nur
"tail -n ". Somit würde "tail -n 0" nichts zurücklieferen, was
mich auf einen Typo des Posters schliessen lässt :)

> LG, Ferry

../otto

Re: Dateiende in Array einlesen

am 06.09.2006 12:10:53 von Ferry Bolhar

Otto Lang:

> Lt. Manpage gibt es die Schreibweise "tail -" nicht, sondern nur
> "tail -n ".

OK, - ist die frühere Schreibweise. Sie funktioniert aber auch jetzt
noch bei allen tail's, die ich getestet habe (Linux, FreeBSD, AIX, OSF).

Und "tail -n 0" bzw. "tail -0" gibt gar nichts aus, macht also keinen Sinn.

LG, Ferry

--
Ing Ferry Bolhar
Magistrat der Stadt Wien - MA 14
A-1010 Wien
E-Mail: bol@adv.magwien.gv.at