perl + create loop to print values from INI file - perl

My name is abbi
My first perl script run on linux machine
This script read the INI file called (input) and print the values of val , param , name .....
How to create loop that print values of val1-valn OR loop to print values of param1-paramn... etc? (in place the print command's in the script )
the loop must have option to match the parameter
for example print only param1 until paramn values
n - Is the last number of each param
#!/usr/bin/perl
open(IN,"input") or die "Couldn't open input: $!\n";
while(<IN>) {
chomp;
/^([^=]+)=(.*)$/;
$config{$1} = $2;
}
close(IN);
print $config{val1};
print $config{val2};
print $config{val3};
print $config{param1};
print $config{param2};
print $config{param3};
print $config{name1};
.
.
.
.
example of the ini file from linux machine
cat input
val1=1
val2=2
val3=3
param1=a
param2=b
param3=c
name1=abbi
name2=diana
name3=elena

You can use Config::Tiny to read your .ini file.
Then you can use the returned hash to filter what you want.

According to your last comment, this will do what you want:
use strict;
use warnings;
my %config;
my $max_n = 0;
my $input = 'input';
open my $in, '<', $input
or die "unable to open '$input' for reading: $!";
while (<$in>) {
chomp;
if (/^(.*?(\d+))\s*=(.*)$/) {
$config{$1} = $3;
$max_n = $2 if $2 > $max_n;
}
}
close $in or die "unable to close '$input': $!";
for my $n(1..$max_n) {
for my $param (qw/val param/) {
print "$param.$n = $config{$param.$n}\n" if exists $config{$param.$n};
}
}

How about this:
use warnings;
use strict;
my %config;
open my $input, "<", "input"
or die "Couldn't open input: $!\n";
while(<$input>) {
chomp;
if ( /^([^=]+)=(.*)$/) {
$config{$1} = $2;
}
}
close($input) or die $!;
for (sort keys %config) {
if (/param\d+/) {
print "$config{$_}\n";
}
}

Related

Perl: How to print a random section (word definition) from a dictionary file

I want to print a random new word English in dictionary file in terminal Unix by Perl. I want to select and print a random line and 2 follow lines.
But my code doesn't complete this work.
Please help me to improve it.
An example of the output I wish:
#inspire: ....
ghk
lko...
Dictionary file:
#inspiration: mean....
abc def...
ghk lmn
...
#inspire: ....
ghk
lko...
#people: ...
...
The complete dictionary file is here anhviet109K.txt. It's about 14MB
My code:
use strict;
use warnings;
use File::Copy qw(copy move);
my $files = 'anhviet109K.txt';
my $fh;
my $linewanted = 16 + int( rand( 513796 - 16 ) );
# 513796: number of lines of file dic.txt
open( $fh, "<", $files ) or die "cannot open < $fh: $!";
my $del = " {2,}";
my $temp = 0;
my $count = 0;
while ( my $line = <$fh> ) {
if ( ( $line =~ "#" ) && ( $. > $linewanted ) ) {
$count = 4;
}
else {
next;
}
if ( $count > 0 ) {
print $line;
$count--;
}
else {
last;
}
}
close $fh;
Something like this, perhaps?
Your data has helped me to exclude the header entries in your dictionary file
This program finds the location of all of the entries (lines beginning with #) in the file, then chooses one at random and prints it
Tốt học tiếng Anh may mắn
use strict;
use warnings 'all';
use Fcntl ':seek';
use constant FILE => 'anhviet109K.txt';
open my $fh, '<', FILE or die qq{Unable to open "#{[FILE]}" for input: $!};
my #seek; # Locations of all the definitions
my $addr = tell $fh;
while ( <$fh> ) {
push #seek, $addr if /^\#(?!00-)/;
$addr = tell $fh;
}
my $choice = $seek[rand #seek];
seek $fh, $choice, SEEK_SET;
print scalar <$fh>;
while ( <$fh> ) {
last if /^\#/;
print;
}
output
#finesse /fi'nes/
* danh từ
- sự khéo léo, sự phân biệt tế nhị
- mưu mẹo, mánh khoé
* động từ
- dùng mưu đoạt (cái gì); dùng mưu đẩy (ai) làm gì; dùng mưu, dùng kế
=to finesse something away+ dùng mưu đoạt cái gì
A single pass approach:
use strict;
use warnings;
use autodie;
open my $fh, '<:utf8', 'anhviet109K.txt';
my $definition = '';
my $count;
my $select;
while (my $line = <$fh>) {
if ($line =~ /^#(?!00-)/) {
++$count;
$select = rand($count) < 1;
if ($select) {
$definition = $line;
}
}
elsif ($select) {
$definition .= $line;
}
}
# remove blank line that some entries have
$definition =~ s/^\s+\z//m;
binmode STDOUT, ':utf8';
print $definition;
This iterative random selection always selects the first item, has a 1/2 chance of replacing it with the second item, a 1/3 for the third, and so on.

Comparing FILE1 value to FILE2 range and printing matches

I'm very new to Perl and am working on a Bioinformatics project at University. I have FILE1 containing a list of positions, in the format:
99269
550
100
126477
1700
And FILE2 in the format:
517 1878 forward
700 2500 forward
2156 3289 forward
99000 100000 forward
22000 23000 backward
I want to compare every position in FILE1 to every range in values on FILE2, and if a position falls into one of the ranges then I want to print the position, range and direction.
So my expected output would be:
99269 99000 100000 forward
550 517 1878 forward
1700 517 1878 forward
Currently it will run with no errors, however it doesn't output any information so I am unsure where I am going wrong! When I split the final 'if' rule it runs but will only work if the position is on exactly the same line as the range.
My code is as follows:
#!/usr/bin/perl
use strict;
use warnings;
my $outputfile = "/Users/edwardtickle/Documents/CC22CDS.txt";
open FILE1, "/Users/edwardtickle/Documents/CC22positions.txt"
or die "cannot open > CC22: $!";
open FILE2, "/Users/edwardtickle/Documents/CDSpositions.txt"
or die "cannot open > CDS: $!";
open( OUTPUTFILE, ">$outputfile" ) or die "Could not open output file: $! \n";
while (<FILE1>) {
if (/^(\d+)/) {
my $CC22 = $1;
while (<FILE2>) {
if (/^(\d+)\s+(\d+)\s+(\S+)/) {
my $CDS1 = $1;
my $CDS2 = $2;
my $CDS3 = $3;
if ( $CC22 > $CDS1 && $CC22 < $CDS2 ) {
print OUTPUTFILE "$CC22 $CDS1 $CDS2 $CDS3\n";
}
}
}
}
}
close(FILE1);
close(FILE2);
I have posted the same question on Perlmonks.
Because you are only reading FILE2 once it is only compared with the first line of FILE1
Subsequent lines are compared with the closed file
Stash the lines from FILE1 in an array and then compare each line in FILE2 with each array entry, as shown below
#!/usr/bin/perl
use strict;
use warnings;
my $outputfile = "out.txt";
open FILE1, "file1.txt"
or die "cannot open > CC22: $!";
open FILE2, "file2.txt"
or die "cannot open > CDS: $!";
open( OUTPUTFILE, ">$outputfile" ) or die "Could not open output file: $! \n";
my #file1list = ();
while (<FILE1>) {
if (/^(\d+)/) {
push #file1list, $1;
}
}
while (<FILE2>) {
if (/^(\d+)\s+(\d+)\s+(\S+)/) {
my $CDS1 = $1;
my $CDS2 = $2;
my $CDS3 = $3;
for my $CC22 (#file1list) {
if ( $CC22 > $CDS1 && $CC22 < $CDS2 ) {
print OUTPUTFILE "$CC22 $CDS1 $CDS2 $CDS3\n";
}
}
}
}
( there are also stylistic issues with the program (like capital letters for variables) but I've ignored these, it's quite a nice program for a beginner)
I thought I could simplify some of that by using split instead of regex, but I think my code is actually longer and more difficult to read! In any event, remember that split works great for problems like this:
# User config area
my $positions_file = 'input_positions.txt';
my $ranges_file = 'input_ranges.txt';
my $output_file = 'output_data.txt';
# Reading data
open my $positions_fh, "<", $positions_file;
open my $ranges_fh, "<", $ranges_file;
chomp( my #positions = <$positions_fh> );
# Store the range data in an array containing hash tables
my #range_data;
# to be used like $range_data[0] = {start => $start, end => $end, dir => $dir}
while (<$ranges_fh>) {
chomp;
my ( $start, $end, $dir ) = split; #splits $_ according to whitespace
push #range_data, { start => $start, end => $end, dir => $dir };
#print "start: $start, end: $end, direction: $dir\n";
} #/while
close $positions_fh;
close $ranges_fh;
# Data processing:
open my $output_fh, ">", $output_file;
#It feels like it should be more efficient to process one range at a time for all data points
foreach my $range (#range_data) { #start one range at a time
#each $range = $range_data[#] = { hash table }
foreach my $position (#positions) { #check all positions
if ( ( $range->{start} <= $position ) and ( $position <= $range->{end} ) ) {
my $output_string = "$position " . $range->{start} . " " . $range->{end} . " " . $range->{dir} . "\n";
print $output_fh $output_string;
} #/if
} #/foreach position
} #/foreach range
close $output_fh;
This code would probably run faster if the data processing was done during the while loop that's reading the range data.
Your bug was because you were embedding file processing, so your inner loop only went through the file's contents a single time and then was stuck at eof.
The easiest solution is just to load the inner loop file entirely into memory first.
The following demonstrates using more Modern Perl techniques:
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
my $cc22file = "/Users/edwardtickle/Documents/CC22positions.txt";
my $cdsfile = "/Users/edwardtickle/Documents/CDSpositions.txt";
my $outfile = "/Users/edwardtickle/Documents/CC22CDS.txt";
my #ranges = do {
# open my $fh, '<', $cdsfile; # Using Fake Data instead below
open my $fh, '<', \ "517 1878 forward\n700 2500 forward\n2156 3289 forward\n99000 100000 forward\n22000 23000 backward\n";
map {[split]} <$fh>;
};
# open my $infh, '<', $cc22file; # Using Fake Data instead below
open my $infh, '<', \ "99269\n550\n100\n126477\n1700\n";
# open my $outfh, '>', $outfile; # Using STDOUT instead below
my $outfh = \*STDOUT;
CC22:
while (my $cc22 = <$infh>) {
chomp $cc22;
for my $cds (#ranges) {
if ($cc22 > $cds->[0] && $cc22 < $cds->[1]) {
print $outfh "$cc22 #$cds\n";
next CC22;
}
}
# warn "$cc22 No match found\n";
}
Outputs:
99269 99000 100000 forward
550 517 1878 forward
1700 517 1878 forward
Live Demo

Perl - binary unpack using pointer and index

I have a binary file that contain 3 files, a PNG, a PHP and a TGA file.
Here the file to give you the idea : container.bin
the file is build this way:
first 6 bytes are a pointer to the index, in this case 211794
Then you have all 3 files stacked one after the other
and at the ofset 211794, you have the index, that tell you where the file start and end
in this example you have:
[offset start] [offset end] [random data] [offset start] [name]
6 15149 asdf 6 Capture.PNG
15149 15168 4584 15149 index.php
15168 211794 12 15168 untilted.tga
meaning that capture.png start at offset 6, finish at offset 15149, then asdf is a random data, and the start offset is repeated again.
Now what I want to do is a perl to separate the file on this binary files.
The perl need to check the first 6 offset of the file (header), then jump to the index location, and use the list to extract the file out.
A mix of seek and read can be used to achieve the task:
#!/usr/bin/env perl
use strict;
use warnings;
use Fcntl 'SEEK_SET';
sub get_files_info {
my ( $fh, $offset ) = #_;
my %file;
while (<$fh>) {
chomp;
my $split_count = my ( $offset_start, $offset_end, $random_data, $offset_start_copy,
$file_name ) = split /\s/;
next if $split_count != 5;
if ( $offset_start != $offset_start_copy ) {
warn "Start of offset mismatch: $file_name\n";
next;
}
$file{$file_name} = {
'offset_start' => $offset_start,
'offset_end' => $offset_end,
'random_data' => $random_data,
};
}
return %file;
}
sub write_file {
my ( $fh, $file_name, $file_info ) = #_;
seek $fh, $file_info->{'offset_start'}, SEEK_SET;
read $fh, my $contents,
$file_info->{'offset_end'} - $file_info->{'offset_start'};
open my $fh_out, '>', $file_name or die 'Error opening file: $!';
binmode $fh_out;
print $fh_out $contents;
print "Wrote file: $file_name\n";
}
open my $fh, '<', 'container.bin' or die "Error opening file: $!";
binmode $fh;
read $fh, my $offset, 6;
seek $fh, $offset, SEEK_SET;
my %file = get_files_info $fh, $offset;
for my $file_name ( keys %file ) {
write_file $fh, $file_name, $file{$file_name};
}
The only real difficulty here is to make sure that both input and output files are read in binary mode. This can be achieved by using the :raw PerlIO layer when the files are opened.
This program seems to do what you want. It first locates and reads the index block into a string, and then opens that string for input and reads the start and end position and name of each of the constituent files. Thereafter processing each file is simple.
Be aware that unless the formatting of the index block is more strict than you say, you can rely only on the first, second, and last whitespace-separated fields on each line since random text could contain spaces. There is also no way to specify a file name containing spaces.
The output, using Data::Dump, is there to demonstrate correct functionality and is not necessary for the functioning of the program.
use v5.10;
use warnings;
use Fcntl ':seek';
use autodie qw/ open read seek close /;
open my $fh, '<:raw', 'container.bin';
read $fh, my $index_loc, 6;
seek $fh, $index_loc, SEEK_SET;
read $fh, my ($index), 1024;
my %contents;
open my $idx, '<', \$index;
while (<$idx>) {
my #fields = split;
next unless #fields;
$contents{$fields[-1]} = [ $fields[0], $fields[1] ];
}
use Data::Dump;
dd \%contents;
for my $file (keys %contents) {
my ($start, $end) = #{ $contents{$file} };
my $size = $end - $start;
seek $fh, $start, SEEK_SET;
my $nbytes = read $fh, my ($data), $size;
die "Premature EOF" unless $nbytes == $size;
open my $out, '>:raw', $file;
print { $out } $data;
close $out;
}
output
{
"Capture.PNG" => [6, 15149],
"index.php" => [15149, 15168],
"untilted.tga" => [15168, 211794],
}

Perl while loops and reading lines

Each record has 4 lines:
Like the following:
#NCYC361­11a03.q1k bases 1 to 1576
GCGTGCCCGAAAAAATGCTTTTGGAGCCGCGCGTGAAAT
+
!)))))****(((***%%((((*(((+,**(((+**+,­
There are two files in which 1 file corresponded to the other
there are an array of seqeunces A1
So read 1 record at a time from file 1. read record from file 2. if the sequence in record 1 file 1 (line 2) matches the seuqnece in the array A1, i print the record from file 2 to an output file so on...but the point is i need to read a record at a time.... how would i break out of the inner loop so that i can read the next record from the file 1 and then compare it to the next record in file 2
If you ask about controlling nested loops you can do that with labels.
Example:
OUTER:
while(<>){
for(#something){
last OUTER;
}
}
See last for example.
In case only lines with same number could ever match, you don't really need more than one loop. You can call reading operation (<>, read, sysread) wherever you want. It only usually placed directly in loop because it conveniently returns undef and breaks it when work is done.
while(defined(my $first_line = <FIRST>)){
my $second_line = <SECOND>;
if($first_line eq $second_line){
print "match\n";
} else {
print "no match\n";
}
}
From your sentence I need to check if the sequence matches any with the sequence from the second I gather that you want to check whether any lines in the two files match?
If you need to read a file several times then you can use seek to rewind to the start of it without reopening it.
This program shows the idea.
use strict;
use warnings;
open my $fh1, '<', 'file1' or die $!;
open my $fh2, '<', 'file2' or die $!;
open my $out, '>', 'matches' or die $!;
while (my $line1 = <$fh1>) {
seek $fh2, 0, 0;
while (my $line2 = <$fh2>) {
if ($line1 eq $line2) {
print $out $line1;
last;
}
}
}
Edit
Your comment has changed the problem. Both files have four-line records and you want to compare the second line in corresponding records across the two files.
use strict;
use warnings;
open my $fh1, '<', 'file1' or die $!;
open my $fh2, '<', 'file2' or die $!;
open my $match, '>', 'matches' or die $!;
open my $nomatch, '>', 'nomatch' or die $!;
while (1) {
my (#data1, #data2);
for (1 .. 4) {
my $line;
$line = <$fh1>;
push #data1, $line if defined $line;
$line = <$fh2>;
push #data2, $line if defined $line;
}
last unless #data1 == 4 and #data2 == 4;
if ($data1[1] eq $data2[1]) {
print $match #data2;
}
else {
print $nomatch #data2;
}
}
A full example :
#!/usr/bin/env perl
use strict;
use warnings;
open F1, "<", "/path/1";
open F2, "<", "/path/2";
#a1 = <F1>;
#a2 = <F2>;
for (0..$#a1) {
if ($a1[$_] eq $a2[$_]) {
print "MATCH line [$_]\n";
} else {
print "DOESN'T MATCH line [$_]\n";
}
}

how to compare a hash key of a one file with a hash value of another in perl

I have two files. one file only contains key and another has key and value both. how could i compare a key of one file with value of another?
example of file1
steve
robert
sandy
alex
example of file2
age25, steve
age29, alex
age30, mindy
age50, rokuna
age25, steve
example of output
age25, steve
age29, alex
Here is what i have so far
my $age_name="file1.txt";
my $name="file2.txt";
open my $MYFILE, "<", $name or die "could not open $name \n";
open my $MYFILE2, "<", $age_name or die "could not open $age_name \n";
while(<$MYFILE>) {
my ($key, $value) = split(",");
my $secondfile = <$MYFILE2>;
if ( defined $secondfile ) {
my ($key2, $value2) = split(",");
if ($value2=~m/$key/) {
print "$key2 - $value2 \n";
}
}
}
close $MYFILE;
close $MYFILE2;
You are reading one line from the first file and one line from the second line. The problem is the lines do not have to be related. The classical solution is to read one file into a hash and then use the hash for lookup while reading the second one:
#!/usr/bin/perl
use strict;
use warnings;
my %age_of;
open my $AGE, '<', 'file2.txt' or die $!;
while (<$AGE>) {
chomp;
my ($age, $name) = split /, /;
$age_of{$name} = $age;
}
open my $NAME, '<', 'file1.txt' or die $!;
while (<$NAME>) {
chomp;
print "$age_of{$_}, $_\n" if exists $age_of{$_};
}