Working with Spreadsheet::ParseExcel - perl

I am working with an Excel worksheet to fetch two columns and replace file names based on them.
This is how I get the values of two columns that I am interested in. the 14th column could be a single value or more than one separated by a comma.
my #required = (2,14);
my #value;
my #files = grep{ -f && -T && -M > 0 } glob("$dir/*");
my #expected_file = grep{ /Parsed/ } #files;
print "#expected_file\n";
if(! $workbook) {
die $parser->error(),"\n";
}
for my $row (1 .. $row_max) {
#value = map{
my $cell = $worksheets[0]->get_cell($row,$_);
$cell ? $cell->value() : '';
}#required;
my %hash_value = #value;
foreach my $key (keys %hash_value ){
my #suffix = split /[, ]/,$hash_value{$key};
push #{ $resample->{$key} },#suffix;
print $key . ":" .#suffix,"\n";
}
}
Output would be :
TY45745a:A,BTY45745a:C,DTY45745a:E,FTY5475a:G,HTY5475a:I,JTY5475a:K,L
Where TY45745a,TY5475a are the keys.What I would like to achieve is something like this : TY45745a A,B,C,D,E,F and TY5475a G,H,I,J,K,L.
And if the file names has [A-E] at the end of the file then it should be renamed to TY45745a[1..6], and if it has [G-L] TY5475a[1..6].
Could this grouping of suffix for a name could be done when fetching from the Excel sheet?
How should I do this ? Any suggestions or pointers will be helpful .

I assume that your table looks like:
| B (2) | N (14) |
|:--------:|:-------:|
| TY45745a | A,B |
| TY45745a | C,D |
| TY45745a | E,F |
| TY5475a | G,H |
| TY5475a | I,J |
| TY5475a | K,L |
You can do the first part
Where TY45745a,TY5475a are the keys.What I would like to achieve is something like this : TY45745a A,B,C,D,E,F and TY5475a G,H,I,J,K,L.
with the following code:
use strict;
use warnings;
use Spreadsheet::ParseExcel;
use Data::Dumper;
my $parser = Spreadsheet::ParseExcel->new();
my $book = $parser->Parse('Mappe1.xls') or die $parser->error(),"\n";
my $sheet = $book->{Worksheet};
my %hash;
for my $row (0 .. $sheet->[0]{MaxRow}) {
my $c2 = $sheet->[0]->get_cell($row, 2-1);
my $key = $c2 ? $c2->value() : '';
my $c14 = $sheet->[0]->get_cell($row, 14-1);
my #values = $c14 ? split(',', $c14->value()) : ();
push #{$hash{$key}}, #values;
}
print Dumper \%hash;
I added the missing parts to get the code running and simplified it a little
bit for demonstration purposes.

Related

Perl: Perl6::Form format

