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 |
|__________________________|___________________________|
Related
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";
}
}
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 |
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.
I want to apply a regex on lines where I have only 2 words. My file looks like this with variable numbers of spaces between the words in parentheses:
Politician_name:(Jose Maria Aznar | jose maria aznar | José María Aznar | josé maría aznar );
Politician_name:(Tony Blair | tony blair | Tony Blair | tony blair );
I want to have as an output:
Politician_name:(Tony Blair | tony blair | Tony Blair | tony blair | blair tony | Blair Tony);
My code applies the regex on each line and I get false output like this:
Politician_name:(Jose Maria Aznar | jose maria aznar | José María Aznar | josé maría aznar | maria jose | Maria Jose );
This is my code :
use strict;
use warnings;
use Data::Dumper;
use utf8;
open(IN, $ARGV[0]) or die "Can't read file $ARGV[0]\n";
while (my $line=<IN>)
{
my ($pol,$value) = split(/:/, $line);
warn Dumper \$pol;
chomp($value);
$value=~ s/[ ]+/ /g;
$value=~ s/\);//g;
my $n;
$n = $1 if ($value =~ /\((.+?)\|/);
$n=~ m/(\w*)\s(\w*)/g;
my $swapname="$2 $1";
warn Dumper \$swapname;
print "$pol: $value | $swapname );\n";
}
close(IN);
What do I need to do to stop handling 3-word names?
$n=~ m/(\w*)\s(\w*)/g; # Replace this regex with the one below
Use the below Regex to compare with $n and also you need to enclose it within an if, other wise your print will be executed for every input: -
my $n;
$n = $1 if ($value =~ /\((.+?)\|/);
if ($n =~ m/^\s*(\w+)\s(\w+)\s*$/g) { # Notice `$` to mark the end of 2 words..
my $swapname="$2 $1";
warn Dumper \$swapname;
print "$pol: $value | $swapname );\n";
}
But, you are not taking into account the next value after |.. You need to do that.. It is just taking the first value..
So, your output will be: -
Politician_name: (Tony Blair |tony blair | Tony Blair | tony blair | Blair Tony )
2nd tony blair is not getting used. You need to modify the code for that.
Actually you need a loop to iterate over every names, to make this code work properly.
UPDATE :- I would rather change your code to: -
# You should always use lexical variables as file handles..
open my $fh, '<', 'D:\demo.txt' or die $!;
while (<$fh>) # Don't need use any extra variable here.. Default to $_
{
my ($pol,$value) = split /:/; # Do split on $_ by default
warn Dumper \$pol;
chomp($value);
$value=~ s/[ ]+/ /g;
$value=~ s/\((.*)\);/$1/g;
my #name = split(/\|/, $value); # Split your string to an array
# Filter out array to remove duplicate
my $_ = $name[0];
if (m/^\s*(\w+)\s(\w+)\s*$/g) {
# If first element contains 2 words, proceed with rest of the elements
print "($value "; # print the original string you want..
# Append to it later on the reverse of other array elements
foreach (#name) {
if (m/^\s*(\w+)\s(\w+)\s*$/g) {
my $swapname = "$2 $1";
warn Dumper \$swapname;
print "| $swapname "; # Print swapnames after $value
}
}
print ");\n"; # End the string..
}
}
close($fh);
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.