I want to output strings into eight columns, but I want to keep the spacing the same. I don't want to do it in HTML, but I am not sure how to do it normally. Example:
Something Something Something Something Something
Else Else Else Else Else
Another Another Another Another Another
The amount of rows will change daily, but the column number will always stay the same. What is the best way to do this?
printf
printf "%-11s %-11s %-11s %-11s %-11s %-11s %-11s %-11s\n",
$column1, $column2, ..., $column8;
Change "11" in the template to whatever value you need.
You could use Perl's format.
This is probably the "complicated" method that you don't understand, most likely because it gives you many options (left|center|right justification/padding, leading 0's, etc).
Perldoc Example:
Example:
format STDOUT =
#<<<<<< #|||||| #>>>>>>
"left", "middle", "right"
.
Output:
left middle right
Here's another tutorial.
Working Example: (Codepad)
#!/usr/bin/perl -w
use strict;
sub main{
my #arr = (['something1','something2','something3','something4','something5','something6','something7','something8']
,['else1' ,'else2' ,'else3' ,'else4' ,'else5' ,'else6' ,'else7' ,'else8' ]
,['another1' ,'another2' ,'another3' ,'another4' ,'another5' ,'another6' ,'another7' ,'another8' ]
);
for my $row (#arr) {
format STDOUT =
#<<<<<<<<<<<< #<<<<<<<<<<<< #<<<<<<<<<<<< #<<<<<<<<<<<< #<<<<<<<<<<<< #<<<<<<<<<<<< #<<<<<<<<<<<< #<<<<<<<<<<<<
#$row
.
write;
}
}
main();
Here is a live example of Perl6::Form:
#!/usr/bin/perl
use Perl6::Form;
my #arr = (
[1..8],
[9..16],
[17..24],
);
foreach my $line (#arr) {
print form
"{<<<<<} "x8,
#{$line};
}
It will output:
1 2 3 4 5 6 7 8
9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
I would look at formatting, but I would do it using Perl 6's (now Raku) Form.pm, which you can obtain as Perl6::Form for Perl 5.
The reason for this is that the format builtin has a number of drawbacks, such as having the format statically defined at compile time (i.e., building it dynamically can be painful and usually requires string eval), along with a whole list of other shortcomings, such as lack of useful field types (and you can't extend these in Perl 5).
Related
I'm not able to apply perl range operator over an input taken from Standard Input. Here's the code:
#!/usr/bin/perl
#range = (1..5, 6, 7, 8..10);
print "#range\n";
print "Enter your range. Esc by CTRL+D.\n";
chomp(#user_range = <STDIN>); # input 1..5<LF> 6<LF> 7..10<LF> CTRL+D
print "#user_range\n";
The first block of code (#range) works fine and outputs: 1 2 3 4 5 6 7 8 9 10;
In the second (#user_range) I input 1..5 6 7..10 CTRL+D. The output I get is 1..5 6 7..10 instead of 1 2 3 4 5 6 7 8 9 10.
Please help.
Anything you read from STDIN or any other filehandle comes in as just a string. This is a basic security measure, you don't want your programming language evaluating input as code.
You could run it through eval, but that's a security hole; it lets users run arbitrary code. Don't use eval STRING unless you know what you're doing. eval BLOCK is fine, it's totally different.
Instead, if you want something done to input you'll have to do it yourself. If it's just simple numbers, then it can be fairly straightforward.
use 5.010;
use strict;
use warnings;
# Read the string from input and remove the newline.
my $list_string = <STDIN>;
chomp $list_string;
# Split it into a list on commas.
# "1..5, 6, 7..10" becomes "1..5", "6", "7..10".
my #list = split /\s*,\s*/, $list_string;
# Go through each element checking for a range operator.
# If there's a range operator, replace it with the range.
# Otherwise leave it alone.
#list = map { range_transform($_) } #list;
print join ", ", #list;
sub range_transform {
my $string = shift;
# Match the X..Y. If it doesn't match, just return it.
return $string unless $string =~ m{^(\d+)\.\.(\d+)$};
# Perform the range operation.
return $1..$2;
}
This doesn't have any error checking, it should probably also check that the input is either a number OR a range operator and puke if it's anything else. But that should get you started.
I'm new to Perl and need help with sorting using the hash and/or any other possible method this can be done in Perl.
I've an input file like below and would like to generate the output file as shown.
I'm thinking if this can be done by putting it in hash and then comparing? Please also provide an explanations to the steps for the learning purpose if possible.
If the file has duplicate/triplicate entries matching with different timestamp, it should only list the latest time stamp entry.
Input file
A May 19 23:59:14
B May 19 21:59:14
A May 22 07:59:14
C Apr 10 12:23:00
B May 11 10:23:34
The output should be
A May 22 07:59:14
B May 19 21:59:14
C Apr 10 12:23:00
You can try to use your data(A,B etc) as key and timestamp as value in perl hash.
Then read input file and compare timestamps using perl time datatype. This way you keep only latest entries and other can be discarded. Print result at the end.
A hash is good for coalescing duplicates.
However sorting by time stamp requires converting the 'text' representation to an actual time. Time::Piece is one of the better options for doing this
#!/usr/local/bin/perl
use strict;
use warnings;
use Time::Piece;
my %things;
while (<DATA>) {
my ( $letter, $M, $D, $T ) = split;
my $timestamp =
Time::Piece->strptime( "$M $D $T 2015", "%b %d %H:%M:%S %Y" );
if ( not defined $things{$letter}
or $things{$letter} < $timestamp )
{
$things{$letter} = $timestamp;
}
}
foreach my $thing ( sort keys %things ) {
print "$thing => ", $things{$thing}, "\n";
}
__DATA__
A May 19 23:59:14
B May 19 21:59:14
A May 22 07:59:14
C Apr 10 12:23:00
B May 11 10:23:34
Note though - your timestamps are ambiguous because they omit the year. You have to deal with this some way. I've gone for the easy road of just inserting 2015. That's not good practice - at the very least you should use some way of discovering 'current year' automatically - but bear in mind that at some points in the year, this will Just Break.
You can format output date using the strftime method within Time::Piece - this is merely the default output.
I'm trying to display output in table format but having some trouble.
I have a few variables that contain strings separated by spaces for example:
$var1 = "1 1003 33 40 9948";
$var2 = "2";
I want the table to look like this:
I want to display the table such that it looks like this:
Column1 Column2
======= =======
1 2
1003
33
40
9948
I want the contents of var1 to wrap down for each value.
I'm able to display the header no problem. I've been trying to use perl's format:
^|||||~~^|||||~~^|||||~~^|||||~~^|||||~~
$var1 $var2 $var3 $var4 $var4
.
...but it's not working well.
The number's don't line up correct and I've tried to padding so that I force them to wrap but they line up unevenly. The contents of the table are being displayed through a foreach loop:
Column1 Column2
======= =======
1 2
1003
33
40
9948
I hope I'm clear on what I'm trying to do!
In general, I haven't found Perl’s patterns to be terribly reliable. They’re really useful for a few things, but generally require more work than it seems like they should.
The previous answer shows a more standard right-justification (which makes sense for numbers), but you seem to want a centered justification. What I don't understand, however, is why you’re using ^||||| instead of #|||||. The former will attempt to wrap text on multiple lines if necessary; that’s not really what you want here. You should probably be using #||||| instead.
Furthermore, there’s no need to use
^|||||~~^|||||~~^|||||~~^|||||~~^|||||~~
because the first ~~ will allow the pattern to be repeated indefinitely.
That said, I think you would get a lot more control over the process by generating the text yourself rather than relying on Perl’s formats.
sub centerText {
my ($text, $width) = #_;
my $pad = ( length($text) < $width )
? ' ' x int(($width - length($text)) / 2)
: '';
return "$pad$text";
}
Then you’d just use:
printf "%-7s %-7s\n", 'Column1', 'Column2';
printf "%-7s %-7s\n", '=======', '=======';
printf "%-7s %-7s\n", centerText($val1[1], 7), centerText($val2[1], 7);
or somesuch.
That make any sense?
I am fairly new to Perl and know next to nothing about Perl's 'proper' syntax.
I have a text file that I use everyday with a listing of names, and other info for our users. This file changes daily and sometimes has two rows in it(tab delimited), and other times has 100+ rows in it.
The file also varies between 6-9 columns of data in a row. I have put together a Perl script that uses the split function on tabs, but the issue I am running into is that if I take row a, which has 5 columns in it and then add a second row b that has 6 columns in it that are all populated with data.
I cannot figure out how to get Perl to see that row a only has 5 columns of data and to continue parsing the text file from that point forward. It continues, but the output wraps lines strangely. How can I get around this issue? I hope that made sense.
You will have to post some code and possibly some sample data, but here's a code that is parsing rows of different lengths without issue.
Script:
#!/usr/bin/perl
use strict;
while (<STDIN>)
{
chomp;
my #info = split("\t");
print join(";", #info), "\n";
}
exit;
Test File:
jsmith 101 777-222-5555 Office 1 Building 1 Manager
aposse 104 777-222-5556 Office 2 Building 2 Stock Clerk
jbraza 105 777-222-5557 Office 3
mcuzui 102 777-222-5557 Office 3 Building 3 Cashier
ghines 107 777-222-5557 Office 3
Output:
%> test.pl < file.txt
jsmith;101;777-222-5555;Office 1;Building 1;Manager
aposse;104;777-222-5556;Office 2;Building 2;Stock Clerk
jbraza;105;777-222-5557;Office 3
mcuzui;102;777-222-5557;Office 3;Building 3;Cashier
ghines;107;777-222-5557;Office 3
You should post some sample data and code and explain desired behavior in terms of what the code currently does and what you want it to do. split will give you as many fields as there are in the input.
#!/usr/bin/perl
use strict; use warnings;
while ( my $row = <DATA> ) {
last unless $row =~ /\S/;
chomp $row;
my #cells = split /\t/, $row;
print "< #cells >\n";
}
__DATA__
1 2 3 4 5
a b c d e f
Text::CSV module can be used for parsing tab-separated-values as well. In reality, Text::CSV could parse values delimited by any character.
Relevant excerpt from its POD:
The module accepts either strings or
files as input and can utilize any
user-specified characters as
delimiters, separators, and escapes so
it is perhaps better called ASV
(anything separated values) rather
than just CSV.
#!/usr/bin/env perl
use strict;
use warnings;
use Text::CSV;
my $csv = Text::CSV->new( { 'sep_char' => "\t" } );
open my $fh, '<', 'data.tsv' or die "Unable to open: $!";
my #rows;
while ( my $row_ref = $csv->getline($fh) ) {
push #rows, $row_ref;
}
$csv->sep_char('|');
for my $row_ref (#rows) {
$csv->combine(#$row_ref);
print $csv->string(), "\n";
}
It's the same question as this one, but using Perl!
I would like to iterate over a value with just one leading zero.
The equivalent in shell would be:
for i in $(seq -w 01 99) ; do echo $i ; done
Since the leading zero is significant, presumably you want to use these as strings, not numbers. In that case, there is a different solution that does not involve sprintf:
for my $i ("00" .. "99") {
print "$i\n";
}
Try something like this:
foreach (1 .. 99) {
$s = sprintf("%02d",$_);
print "$s\n";
}
The .. is called the Range Operator and can do different things depending on its context. We're using it here in a list context so it counts up by ones from the left value to the right value. So here's a simpler example of it being used; this code:
#list = 1 .. 10;
print "#list";
has this output:
1 2 3 4 5 6 7 8 9 10
The sprintf function allows us to format output. The format string %02d is broken down as follows:
% - start of the format string
0 - use leading zeroes
2 - at least two characters wide
d - format value as a signed integer.
So %02d is what turns 2 into 02.
printf("%02d\n",$_) foreach (1..20)
print foreach ("001" .. "099")
foreach $i (1..99) {printf "%02d\n", $i;}
I would consider to use sprinft to format $i according to your requirements. E.g. printf '<%06s>', 12; prints <000012>.
Check Perl doc about sprinft in case you are unsure.
Well, if we're golfing, why not:
say for "01".."99"`
(assuming you're using 5.10 and have done a use 5.010 at the top of your program, of course.)
And if you do it straight from the shell, it'd be:
perl -E "say for '01'..'99'"