colintu has asked for the wisdom of the Perl Monks concerning the following question:
I'm writing and reading some data which includes array variables. I can make it work but I can't find an 'elegant' way. Code fragments below
my @c_speed = (5, 8, 12, 8);
my $sfh;
open($sfh, '>', $save_fn) or
die "Unable to open file $save_fn : $!";
print $sfh "@c_speed\n";
...
The file contains a line that looks like "5 8 12 8\n"
...
my $lfh;
open($lfh, '<', $load_fn) or
die "Unable to open file $load_fn : $!";
$rstring = <$lfh>; # Char speeds
($c_speed[0], $c_speed[1], $c_speed[2], $c_speed[3]) = sscanf("%d %d
+ %d %d", $rstring);
I tried using read to get the data but either encountered syntax errors or got zero (or maybe 'undef') results.
What is the correct way to read the array back in? What I'm doing works for an array of 4 but is obviously impractical for bigger arrays.
Re: Reading into array with sscanf
by hippo (Archbishop) on Jul 16, 2024 at 11:24 UTC
|
The correct answer is to choose an appropriate data format to save as, such as CSV, JSON, XML, etc. You can then use your module of choice for reliably reading and writing. If it's just for temporary backing storage then Storable, Sereal and friends will do the job too.
However, for your example set of integers here's a workable approach using split:
#!/usr/bin/env perl
use strict;
use warnings;
my $save_fn = 'colintu.dat';
my @c_speed = (5, 8, 12, 8);
open my $sfh, '>', $save_fn or
die "Unable to open file $save_fn : $!";
print $sfh "@c_speed\n";
close $sfh;
open my $lfh, '<', $save_fn or
die "Unable to open file $save_fn : $!";
@c_speed = split / /, <$lfh>;
print "Have: @c_speed\n";
close $lfh;
unlink $save_fn; # clean up
(Edited for typo fix - thanks, Corion)
| [reply] [d/l] |
|
Ah, thanks, that is what I was looking for (or failing to remember).
I don't want to use any of the usual things like JSON, XML etc because this has got to be extremely light weight as it's going to be embedded.
| [reply] |
|
I don't want to use any of the usual things like JSON, XML etc because this has got to be extremely light weight as it's going to be embedded
That's fine.
I still encourage you to structure your lightweight script file into (unit-testable) subroutines
at the top of the file, with each subroutine having well-defined inputs and outputs and not relying
on global data ... followed by a short mainline at the end.
A simple example of this approach can be found in this node.
| [reply] |
|
Don't forget to chomp your lines after reading from a file.
| [reply] |
|
I tried your example and it works BUT it does not do the same as my code example.
My code fragments return values into the c_speed array that are *numeric* not the strings that split returns, Which is why I was using sscanf from String::Scanf
The variable array c_speed needs to be numeric not string because the values get processed elsewhere.
| [reply] |
|
Perl makes very little difference between strings and numbers.
There are Perl operators, like + that treat their arguments as numbers, and other operators that treat their arguments as strings.
Where in your code do you see that the variables get treated differently?
| [reply] [d/l] |
|
#!/usr/bin/env perl
use strict;
use warnings;
my $save_fn = 'colintu.dat';
my @c_speed = (5, 8, 12, 8);
open my $sfh, '>', $save_fn or
die "Unable to open file $save_fn : $!";
print $sfh "@c_speed\n";
close $sfh;
open my $lfh, '<', $save_fn or
die "Unable to open file $save_fn : $!";
@c_speed = split / /, <$lfh>;
print "Have: @c_speed\n";
@c_speed = map { $_ * 2 } @c_speed;
print "Doubled: @c_speed\n";
close $lfh;
unlink $save_fn; # clean up
| [reply] [d/l] |
Re: Reading into array with sscanf
by tybalt89 (Monsignor) on Jul 16, 2024 at 13:53 UTC
|
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11160631
use warnings;
my $save_fn = 'save.fn'; # NOTE
my @c_speed = (5, 8, 12, 8);
my $sfh;
open($sfh, '>', $save_fn) or
die "Unable to open file $save_fn : $!";
print $sfh "@c_speed\n";
close $sfh; # NOTE
#...
#
#The file contains a line that looks like "5 8 12 8\n"
#...
my $load_fn = 'save.fn'; # NOTE
my $lfh;
open($lfh, '<', $load_fn) or
die "Unable to open file $load_fn : $!";
#$rstring = <$lfh>; # Char speeds
# ($c_speed[0], $c_speed[1], $c_speed[2], $c_speed[3]) = sscanf("%d %
+d %d %d", $rstring);
# NOTE using ' ' instead of / / also removes the \n
@c_speed = map $_ + 0, split ' ', <$lfh>; # NOTE force number
use Data::Dump 'dd'; dd @c_speed;
Outputs:
(5, 8, 12, 8)
| [reply] [d/l] [select] |
|
my $cs_e1 = $seg1_f->Entry(-textvariable => \$c_speed[0], -width =>
+4,
-validate => 'all',
-vcmd => \&validate_s
+peed,
-font => $med_font)->g
+rid(
-row => 0, -column =>
+1,
-sticky => "w");
sub validate_speed{
my $val = shift;
$val ||= 0;
#get alphas and punctuation out
if( $val !~ /^\d+$/ ){ return 0 }
if (($val >= 0) and ($val <= 10)) {return 1}
else{ return 0 }
}
When I use split I can print c_speed to the terminal and the values are there, the same as when I use my sscanf lines
$rstring = <$lfh>;
($c_speed[0], $c_speed[1], $c_speed[2], $c_speed[3]) = sscanf("%d %d
+ %d %d", $rstring);
BUT, the Tk Entry widget only sees the new values when using sscanf. When I use split the variable still retains it's old value.
I'm assuming this is some widget weirdness - can anyone explain please?
| [reply] [d/l] [select] |
|
| [reply] |
|
|
|
Re: Reading into array with sscanf
by LanX (Saint) on Jul 16, 2024 at 11:33 UTC
|
I'm confused, Perl doesn't implement sscanf , that's apparently a C function to "inverse"
printf ( for completeness compare String::Scanf )
Anyway I'm pretty sure you basically only need this to "elegantly" parse one line:
@c_speed = split / /, $rstring;
See split for more
There are more issues with your code, but better little by little.
| [reply] [d/l] [select] |
Re: Reading into array with sscanf
by jwkrahn (Abbot) on Jul 16, 2024 at 19:52 UTC
|
my $lfh;
open($lfh, '<', $load_fn) or
die "Unable to open file $load_fn : $!";
$rstring = <$lfh>; # Char speeds
($c_speed[0], $c_speed[1], $c_speed[2], $c_speed[3]) = sscanf("%d %d
+ %d %d", $rstring);
The simple answer is:
open( my $lfh, '<', $load_fn ) or
die "Unable to open file $load_fn : $!";
my @c_speed = split ' ', <$lfh>; # Char speeds
Naked blocks are fun!
-- Randal L. Schwartz, Perl hacker
| [reply] [d/l] [select] |
|
|