Perl compare array content to hash values - arrays

I'm trying to check the existence of words from a file in a hash, and if so to display the corresponding hash key and value.
So far, I've been able to do it with an array #motcherches which values are given in the script.
I can't find a way to give as values to this array words from a external file (here FILEDEUX).
Could you point me in the right direction?
(Please don't hesitate to correct my way of explaining my problem.)
#!/usr/bin/perl
use v5.10;
use strict;
use warnings;
my $hashtable = $ARGV[0];
my $textecompare = $ARGV[1];
open FILE,"<$hashtable" or die "Could not open $hashtable: $!\n";
open FILEDEUX,"<$textecompare" or die "Could not open $textecompare: $!\n";
while ( my $line = <FILE>)
{
chomp($line);
my %elements = split(" ",$line);
my #motcherches = qw/blop blup blip blap/;
foreach my $motcherches (#motcherches) {
while ((my $key, my $value) = each (%elements)) {
# Check if element is in hash :
say "$key , $value" if (exists $elements{$motcherches}) ;
}
}
}
close FILE;
close FILEDEUX;
EDIT: Example inputs
FILE (transformed into %elements hash)
Zieler player
Chilwell player
Ulloa player
Mahrez player
Ranieri coach
============================================================================
FILEDEUX (transformed into motcherchesarray)
One save is quickly followed by another, and this time Zieler had to be a little sharper to keep it out.
Izaguirre gives Mahrez a taste of his own medicine by beating him all ends up down the left flank, although the Leicester winger gave up far too easily.
Claudio Ranieri will be happy with what he has seen from his side so far.
=============================================================================
Expected output:
Zieler player
Mahrez player
Ranieri coach

use strict;
use warnings;
use feature qw/ say /;
# Read in your paragraph of text here:
open my $text, '<', 'in.txt' or die $!;
my %data;
while(<$text>){
chomp;
$data{$_}++ for split; # by default split splits on ' '
}
my %players;
while(<DATA>){
chomp;
my #field = split;
say if $data{$field[0]};
}
__DATA__
Zieler player
Chilwell player
Ulloa player
Mahrez player
Ranieri coach

Everything is fine but add the chomp in your program
while ( my $line = <FILE>)
{
chomp($line);
my %elements = split(" ",$line);

Related

How to get the data of each line from a file?

Here, I want to print the data in each line as 3 separate values with ":" as separator. The file BatmanFile.txt has the following details:
Bruce:Batman:bat#bat.com
Santosh:Bhaskar:santosh#santosh.com
And the output I expected was:
Bruce
Batman
bat#bat.com
Santosh
Bhaskar
santosh#santosh.com
The output after executing the script was:
Bruce
Batman
bat#bat.com
Bruce
Batman
bat#bat.com
Please explain me what I am missing here:
use strict;
use warnings;
my $file = 'BatmanFile.txt';
open my $info, $file or die "Could not open $file: $!";
my #resultarray;
while( my $line = <$info>) {
#print $line;
chomp $line;
my #linearray = split(":", $line);
push(#resultarray, #linearray);
print join("\n",$resultarray[0]),"\n";
print join("\n",$resultarray[1]),"\n";
print join("\n",$resultarray[2]),"\n";
}
close $info;
You are looping through file line by line. You have stored all lines (after splitting) in an array. Once the loop finishes you have all data in resultarray array, just print whole array after the loop (instead of printing just first 3 indexes which are you doing at the moment).
#!/usr/bin/perl
use strict;
use warnings;
my #resultarray;
while( my $line = <DATA>){
chomp $line;
my #linearray = split(":", $line);
push #resultarray, #linearray;
}
print "$_\n" foreach #resultarray;
__DATA__
Bruce:Batman:bat#bat.com
Santosh:Bhaskar:santosh#santosh.com
Demo
You can avoid all variables and do something like below
while(<DATA>){
chomp;
print "$_\n" foreach split(":");
}
One liner:
perl -e 'while(<>){chomp; push #result, split(":",$_);} print "$_\n" foreach #result' testdata.txt
When you do:
push(#resultarray, #linearray);
you're pushing #linearray into #resultarray at the end, so index 0 through 2 is still the items from the first time you pushed #linearray.
To overwrite #resultarray with the values from the second iteration, do:
#resultarray = #linearray;
instead.
Alternatively, use unshift to place #linearray at the start of #resultarray, as suggested by Sobrique:
unshift #resultarray, #linearray;
So, you just want to transliterate : to \n?
$ perl -pe 'tr/:/\n/' data.txt
Output:
Bruce
Batman
bat#bat.com
Santosh
Bhaskar
santosh#santosh.com
use strict;
use warnings;
my $file = 'BatmanFile.txt';
open my $info, $file or die "Could not open $file: $!";
my #resultarray;
while( my $line = <$info>) {
#print $line;
chomp $line;
my #linearray = split(":", $line);
#push(#resultarray, #linearray);
print join("\n",$linearray[0]),"\n";
print join("\n",$linearray[1]),"\n";
print join("\n",$linearray[2]),"\n";
}
close $info;

How to read array line in sequence from Input

I have some trouble
with array, how to read an array line in sequence from Input.
Here's my code.
sites.txt: (input file)
site1
site2
site3
program
#!/usr/bin/perl
my $file = 'sites.txt';
open(my $fh, '<:encoding(UTF-8)', $file)
or die "Couldn't open file !'$file' $!";
my #rows = <$fh>;
chomp #rows;
foreach my $site (#rows) {
$sitename = $site;
#domains = qw(.com .net .org);
foreach $dns (#domains){
$domain = $dns;
print "$site$dns\n";
}
}
and the output is like this
site1.com
site1.net
site1.org
site2.com
site2.net
site2.org
site3.com
site3.net
site3.org
I understand until that point,but i want make it the first element of array from #domains
reading until the end of Input line first,
then looping back to the 1st line of Input and going to the next element of array so
the output would be like this,
site1.com
site2.com
site3.com
site1.net
site2.net
site3.net
site1.org
site2.org
site3.org
it possible doing it? or need another module.sorry for basic question
I'll be really appreciated for the Answers.
thanks :)
You are iterating over all your sites and then (for each site) add the domain to the current site.
In pseudocode this is:
foreach site
foreach domain
print site + domain
Swap your loops so that the logic is
foreach domain
foreach site
print site + domain
Note that this is pseudocode, not Perl.
In "real" Perl this would look like:
#!/usr/bin/env perl
use strict;
use warnings;
my $file = 'sites.txt';
open( my $fh, '<:encoding(UTF-8)', $file )
or die "Couldn't open file !'$file' $!";
my #rows = <$fh>;
chomp #rows;
my #domains = qw(.com .net .org);
foreach my $dns (#domains) {
foreach my $site (#rows) {
print "$site$dns\n";
}
}
Output
site1.com
site2.com
site3.com
site1.net
site2.net
site3.net
site1.org
site2.org
site3.org
Please always include use strict; and use warnings; on top of your scripts. These two statements will show you the most common errors in your code.

Tie::File - Get 5 lines from a file into an array while removing them from file

I want to open a file, push 5 lines into an array for later use (or what is left if less than 5) and remove those 5 lines from the file as well.
It does not matter whether I am removing (or pushing) from head or tail of file.
I have used Tie::File in the past and am willing to use it, but I cannot figure it out with or without the Tie module.
use Tie::File;
my $limit='5';
$DataFile='data.txt';
###open my $f, '<', $DataFile or die;
my #lines;
tie (#lines, 'Tie::File', $DataFile);
$#lines = $limit;
###while( <#lines> ) {
shift #lines if #lines <= $limit;
push (#lines, $_);
###}
print #lines;
untie #lines;
Also tried File::ReadBackwards from an example I found but, I cannot figure out how to get the array of 5.
my $pos = do {
my $fh = File::ReadBackwards->new($DataFile) or die $!;
##lines =(<FILE>)[1..$limit];
#$fh->readline() for 1..$limit;
my $log_line = $fh->readline for 1..$limit;
print qq~ LogLine $log_line~;
$fh->tell()};
All that said, this came close, but no cigar. How do I get the 5 into an array?
use File::ReadBackwards;
my $num_lines = 5;
my $pos = do {
my $fh = File::ReadBackwards->new($DataFile) or die $!;
$fh->readline() for 1..$num_lines;
$fh->tell()};
truncate($DataFile, $pos)or die $!;
I will check each line in the array against a regex later on. They still need to be removed from the file either way.
If you extract the last five lines instead of the first five, then you can use truncate instead of writing the entire file. Furthermore, you can use File::ReadBackwards to get those five lines without reading the entire file. That makes the following solution insanely faster than Tie::File for large files (and it will use far less memory):
use File::ReadBackwards qw( );
my $num_lines = 5;
my $fh = File::ReadBackwards->new($DataFile)
or die("Can't open $DataFile: $!\n");
my #extracted_lines;
while ($_ = $fh->readline() && #extracted_lines < $num_lines) {
push #extracted_lines, $_;
}
truncate($fh->get_handle(), $fh->tell())
or die("Can't truncate $DataFile: $!\n");
This removes the first five lines of the data.txt file, stores them in another array and prints the removed lines on STDOUT:
use warnings;
use strict;
use Tie::File;
my $limit = 5;
my $DataFile = 'data.txt';
tie my #lines, 'Tie::File', $DataFile or die $!;
my #keeps = splice #lines, 0, $limit;
print "$_\n" for #keeps;
untie #lines;

Perl Hashes of Arrays and Some issues

I currently have a csv file that looks like this:
a,b
a,d
a,f
c,h
c,d
So I saved these into a hash such that the key "a" is an array with "b,d,f" and the key "c" is an array with "h,d"... this is what I used for that:
while(<$fh>)
{
chomp;
my #row = split /,/;
my $cat = shift #row;
$category = $cat if (!($cat eq $category)) ;
push #{$hash{$category}}, #row;
}
close($fh);
Not sure about the efficiency but it seems to work when I do a Data Dump...
Now, the issue I'm having is this; I want to create a new file for each key, and in each of those files I want to print every element in the key, as such:
file "a" would look like this:
b
d
f
<end of file>
Any ideas? Everything I've tried isn't working, I'm not too familiar / experienced with hashes...
Thanks in advance :)
The output process is very simple using the each iterator, which provides the key and value pair for the next hash element in a single call
use strict;
use warnings;
use autodie;
open my $fh, '<', 'myfile.csv';
my %data;
while (<$fh>) {
chomp;
my ($cat, $val) = split /,/;
push #{ $data{$cat} }, $val;
}
while (my ($cat, $values) = each %data) {
open my $out_fh, '>', $cat;
print $out_fh "$_\n" for #$values;
}
#!/usr/bin/perl
use strict;
use warnings;
my %foos_by_cat;
{
open(my $fh_in, '<', ...) or die $!;
while (<$fh_in>) {
chomp;
my ($cat, $foo) = split /,/;
push #{ $foos_by_cat{$cat} }, $foo;
}
}
for my $cat (keys %foos_by_cat) {
open(my $fh_out, '>', $cat) or die $!;
for my $foo (#{ $foos_by_cat{$cat} }) {
print($fh_out "$foo\n");
}
}
I wrote the inner loop as I did to show the symmetry between reading and writing, but it can also be written as follows:
print($fh_out "$_\n") for #{ $foos_by_cat{$cat} };

problem with the code in perl

My problem is that I am not able to figure out that why my code is taking each of the line from the file as one element of an array instead of taking the whole record starting from AD to SS as one element of the array. As you can see that my file is starting from AD and ending at SS which is same for all the followed lines in the data. But I want to make the array having elements starting from AD to SS which will be having all the lines in between AD to SS that is BC....,EG...., FA.....etc.not each line as an element. I tried my way and get the same file as such.Could anyone check my code. Thanks in advance.
AD uuu23
BC jjj
EG iii
FA vvv
SS
AD hhh25
BC kkk
EG ppp
FA aaa
SS
AD ttt26
BC xxx
FA rrr
SS
#!/usr/bin/env perl
use strict;
use warnings;
my $ifh;
my $line = '';
my #data;
my $ifn = "fac.txt";
open ($ifh, "<$ifn") || die "can't open $ifn";
my $a = "AD ";
my $b = "SS ";
my $_ = " ";
while ($line = <$ifh>)
{
chomp
if ($line =~ m/$a/g); {
$line = $_;
push #data, $line;
while ($line = <$ifh>)
{
$line .= $_;
push #data, $line;
last if
($line =~ m/$b/g);
}
}
push #data, $line; }
print #data;
If I understand correctly your problem, the fact is that the way you are reading the file:
while ($line = <$ifh>)
is inherently a line-by-line approach. It uses the content of the "line termination variable" ($/) to understand where to split lines. One easy way to change this behavior is un-defining the $/:
my $oldTerminator = $/;
undef $/;
....... <your processing here>
$/ = $oldTerminator;
so, your file would be just one line, but I am not sure what would happen of your code.
Another approach is the following (keeping in mind what I said about the fact that you are reading the file line-by-line): instead of doing
`push #data, $line;`
at each iteration of your loop, just accumulate the lines you read in a variable
$line .= $_;
(like you already do), and do the push only at the end, just once. Actually, this second approach will be more easily applicable to your code (you only have to remove the two push statements you have and put one outside of the loop).
I believe part of your problem is here
chomp
if ($line =~ m/$a/g);
it should be
chomp;
if ($line =~ m/$a/g)
otherwise the if statement is always executed. Please update your question if this has helped you advance
Here's a way to accomplish reading the records into an array, with newlines removed:
Code:
use strict;
use warnings;
use autodie;
my #data;
my $record;
my $file = "fac.txt";
open my $fh, '<', $file;
while (<$fh>) {
chomp;
if (/^AD /) { # new record starts
$record = $_;
while (<$fh>) {
chomp;
$record .= $_;
last if /^SS\s*/;
}
push #data, $record;
} else { die "Data outside record: $_" }
}
use Data::Dumper;
print Dumper \#data;
Output:
$VAR1 = [
'AD uuu23BC jjjEG iiiFA vvvSS',
'AD hhh25BC kkkEG pppFA aaaSS',
'AD ttt26BC xxxFA rrrSS'
];
This is another version, using the input record separator $/:
use strict;
use warnings;
use autodie;
my $file = "fac.txt";
open my $fh, '<', $file;
my #data;
$/ = "\nSS";
while (<$fh>) {
s/\n//g;
push #data, $_;
}
use Data::Dumper;
print Dumper \#data;
Produces the same output with this data. It does not care about the record start characters, only the end, which is SS at the beginning of a line.

Resources