I have file something like this,
SR Name Rollno Class
1 Sanjay 01 B
2 Rahul_Kumar_Khanna 09 A
Now I need to add "|" between each. So it should look like
SR | Name |Rollno | Class|
1 | Sanjay |01 | B |
2 | Rahul_Kumar_Khanna|09 | A |
I am using Perl6::form
my $text;
foreach my $line (#arr) {
my ($SR, $Name, $Rollno, $Class) = split (" ", $line);
my $len = length $Name;
$text = form
'| {||||||||} | {||||||||} | {||||||||} | {||||||||}|',
$SR, $Name, $Rollno, $Class;
print $text;
}
Here till now I have done but the name is not comming out properly. I have add extra "|" in name for that. Is there any way we can add "|" by calculating length like(below). I tried but getting error.
'| {||||||||} | {||||||||}x$len | {||||||||} | {||||||||}|',
Problem #1
'| {||||||||} | {||||||||}x$len | {||||||||} | {||||||||}|'
produces
| {||||||||} | {||||||||}x20 | {||||||||} | {||||||||}|
but you're trying to get
| {||||||||} | {||||||||||||||||||||} | {||||||||} | {||||||||}|
For that, you'd want
'| {||||||||} | {'.( "|" x $len ).'} | {||||||||} | {||||||||}|'
Problem #2
$len is the length of the name field of the current row. It's different for every row. This is wrong, cause you want the output to be the same width for every row. $len needs to be the length of the longest name field.
You will need to find the correct value for $len before even starting the loop.
# Read in the data as an array of rows.
# Each row is an array of values.
my #rows = map { [ split ] } <>;
# Find the maximum width of each column.
my #col_lens = (0) x #{rows[0]};
for my $row (#rows) {
# Skip the blank line after the header.
next if !#$row;
for my $col_idx (0..$#$row) {
my $col_len = $row->[$col_idx];
if ($col_lens->[$col_idx] < $col_len) {
$col_lens->[$col_idx] = $col_len;
}
}
}
my $form =
join "",
"| ",
"{".( "|"x($col_lens[0]-2) )."}",
" | ",
"{".( "|"x($col_lens[1]-2) )."}",
" | ",
"{".( "|"x($col_lens[2]-2) )."}",
" | ",
"{".( "|"x($col_lens[3]-2) )."}",
" |";
for my $row (#rows) {
if (#$row) {
print form($form, #$row);
} else {
print "\n";
}
}

Perl: How to align different columns according to their size

I have file something like this,
SR Name Rollno Class
1 Sanjay 01 B
2 Rahul Kumar 09 A
Now I need to add "|" between each. So it should look like
SR | Name |Rollno | Class|
1 | Sanjay |01 | B |
2 | Rahul Kumar Khanna|09 | A |
I tried this,
sub alignment {
my ( $string ) = #_;
my $blk_len = 15; #Assuming some block size.
my $len = length $string;
my $right = $blk_len - $len;
my $string = $string . ( " " x $right );
return $string;
}
But the problem is if the name of the person is big I need to increase the block size so that the "|" could come properly.
The block size will apply to all and I am getting like this
SR | Name | Rollno | Class |
1 | Sanjay | 01 | B |
2 | Rahul Kumar Khanna | 09 | A |
UPDATE:
I am using Perl6::form
my $text;
foreach my $line (#arr) {
my ($SR, $Name, $Rollno, $Class) = split (" ", $line);
my $len = length $Name;
$text = form
'| {||||||||} | {||||||||} | {||||||||} | {||||||||}|',
$SR, $Name, $Rollno, $Class
print $text;
}
};
Here till now I have done but the name is not comming properly . I have add extra "|" in name for that. Is there any way we can add "|" by calculating length like(below) but geeting error.
{||||||}x$len
If that function is being applied to each field you will determine the maximum length for each column and keep an external record of that. That value, along with the field text would need to be feed to the alignment function (changes needed) and the expected right padding could then be produced.
Trying to change your code as little as possible:
#!/usr/bin/perl
my #lines;
my #max;
# Read file line by line.
while (my $line = <STDIN>) {
next if ($line =~ /^\s+?$/);
chomp($line);
my #fields;
# store lines, field by field
push #fields, split(/\s{2,}/, $line);
push #lines, \#fields;
# check for maximum fields length
for (my $i = 0; $i <= $#fields; ++$i) {
$max[$i] = length($fields[$i]) if ($#max < $i || $max[$i] < length($fields[$i]));
}
}
# Format each line
foreach my $line (#lines) {
for (my $i = 0; $i <= $#{$line}; ++$i) {
print alignment($$line[$i], $max[$i]) . "|";
}
print "\n";
}
# Your function with minimum changes
sub alignment {
# Added a new parameter
my ($string, $m_field_size) = #_;
# Determine the blk_len to use. The default value 15 would be better of as a Constant.
my $blk_len = ($m_field_size > 15 ? $m_field_size : 15); #Assuming some block size
my $len = length $string;
my $right = $blk_len - $len;
my $string = $string . ( " " x $right );
return $string;
}
This is all it is needed.
Please study following approach if it fits your task
extend string to max length
split into array based on field lengths
join array element with separator
use strict;
use warnings;
use feature 'say';
my $length = 32;
while( my $str = <DATA> ) {
chomp $str;
if ( $str eq '' ) {
say $str;
} else {
$str .= ' ' for length($str)..$length;
say join '| ', unpack 'a5a13a9a6a', $str;
}
}
# 1 2 3 4
#1234567890123456789012345678901234567890
__DATA__
SR Name Rollno Class
1 Sanjay 01 B
2 Rahul Kumar 09 A
Output
SR | Name | Rollno | Class |
1 | Sanjay | 01 | B |
2 | Rahul Kumar | 09 | A |

Using Tree::DAG_Node to print list heap into tree-format

To make it simple, I am trying to get this heap to print in a tree like format. It's close but I know I am missing stuff, but I just can't wrap my head around this module. I know there is tree::simple and I think just Tree? But I can't really find any tutorials on how to actually use with a list or a array. The heap sort is right, cause it's sorting the list after the tree has been posted but I can't figure how to draw the tree correctly, then again outputting has never been my strong suit on any language. I think it's not grabbing the data from the file? least that is my idea but i'm not confident enough to be sure. here is my code so far.
#!/usr/bin/perl
use 5.006;
use strict;
use warnings;
use Tree::DAG_Node;
process_data(read_file('data.txt'));
process_data((3,1,4,1,5,9,2,6,5,3,6));
sub read_file{
my($filename)=#_;
my #data=();
my #words;
open(my $fh, "<", $filename)
or die "Could not open file: $!\n";
while(<$fh>){
chomp;
#words = split(' ');
foreach my $word(#words) {
push #data, $word;
}
}
close $fh;
return #data;
}
sub heap_sort {
my ($a) = #_;
my $n = #$a;
for (my $i = ($n - 2) / 2; $i >= 0; $i--) {
down_heap($a, $n, $i);
}
for (my $i = 0; $i < $n; $i++) {
my $t = $a->[$n - $i - 1];
$a->[$n - $i - 1] = $a->[0];
$a->[0] = $t;
down_heap($a, $n - $i - 1, 0);
}
}
sub down_heap {
my ($a, $n, $i) = #_;
while (1) {
my $j = max($a, $n, $i, 2 * $i + 1, 2 * $i + 2);
last if $j == $i;
my $t = $a->[$i];
$a->[$i] = $a->[$j];
$a->[$j] = $t;
$i = $j;
}
sub max {
my ($a, $n, $i, $j, $k) = #_;
my $m = $i;
$m = $j if $j < $n && $a->[$j] > $a->[$m];
$m = $k if $k < $n && $a->[$k] > $a->[$m];
return $m;
}
}
sub draw_tree{
my(#data)=#_;
my $root = Tree::DAG_Node->new;
$root->name($_[0]);
$root->new_daughter->name($_) for ('1'..'10');
my #names = #data;
my $count =0;
for my $n ($root->daughters) {
for (split //, $names[$count++]) {
$n->new_daughter->name($_)
}
}
print map "$_\n", #{$root->draw_ascii_tree};
}
sub process_data{
my(#data)=#_;
my #a = #data;
print "#a\n";
print "\n";
heap_sort(\#a);
print "\n";
print "#a\n";
print "\n";
draw_tree(#a);
}
and here is the output I am getting so far.
10,4,5,2,1,7
Use of uninitialized value in split at HEAPSORTtree.pl line 77.
Use of uninitialized value in split at HEAPSORTtree.pl line 77.
Use of uninitialized value in split at HEAPSORTtree.pl line 77.
Use of uninitialized value in split at HEAPSORTtree.pl line 77.
Use of uninitialized value in split at HEAPSORTtree.pl line 77.
Use of uninitialized value in split at HEAPSORTtree.pl line 77.
Use of uninitialized value in split at HEAPSORTtree.pl line 77.
Use of uninitialized value in split at HEAPSORTtree.pl line 77.
Use of uninitialized value in split at HEAPSORTtree.pl line 77.
|
<10,4,5,2,1,7>
/---------------------------------------+--
-+---+---+---+---+---+---+---\
| |
| | | | | | | |
<1> <2>
<3> <4> <5> <6> <7> <8> <9> <10>
/-----------------+-----------------+---+---+---+---+---+---+---+---+---\
| | | | | | | | | | | |
<1> <Tree::DAG_Node=HASH(0x4b32dc)> <,> <4> <,> <5> <,> <2> <,> <1> <,> <7>
10,4,5,2,1,7
3 1 4 1 5 9 2 6 5 3 6
|
<1>
/---+---+---+---+---+---+---+---+---\
| | | | | | | | | |
<1> <2> <3> <4> <5> <6> <7> <8> <9> <10>
| | | | | | | | | |
<1> <1> <2> <3> <3> <4> <5> <5> <6> <9>
1 1 2 3 3 4 5 5 6 9 6
Press any key to continue . . .
the output I want is similar to this
|
<root>
/-------+-------+-------\
| | | |
<1> <d> <e> <f>
/---+---\ |
| | | <3>
<a> <b> <c> /---+---\
| | |
<g> <h> <i>

Get Multiple IP addresses from one variable using Perl to print a list

I need the script to print out the list of IP addresses line by line with the corresponding username and email address and the country. How do I get the multiple IP addresses to execute a command? I tried doing a loop but it only showed me one line of IP addresses. I would like my output to look like:
1 | login | emailadd#yahoo.com | 160.79.208.82 | United States
16 | login1 | emailadd#yahoo.com | 61.95.83.10 | Italy
23 | login2 | emailadd#gmail.com | 81.48.63.93 | Australia
36 | login3 | emailadd#yahoo.com | 38.117.170.82 | Japan
51 | login4 | emailadd#gmail.com | 2.233.30.85 | Mexico
Here is my code:
#!/usr/bin/perl -w
use lib '~/lib';
use strict;
use Net::IPInfoDB;
my $g = Net::IPInfoDB->new;
$g->key(api_key);
my $login = '1 | login | emailadd#yahoo.com | 160.79.208.82
16 | login1 | emailadd#yahoo.com | 61.95.83.10
23 | login2 | emailadd#gmail.com | 81.48.63.93
36 | login3 | emailadd#yahoo.com | 38.117.170.82
51 | login4 | emailadd#gmail.com | 2.233.30.85';
$login =~ /(\d+\.\d+\.\d+\.\d+)/;
my $city = $g->get_city("$1");
my $addr = $g->get_country("$1");
printf "$login | (%s, %s)\n",
$city->city_name, $addr->country_name;
If you want to stick to not using the array, here's a solution to getting the IP addresses.
while ($login =~ m/(\d+\.\d+\.\d+\.\d+)/g) {
print "$1\n";
}
Use /g to find all matches.
my #ips = /(\d+\.\d+\.\d+\.\d+)/g;
That said, you obviously want the 4th field, so let's actually do that rather than risking getting something from the third field.
sub trim { my ($s) = #_; $s =~ s/^\s+//; $s =~ s/\s+\z//; $s }
for (split /^/m, $login) {
chomp;
my #fields = map trim($_), split /\|/;
my $ip = $fields[2];
...
}
You are getting only one IP address, because it is exactly what you are doing by applying the regex ONCE on the whole $login.
#we split $login into an array, line-by-line
my #lines = split("\n",$login);
for my $line (#lines) {
#now we iterate through every line one-by-one
$line =~ /(?<ip>\d+\.\d+\.\d+\.\d+)/;
print $+{"ip"}."\n";
}
Here we iterated through every line on $login and we applied the regex for each line individually..Instead of printing ofc you can do whatever you want with that ip.
Also I'm using here named match, which is only my preference, you don't have to use it.

Perl create a formatted table without using an object

I want to simply output some results in a table without there being any offset problems. Do not worry about the foreach and the output of values that is just pseudocode for clarity.
print "\n ______________________________________________________";
print "\n | |";
print "\n | Title |";
print "\n +______________________________________________________+";
print "\n | | |";
print "\n | City | Size |";
print "\n |__________________________|___________________________|";
#Sort by highest scores
################################
foreach (city, size)
{
print "\n | (city(value)";
print "| (size(value)";
}
Any Ideas?
It's rarely used anymore, but Perl has the built in ability to create these type of forms.
Basically, you use a specification to state how you want these tables formatted, and where information in these tables will be placed using the format statement. Then, you use the Perl write statement to write to that format. You can specify headers and footers of your tables too.
I suggest you use
substr
to overwrite the correct portion of a template line.
use strict;
use warnings;
my %data = (
Birmingham => 1_000_000,
Bristol => 430_000,
Manchester => 110_000,
);
print " ______________________________________________________\n";
print " | |\n";
print " | Title |\n";
print " +______________________________________________________+\n";
my $template =
" | | |\n";
print $template;
while (my ($city, $size) = each %data) {
my $line = $template;
substr $line, 12, length $city, $city;
substr $line, 39, length $size, $size;
print $line;
}
print " |__________________________|___________________________|\n";
output
______________________________________________________
| |
| Title |
+______________________________________________________+
| | |
| Bristol | 430000 |
| Manchester | 110000 |
| Birmingham | 1000000 |
|__________________________|___________________________|