Using Perl, I want to replace CRLF by | in the end of a line beginning with "ID".
So, to be more explicit: If a line begins with "ID", I replace CRLF in the end of this sentence by |.
This is what I have done:
elsif ($line =~ /^ID:\n/) { print $outputFile $line."|"; }
I think that it is not good ..
Depending on platform, \n has diffrent meanings. From perlport:
LF eq \012 eq \x0A eq \cJ eq chr(10) eq ASCII 10
CR eq \015 eq \x0D eq \cM eq chr(13) eq ASCII 13
| Unix | DOS | Mac |
---------------------------
\n | LF | LF | CR |
\r | CR | CR | LF |
\n * | LF | CRLF | CR |
\r * | CR | CR | LF |
---------------------------
* text-mode STDIO
You could do:
elsif ($line =~ /^(ID\b.*)\R/) { print $outputFile "$1|" }
\R stands for any kind of linebreak.
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";
}
}
Yes, I'm re-writing cowsay :)
#!/usr/bin/perl
use Text::Wrap;
$Text::Wrap::columns = 40;
my $FORTUNE = "The very long sentence that will be outputted by another command and it can be very long so it is word-wrapped The very long sentence that will be outputted by another command and it can be very long so it is word-wrapped";
my $TOP = " _______________________________________
/ \\
";
my $BOTTOM = "\\_______________________________________/
";
print $TOP;
print wrap('| ', '| ', $FORTUNE) . "\n";
print $BOTTOM;
Produces this
_______________________________________
/ \
| The very long sentence that will be
| outputted by another command and it
| can be very long so it is
| word-wrapped The very long sentence
| that will be outputted by another
| command and it can be very long so it
| is word-wrapped
\_______________________________________/
How can I get this ?
_______________________________________
/ \
| The very long sentence that will be |
| outputted by another command and it |
| can be very long so it is |
| word-wrapped The very long sentence |
| that will be outputted by another |
| command and it can be very long so it |
| is word-wrapped |
\_______________________________________/
I could not find a way in the documentation, but you can apply a small hack if you save the string. It is possible to assign a new line ending by using a package variable:
$Text::Wrap::separator = "|$/";
You also need to prevent the module from expanding tabs and messing with the character count:
$Text::Wrap::unexpand = 0;
This is simply a pipe | followed by the input record separator $/ (newline most often). This will add a pipe to the end of the line, but no padding space, which will have to be added manually:
my $text = wrap('| ', '| ', $FORTUNE) . "\n";
$text =~ s/(^.+)\K\|/' ' x ($Text::Wrap::columns - length($1)) . '|'/gem;
print $text;
This will match the beginning of each line, ending with a |, add the padding space by multiplying a space by columns minus length of matched string. We use the /m modifier to make ^ match newlines inside the string. .+ by itself will not match newlines, which means each match will be an entire line. The /e modifier will "eval" the replacement part as code, not a string.
Note that it is somewhat of a quick hack, so bugs are possible.
If you're willing to download a more powerful module, you can use Text::Format. It has a lot more options for customizing, but the most relevant one is rightFill which fills the rest of the columns in each line with spaces.
Unfortunately, you can't customize the left and right sides with non-space characters. You can use a workaround by doing regex substitutions, just as Text::NWrap does in its source code.
#!/usr/bin/env perl
use utf8;
use Text::Format;
chop(my $FORTUNE = "The very long sentence that will be outputted by another command and it can be very long so it is word-wrapped " x 2);
my $TOP = "/" . '‾'x39 . "\\\n";
my $BOTTOM = "\\_______________________________________/\n";
my $formatter = Text::Format->new({ columns => 37, firstIndent => 0, rightFill => 1 });
my $text = $formatter->format($FORTUNE);
$text =~ s/^/| /mg;
$text =~ s/\n/ |\n/mg;
print $TOP;
print $text;
print $BOTTOM;
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 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 |
|__________________________|___________________________|
Input of the text file
A | 1 | def | 1432
A | 1 | ffr | 1234
A | 1 | dfs | 3241
A | 2 | asf | 2213
Desired Output
A | 1 | def 1432,ffr 1234,dfs 3241
A | 2 | asf 2213
Merging values related to 2nd column in a single row
And here is a Perl attempt:
perl -F'\s+\|\s+' -alne '
$a{$F[1]} .= "$F[2] $F[3],";
END {
$_ = "A | $_ | $a{$_}", s/,$//, print for sort keys %a;
}' FILE
Your problem is not well specified, but here's a step towards a solution:
awk -F\| '{ a[$1 "|" $2] = a[$1 "|" $2 ] "," $3 $4 }
END { for( x in a ) print x a[x]}' input |
sed 's/,/|/' # Trim leading comma
This will incorrectly delete the first comma in either of the first 2 columns instead of the incorrectly inserted leading comma in the 3rd column of output, and sorts on both of the first 2 columns rather than just the 2nd. Also, the order of the output will be different than the input. There are probably other issues, but this may help.
awk '
BEGIN { FS = " \\| "; OFS = SUBSEP = " | " }
{
val[$1,$2] = val[$1,$2] sep[$1,$2] $3 " " $4
sep[$1,$2] = ","
}
END { for (key in val) print key, val[key] }
'
This will likely not preserve the order of the input. Also, it uses both the 1st and 2nd columns as the key, but as you say the 1st column does not change it is irrelevant.