I am trying to do print all of the values of an array from a CSV file. I am sort of manually doing this in the example below. Can someone show me the code for doing this for all of the fields of the array no matter how many fields there are? I'm basically just trying to print each field on a new line.
#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV_XS;
my $file = 'test.csv';
my $csv = Text::CSV_XS->new ({
quote_char => '"',
escape_char => '#',
binary => 1,
keep_meta_info => 0,
allow_loose_quotes => 1,
allow_whitespace => 1,
});
open (CSV, "<", $file) or die $!;
while (<CSV>) {
if ($csv->parse($_)) {
my #columns = $csv->fields();
print "$columns[0]\r\n";
print "$columns[1]\r\n";
print "$columns[2]\r\n";
print "$columns[3]\r\n";
print "$columns[4]\r\n";
print "$columns[5]\r\n";
print "$columns[6]\r\n";
print "$columns[7]\r\n";
}
else {
my $err = $csv->error_input;
print "Failed to parse line: $err";
}
}
close CSV;
foreach(#columns)
{
print "$_\r\n";
}
Instead of all the columns[number].
For debugging purposes, Data::Dump is my weapon of choice. It basically pretty-prints data structures.
use strict;
use warnings;
use Data::Dump 'dump';
# Do some stuff....
dump #array; # Unlike Data::Dumper, there's no need to backslash ('\#array')
dump %hash; # Same goes for hashes
dump $arrayref;
dump $hashref; # References handled just as well
There are many other ways to print arrays, of course:
say foreach #columns; # If you have Perl 5.10+
print $_,"\n" foreach #columns; # If you don't
print "#columns"; # Prints all elements, space-separated by default
The 'best' answer depends on the situation. Why do you need it? What are you working with? And what do you want it for? Then season the code accordingly.
If you just want to print the elements separated by spaces:
print #columns;
If you want to be a bit more fancy, you can use join:
print join("\n", #columns);
If you need to do something more, iterate over it:
foreach (#columns) {
# do stuff with $_
}
If you're doing this for diagnostic purposes (as opposed to presentation) you might consider Data::Dumper. In any case it's a good tool to know about if you want a quick printout of more-or-less arbitrary data.
{ $"="\n"; print $fh "#files"; }
Related
I have a text file like which contains information like this:
name=A
class=B
RollNo=C
I want to extract the values in perl script
key(name) = value(A)
key(class) = value(B)
key(RollNo) = value(C)
the keys should be exported as the variables which will have values. Whenever we type
print $name
the output should be 'A'
I have tried:
open my $fh, '<', $file_name
or die "Could not open sample.txt: $!";
my #lines = <$fh>;
my %hash;
while (<#lines>) {
chomp;
my ($key, $value) = split /=/;
next unless defined $value;
$hash{$key} = $value;
}
print %hash;
Your code looks pretty good and most of what you've done so far works.
At the end, you run print %hash and that doesn't give you what you expect. That will "unroll" the keys and values from the hash into a list and print that list. So you get all of the keys and values printed out.
If you just want one value (for example, the value associated with the "name" key), then just print that.
print $hash{name};
Is that what you were looking for?
You could try using one of the configuration modules that are available. Config::Tiny seems to fit your data:
use strict;
use warnings;
use Data::Dumper;
use Config::Tiny;
my $Config = Config::Tiny->new;
$Config = Config::Tiny->read( 'a.txt' ); # your text file name goes here
print $Config->{_}{name}; # print the name value
print Dumper $Config; # print all the values in perl variable format
You can store data in hash and can retrieve from their.
use strict;
use warnings;
use Data::Dumper;
my %hash = (
name => 'A',
class => 'B',
RollNo => 'C'
);
print Dumper(\%hash);
print $hash{'name'};
I am a beginner programmer, who has been given a weeklong assignment to build a complex program, but is having a difficult time starting off. I have been given a set of data, and the goal is separate it into two separate arrays by the second column, based on whether the letter is M or F.
this is the code I have thus far:
#!/usr/local/bin/perl
open (FILE, "ssbn1898.txt");
$x=<FILE>;
split/[,]/$x;
#array1=$y;
if #array1[2]="M";
print #array2;
else;
print #array3;
close (FILE);
How do I fixed this? Please try and use the simplest terms possible I stared coding last week!
Thank You
First off - you split on comma, so I'm going to assume your data looks something like this:
one,M
two,F
three,M
four,M
five,F
six,M
There's a few problems with your code:
turn on strict and warnings. The warn you about possible problems with your code
open is better off written as open ( my $input, "<", $filename ) or die $!;
You only actually read one line from <FILE> - because if you assign it to a scalar $x it only reads one line.
you don't actually insert your value into either array.
So to do what you're basically trying to do:
#!/usr/local/bin/perl
use strict;
use warnings;
#define your arrays.
my #M_array;
my #F_array;
#open your file.
open (my $input, "<", 'ssbn1898.txt') or die $!;
#read file one at a time - this sets the implicit variable $_ each loop,
#which is what we use for the split.
while ( <$input> ) {
#remove linefeeds
chomp;
#capture values from either side of the comma.
my ( $name, $id ) = split ( /,/ );
#test if id is M. We _assume_ that if it's not, it must be F.
if ( $id eq "M" ) {
#insert it into our list.
push ( #M_array, $name );
}
else {
push ( #F_array, $name );
}
}
close ( $input );
#print the results
print "M: #M_array\n";
print "F: #F_array\n";
You could probably do this more concisely - I'd suggest perhaps looking at hashes next, because then you can associate key-value pairs.
There's a part function in List::MoreUtils that does exactly what you want.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use List::MoreUtils 'part';
my ($f, $m) = part { (split /,/)[1] eq 'M' } <DATA>;
say "M: #$m";
say "F: #$f";
__END__
one,M,foo
two,F,bar
three,M,baz
four,M,foo
five,F,bar
six,M,baz
The output is:
M: one,M,foo
three,M,baz
four,M,foo
six,M,baz
F: two,F,bar
five,F,bar
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my #boys=();
my #girls=();
my $fname="ssbn1898.txt"; # I keep stuff like this in a scalar
open (FIN,"< $fname")
or die "$fname:$!";
while ( my $line=<FIN> ) {
chomp $line;
my #f=split(",",$line);
push #boys,$f[0] if $f[1]=~ m/[mM]/;
push #girls,$f[1] if $f[1]=~ m/[gG]/;
}
print Dumper(\#boys);
print Dumper(\#girls);
exit 0;
# Caveats:
# Code is not tested but should work and definitely shows the concepts
#
In fact the same thing...
#!/usr/bin/perl
use strict;
my (#m,#f);
while(<>){
push (#m,$1) if(/(.*),M/);
push (#f,$1) if(/(.*),F/);
}
print "M=#m\nF=#f\n";
Or a "perl -n" (=for all lines do) variant:
#!/usr/bin/perl -n
push (#m,$1) if(/(.*),M/);
push (#f,$1) if(/(.*),F/);
END { print "M=#m\nF=#f\n";}
I am currently writing a perl script where I have a reference to an array (students) of references. After adding the hash references to the array. Now I add the references to the array of students and then ask the user how to sort them. This is where it gets confusing. I do not know how to deference the sorted array. Using dumper I can get the sorted array but in a unorganized output. How can I deference the array of hash references after sorting?
#!bin/usr/perl
use strict;
use warnings;
use Data::Dumper;
use 5.010;
#reference to a var $r = \$var; Deferencing $$r
#reference to an array $r = \#var ; Deferencing #$r
#referenc to a hash $r = \%var ; deferencing %$r
my $filename = $ARGV[0];
my $students = [];
open ( INPUT_FILE , '<', "$filename" ) or die "Could not open to read \n ";
sub readLines{
while(my $currentLine = <INPUT_FILE>){
chomp($currentLine);
my #myLine = split(/\s+/,$currentLine);
my %temphash = (
name => "$myLine[0]",
age => "$myLine[1]",
GPA => "$myLine[2]",
MA => "$myLine[3]"
);
pushToStudents(\%temphash);
}
}
sub pushToStudents{
my $data = shift;
push $students ,$data;
}
sub printData{
my $COMMAND = shift;
if($COMMAND eq "sort up"){
my #sortup = sort{ $a->{name} cmp $b->{name} } #$students;
print Dumper #sortup;
}elsif($COMMAND eq "sort down"){
my #sortdown = sort{ $b->{name} cmp $a->{name} } #$students;
print Dumper #sortdown;
//find a way to deference so to make a more organize user friendly read.
}else{
print "\n quit";
}
}
readLines();
#Output in random, the ordering of each users data is random
printf"please choose display order : ";
my $response = <STDIN>;
chomp $response;
printData($response);
The problem here is that you're expected Dumper to provide an organised output. It doesn't. It dumps a data structure to make debugging easier. The key problem will be that hashes are explicitly unordered data structures - they're key-value mappings, they don't produce any output order.
With reference to perldata:
Note that just because a hash is initialized in that order doesn't mean that it comes out in that order.
And specifically the keys function:
Hash entries are returned in an apparently random order. The actual random order is specific to a given hash; the exact same series of operations on two hashes may result in a different order for each hash.
There is a whole section in perlsec which explains this in more detail, but suffice to say - hashes are random order, which means whilst you're sorting your students by name, the key value pairs for each student isn't sorted.
I would suggest instead of:
my #sortdown = sort{ $b->{name} cmp $a->{name} } #$students;
print Dumper #sortdown;
You'd be better off with using a slice:
my #field_order = qw ( name age GPA MA );
foreach my $student ( sort { $b -> {name} cmp $a -> {name} } #$students ) {
print #{$student}{#field_order}, "\n";
}
Arrays (#field_order) are explicitly ordered, so you will always print your student fields in the same sequence. (Haven't fully tested for your example I'm afraid, because I don't have your source data, but this approach works with a sample data snippet).
If you do need to print the keys as well, then you may need a foreach loop instead:
foreach my $field ( #field_order ) {
print "$field => ", $student->{$field},"\n";
}
Or perhaps the more terse:
print "$_ => ", $student -> {$_},"\n" for #field_order;
I'm not sure I like that as much though, but that's perhaps a matter of taste.
The essence of your mistake is to assume that hashes will have a specific ordering. As #Sobrique explains, that assumption is wrong.
I assume you are trying to learn Perl, and therefore, some guidance on the basics will be useful:
#!bin/usr/perl
Your shebang line is wrong: On Windows, or if you run your script with perl script.pl, it will not matter, but you want to make sure the interpreter that is specified in that line uses an absolute path.
Also, you may not always want to use the perl interpreter that came with the system, in which case #!/usr/bin/env perl maybe helpful for one-off scripts.
use strict;
use warnings;
use Data::Dumper;
use 5.010;
I tend to prefer version constraints before pragmata (except in the case of utf8). Data::Dumper is a debugging aid, not something you use for human readable reports.
my $filename = $ARGV[0];
You should check if you were indeed given an argument on the command line as in:
#ARGV or die "Need filename\n";
my $filename = $ARGV[0];
open ( INPUT_FILE , '<', "$filename" ) or die "Could not open to read \n ";
File handles such as INPUT_FILE are called bareword filehandles. These have package scope. Instead, use lexical filehandles whose scope you can restrict to the smallest appropriate block.
There is no need to interpolate $filename in the third argument to open.
Always include the name of the file and the error message when dying from an error in open. Surrounding the filename with ' ' helps you identify any otherwise hard to detect characters that might be causing the problem (e.g. a newline or a space).
open my $input_fh, '<', $filename
or die "Could not open '$filename' for reading: $!";
sub readLines{
This is reading into an array you defined in global scope. What if you want to use the same subroutine to read records from two different files into two separate arrays? readLines should receive a filename as an argument, and return an arrayref as its output (see below).
while(my $currentLine = <INPUT_FILE>){
chomp($currentLine);
In most cases, you want all trailing whitespace removed, not just the line terminator.
my #myLine = split(/\s+/,$currentLine);
split on /\s+/ is different than split ' '. In most cases, the latter is infinitely more useful. Read about the differences in perldoc -f split.
my %temphash = (
name => "$myLine[0]",
age => "$myLine[1]",
GPA => "$myLine[2]",
MA => "$myLine[3]"
);
Again with the useless interpolation. There is no need to interpolate those values into fresh strings (except maybe in the case where they might be objects which overloaded the stringification, but, in this case, you know they are just plain strings.
pushToStudents(\%temphash);
No need for the extra pushToStudents subroutine in this case, unless this is a stub for a method that will later be able to load the data to a database or something. Even in that case, it be better to provide a callback to the function.
sub pushToStudents{
my $data = shift;
push $students ,$data;
}
You are pushing data to a global variable. A program where there can only ever be a single array of student records is not useful.
sub printData{
my $COMMAND = shift;
if($COMMAND eq "sort up"){
Don't do this. Every subroutine should have one clear purpose.
Here is a revised version of your program.
#!/usr/bin/env perl
use 5.010;
use strict;
use warnings;
use Carp qw( croak );
run(\#ARGV);
sub run {
my $argv = $_[0];
#$argv
or die "Need name of student records file\n";
open my $input_fh, '<', $argv->[0]
or croak "Cannot open '$argv->[0]' for reading: $!";
print_records(
read_student_records($input_fh),
prompt_sort_order(),
);
return;
}
sub read_student_records {
my $fh = shift;
my #records;
while (my $line = <$fh>) {
last unless $line =~ /\S/;
my #fields = split ' ', $line;
push #records, {
name => $fields[0],
age => $fields[1],
gpa => $fields[2],
ma => $fields[3],
};
}
return \#records;
}
sub print_records {
my $records = shift;
my $sorter = shift;
if ($sorter) {
$records = [ sort $sorter #$records ];
}
say "#{ $_ }{ qw( age name gpa ma )}" for #$records;
return;
}
sub prompt_sort_order {
my #sorters = (
[ "Input order", undef ],
[ "by name in ascending order", sub { $a->{name} cmp $b->{name} } ],
[ "by name in descending order", sub { $b->{name} cmp $a->{name} } ],
[ "by GPA in ascending order", sub { $a->{gpa} <=> $b->{gpa} } ],
[ "by GPA in descending order", sub { $b->{gpa} <=> $a->{gpa} } ],
);
while (1) {
print "Please choose the order in which you want to print the records\n";
print "[ $_ ] $sorters[$_ - 1][0]\n" for 1 .. #sorters;
printf "\n\t(%s)\n", join('/', 1 .. #sorters);
my ($response) = (<STDIN> =~ /\A \s*? ([1-9][0-9]*?) \s+ \z/x);
if (
$response and
($response >= 1) and
($response <= #sorters)
) {
return $sorters[ $response - 1][1];
}
}
# should not be reached
return;
}
I would like to input data from a list in Perl. So far, I have been pasting lists of data into the actual program for it to process. My original sorting program looked like this:
# /perl/bin
use strict; #force perl to code properly
use warnings; #find typing mistakes in program like missing semicolons, etc.
use Text::ParseWords; #parse text into an array of tokens or array of arrays
my #rows;
while (<DATA>) {
push #rows, [ parse_line(',', 0, $_) ];
}
#rows = sort { $a->[2] <=> $b->[2] } #rows;
open OUTPUT, ">OUTPUT.TXT";
foreach (#rows) {
print OUTPUT join ',', #$_;
}
__DATA__
SMITH,M,1
JONES,F,1
...
But I would like to have it input from a file that has this list instead. I'm not sure if I'm even on the right track but this is what I have so far:
# /perl/bin
use strict; #force perl to code properly
use warnings; #find typing mistakes in program like missing semicolons, etc.
use autodie; #replace functions with ones that succeed or die with lexical scope
use Text::ParseWords; #parse text into an array of tokens or array of arrays
open(MYINPUTFILE, "<inputfile.txt"); # open for input
my #rows = <MYINPUTFILE>; # read file into list
while (<MYINPUTFILE>) {
push #rows, [ parse_line(',', 0, $_) ];
}
#rows = sort { $a->[2] <=> $b->[2] } #rows;
open OUTPUT, ">OUTPUT.TXT";
foreach (#rows) {
print OUTPUT join ',', #$_;
}
Here's the crux of your problem:
open(MYINPUTFILE, "<inputfile.txt"); # open for input
my #rows = <MYINPUTFILE>; # read file into list
Since "#rows =" gives a "wantarray" or list
context to the right-hand side,
"<>" reads the entire file.
Then:
while (<MYINPUTFILE>) { # try to read file again,
# but you've already read it all
push #rows, [ parse_line(',', 0, $_) ];
}
You're trying to read the file twice.
There are other issues with your code, but you probably meant to write only:
open(MYINPUTFILE, "<inputfile.txt"); # open for input
my #rows;
while (<MYINPUTFILE>) {
push #rows, [ parse_line(',', 0, $_) ];
}
… by parallel to your earlier version.
At the least, you might consider a couple of Perlish changes;
open my $input_file, '<', 'inputfile.txt';
Using a lexical ("my") variable instead of a *FILEHANDLE is nicer in any more complex situation than this one. (Among other things, you can pass it to a subroutine much more easily.) Using the three-argument form of open also protects you against problems if you allow others to specify the filename to your program. You then would use <$input_file> in your while loop.
This question already has an answer here:
I need help in perl, how to write a code to get the output of my csv file in the form of a hash [closed]
(1 answer)
Closed 10 years ago.
I am new to Perl, and have to write a code which takes contents of a file into and array and print the output that it looks like a hash. Here is an example entry:
my %amino_acids = (F => ["Phenylalanine", "Phe", ["TTT", "TTC"]])
Out put should be exactly in above format.
Lines of Files are like this...
"Methionine":"Met":"M":"AUG":"ATG"
"Phenylalanine":"Phe":"F":"UUU, UUC":"TTT, TTC"
"Proline":"Pro":"P":"CCU, CCC, CCA, CCG":"CCT, CCC, CCA, CCG"
I have to take last codons after semicolon and ignore the first group.
Is it your intention to build the equivalent hash? Or do you really want the string format? This program uses Text::CSV to build the hash from the file and then dumps it using Data::Dump so that you have the string format as well.
use strict;
use warnings;
use Text::CSV;
use Data::Dump 'dump';
my $csv = Text::CSV->new({ sep_char => ':' });
open my $fh, '<', 'amino.txt' or die $!;
my %amino_acids;
while (my $data= $csv->getline($fh)) {
$amino_acids{$data->[2]} = [
$data->[0],
$data->[1],
[ $data->[4] =~ /[A-Z]+/g ]
];
}
print '$amino_acids = ', dump \%amino_acids;
output
$amino_acids = {
F => ["Phenylalanine", "Phe", ["TTT", "TTC"]],
M => ["Methionine", "Met", ["ATG"]],
P => ["Proline", "Pro", ["CCT", "CCC", "CCA", "CCG"]],
}
Update
If you really don't want to install modules (it is a very straightforward process and makes the code much more concise and reliable) then this does what you need.
use strict;
use warnings;
open my $fh, '<', 'amino.txt' or die $!;
print "my %amino_acids = (\n";
while (<$fh>) {
chomp;
my #data = /[^:"]+/g;
my #codons = $data[4] =~ /[A-Z]+/g;
printf qq{ %s => ["%s", "%s", [%s]],\n},
#data[2,0,1],
join ', ', map qq{"$_"}, #codons;
}
print ")\n";
output
my %amino_acids = (
M => ["Methionine", "Met", ["ATG"]],
F => ["Phenylalanine", "Phe", ["TTT", "TTC"]],
P => ["Proline", "Pro", ["CCT", "CCC", "CCA", "CCG"]],
)
Assuming you actually want valid perl as the output, this will do it:
open(my $IN, "<input.txt") or die $!;
while(<$IN>){
chomp;
my #tmp = split(':',$_);
if(#tmp != 5){
# error on this line
next;
}
my $group = join('","',split(/,\s*/,$tmp[4]));
print "\$amino_acids{$tmp[2]} = [$tmp[0],$tmp[1],[$group]];\n";
}
close $IN;
Using your sample lines, the output is:
$amino_acids{"M"} = ["Methionine","Met",["ATG"]];
$amino_acids{"F"} = ["Phenylalanine","Phe",["TTT","TTC"]];
$amino_acids{"P"} = ["Proline","Pro",["CCT","CCC","CCA","CCG"]];
#Borodin Thank you very much for your answer, actually I don't have to use Text::csv or Data::dump.I have to open a file and build the equivalent hash from the file.I am trying to do without using both, hopefully it will help.Thanks again!!!
Perl has no special method to print hashes. What you should probably do is create a hash when reading the file:
while (<FILE>) {
my #line = split ':'; # split the line into an array
$amino_acids{$line[0]} = \#line[1..-1]; # take elements 1..end
}
And then print out the hash one entry at a time:
foreach (keys %amino_acids) {
print "$_ => [", (join ",", #$amino_acids{$_}), "]\n";
}
Note that I didn't compile this, so it may need a small amount of work to get it done.