I asked this question on the chatterbox, and it provoked a flurry of interesting responses -- seven or eight different ones from tye alone! -- so I thought I might post it and see what monks came up with.
Problem -- I have a file. I want to read it in four-line chunks, because it goes
name1
address1
phone1
fax1
name2
address2
phone2
fax2
and so on.
Solution? My first one was to slurp and then do like this:
for($i=0;$<scalar(@slurpedarray);$i+=4){
# work with $slurpedarray[$i], $slurpedarray[($i+1)],
# $slurpedarray[($i+2)] and $slurpedarray[($i+2)]
}
which makes me wince to look at it.
Now I'm going to do it with the much more Perlish
while(@slurpedarray){
($name,$address,$phone,$fax) = splice(@slurpedarray,0,3)
# work with nice named vars
}
Interesting bit: all these solutions are based on whole-file slurpage (fine for me, very small files) but the interesting solutions were the ones which didn't do that, the ones for actually reading the file itself.
Any interesting solutions? (Stands back...)
--
Every bit of code is either naturally related to the problem at hand, or else it's an accidental side effect of the fact that you happened to solve the problem using a digital computer.
M-J D
Re: Read File In Four-Line Chunks / TMTOWTDI / Golf
by larsen (Parson) on Jun 06, 2003 at 08:14 UTC
|
According to me, the simplest way is to abstract the operation of "reading a chunk of data" in a procedure, like in this example:
use strict;
use warnings;
while ( my @s = read_chunk() )
{
print join " | ", @s;
print "\n";
}
sub read_chunk
{
my @stuff = ();
while( my $line = <DATA> ) {
chomp $line;
push @stuff, $line;
last if $. % 4 == 0;
}
return @stuff;
}
__DATA__
first1
second1
third1
fourth1
first2
second2
third2
fourth2
first3
second3
So you can treat a read_chunk() operation as an atomic one.
A more clever option you could explore is using Tie::File, which allows you to manipulate the file as an array of lines, even if simgle lines are actually read only when they're needed (details on the documentation).
| [reply] [d/l] [select] |
Re: Read File In Four-Line Chunks / TMTOWTDI / Golf
by Aristotle (Chancellor) on Jun 06, 2003 at 11:45 UTC
|
#!/usr/bin/perl -w
use strict;
open my $fh, '<', 'details.txt' or die $!;
until(grep !defined, my @details = map scalar <$fh>, 1 .. 4) {
print @details, "\n";
}
A C programmer's well trained reflex:
#!/usr/bin/perl -w
use strict;
open my $fh, '<', 'details.txt' or die $!;
my $i;
my @details;
while(<$fh>) {
push @details, $_;
next if ++$i % 4;
print @details, "\n";
@details = ();
}
A LISP hacker's immediate reaction:
#!/usr/bin/perl -w
use strict;
open my $fh, '<', 'details.txt' or die $!;
sub read_lines {
my ($fh, $amnt) = @_;
return unless defined(my $line = <$fh>);
return (
$line,
$amnt > 1 ? read_lines($fh, $amnt - 1) : ()
);
}
while(my @details = read_lines($fh, 4)) {
print @details, "\n";
}
Makeshifts last the longest. | [reply] [d/l] [select] |
|
Some further perversions of the LISPish way:
#!/usr/bin/perl -w
use strict;
open my $fh, '<', 'details.txt' or die $!;
my $read_lines;
while(
my @details = ($read_lines = sub {
my $amnt = shift;
return unless defined(my $line = <$fh>);
return (
$line,
$amnt > 1 ? $read_lines->($amnt - 1) : ()
);
})->(4)
) {
print @details, "\n";
}
Refined perversion:
#!/usr/bin/perl -w
use strict;
sub make_read_lines {
my ($fh, $lines) = @_;
return $lines == 1
? sub { return unless defined(my $line = <$fh>); $line }
: do {
my $next_lines = make_read_lines($fh, $lines - 1);
sub {
return unless defined(my $line = <$fh>);
$line, $next_lines->()
};
};
}
open my $fh, '<', 'details.txt' or die $!;
my $read_4_lines = make_read_lines($fh, 4);
while(my @details = $read_4_lines->()) {
print @details, "\n";
}
Makeshifts last the longest. | [reply] [d/l] [select] |
Re: Read File In Four-Line Chunks / TMTOWTDI / Golf
by tilly (Archbishop) on Jun 06, 2003 at 08:40 UTC
|
Untested code which should not be sprung on unwary maintainance programmers:
while(grep {$_=<FH>; chop} my($name,$address,$phone,$fax) ) {
# Do stuff
}
(No Perl here, I can't test it. And I will hang my head in shame if it doesn't work...)
| [reply] [d/l] |
|
| [reply] |
|
while ( 4 == scalar grep {$_ = <FH>; chomp} my($name,$address,$phone,$
+fax) ) {
# Do stuff
}
Update: See Aristotle's comments below. This will fail to process a four line record if the last record is missing an EOL. Also, using an explicit scalar is superfluous... and if you are already torturing your maintenance programmer with this construct, why be explicit? :-)
-sauoq
"My two cents aren't worth a dime.";
| [reply] [d/l] |
|
Careful - if the last line of the file has no newline, chomp will return 0, causing grep to return 3, rather than 4, causing the while to terminate early even though the record is complete. Also, you don't need the explicit scalar - == provides scalar context for you (case in point: @a == @b checks for identical array size).
while(4 == grep { chomp($_ = <FH>); defined } my($name,$address,$phone
+,$fax)){
# Do stuff
}
Makeshifts last the longest. | [reply] [d/l] |
Re: Read File In Four-Line Chunks / TMTOWTDI / Golf
by Lachesis (Friar) on Jun 06, 2003 at 08:05 UTC
|
Not a big change but you can do
while (my ($name,$address,$phone,$fax) = splice(@slurpedarray,0,4)){
#work with named vars
}
The last splice argument there should be 4 since its a length not an index. | [reply] [d/l] |
|
while (my ($name,$address,$phone,$fax) = (<FILE>, <FILE>, <FILE>, <FIL
+E>)) {
#work with named vars (don't forget chomp)
}
Update: just did a little test, and it does one iteration and stops. Hm.
-- [ e d @ h a l l e y . c c ] | [reply] [d/l] |
|
You're using <FH> in a list context so it would read in the entire file in the first iteration
Update: apologies I'm speaking rubbish there. I did a quick test my self and it picks up the data you would expect but only does iterate the once.
| [reply] |
Re: Read File In Four-Line Chunks / TMTOWTDI / Golf
by BrowserUk (Patriarch) on Jun 06, 2003 at 10:32 UTC
|
#! perl -slw
use strict;
require 5.008;
use constant { NAME=>0, ADDR=>1, PHONE=>2, FAX=>3 };
open my $fh, '<', 'details.txt' or die $!;
while( !eof($fh) ){
my @details = map{ scalar <$fh> } 1..4;
chomp @details;
print "@details[ NAME, ADDR, PHONE, FAX ]";
}
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
| [reply] [d/l] |
(jeffa) Re: Read File In Four-Line Chunks / TMTOWTDI / Golf
by jeffa (Bishop) on Jun 06, 2003 at 15:23 UTC
|
use strict;
use warnings;
use Tie::File;
use Data::Dumper;
my @file;
my @key = qw(name address phone fax);
tie @file, 'Tie::File', 'address.txt' or die $!;
my @record = map {
my %hash;
@hash{@key} = @file[$_..$_+4-1];
\%hash;
} range(0,$#file,4);
print Dumper \@record;
sub range {grep!(($_-$_[0])%($_[2]||1)),$_[0]..$_[1]}
jeffa
L-LL-L--L-LL-L--L-LL-L--
-R--R-RR-R--R-RR-R--R-RR
B--B--B--B--B--B--B--B--
H---H---H---H---H---H---
(the triplet paradiddle with high-hat)
| [reply] [d/l] |
Re: Read File In Four-Line Chunks / TMTOWTDI / Golf
by trs80 (Priest) on Jun 06, 2003 at 18:35 UTC
|
UPDATE - first post didn't keep them in named variables
use strict;
use Data::Dumper;
my @four;
my @all;
my $count = 0;
while (<DATA>) {
s/\s+$//;
push @four , $_;
$count++;
if ( ($count % 4) == 0) {
my %hash;
@hash{'name','address','phone','fax'} = @four;
push @all , \%hash;
@four = ();
}
}
print Dumper(\@all);
__DATA__
name1
address1
phone1
fax1
name2
address2
phone2
fax2
original post below
use strict;
use Data::Dumper;
my @four;
my @all;
my $count = 0;
while (<DATA>) {
s/\s+$//;
push @four , $_;
$count++;
if ( ($count % 4) == 0) {
push @all , [ @four ];
@four = ();
}
}
print Dumper(\@all);
__DATA__
name1
address1
phone1
fax1
name2
address2
phone2
fax2
| [reply] [d/l] [select] |
|
|