How to append LF (0A) character to a file using Path::Tiny? - perl

This approach doesn't work - just string "10" is added
use Modern::Perl;
use Path::Tiny qw( path );
use DateTime;
my $d1 = DateTime->new(year => 2019, month => 5, day => 6);
my #lines_to_add;
$lines_to_add[0]= "1|" . $d1->dmy('.') . "|";
$,="\n";
my $filename = "./load";
path($filename)->spew_raw(#lines_to_add);
path($filename)->append({binmode => ":raw"}, 10);
I'd like to generate some data and then insert them into a table (Informix running on AIX). However, the environment requires the LF character at the end of files to load from. And I'd like to use just Path::Tiny library for that. (I am on Windows using Strawberry Perl)

The value produced by the numerical literal
10
is being stringified into the two character string
10
To get a string consisting of character 10, you can use any of the following:
"\n"
"\N{LINE FEED}"
"\N{LF}"
"\N{U+000A}"
"\x{0A}"
"\x0A"
"\012"
chr(10)
pack('C', 10)

Related

how to make grep use a "from X to Y" syntax? (Using date as parameter)

so I would like to write a script that scans through orders and files and pastes certain lines of these files into a file.
How can I let my file scan through a specified range instead of a singular date?
Actually the code I'd need to change looks like this:
$curdir = "$scriptdir\\$folder";
opendir my $dir_b, "$curdir" or die "Can't open directory: $!";
my #file = grep { /$timestamp/ } readdir $dir_b;
closedir $dir_b;
Now line 3 needs to work actually like this
my #file = grep { /$timestamp1 to $timestamp2/ } readdir $dir_b;
anyone knows how to achieve this? timestamp1 would be as example 20160820 and timestamp2 would be 20160903 or 20160830
thanks for the help
You can use Regexp::Assemble to build one pattern out of all timestamps that are in the range of your dates.
use strict;
use warnings;
use Regexp::Assemble;
my $timestamp_late = 20160830;
my $timestamp_early = 20160820;
my $ra = Regexp::Assemble->new;
$ra->add( $_ ) for $timestamp_early .. $timestamp_late;
print $ra->re;
The pattern for that case is: (?^:201608(?:2\d|30))
You can now use it like this:
my $pattern = $ra->re;
my #files = grep { /$pattern/ } readdir $dir_b;
It works by combining multiple patterns into a single one.
Regexp::Assemble takes an arbitrary number of regular expressions and assembles them into a single regular expression (or RE) that matches all that the individual REs match.
As a result, instead of having a large list of expressions to loop over, a target string only needs to be tested against one expression. This is interesting when you have several thousand patterns to deal with. Serious effort is made to produce the smallest pattern possible.
Our patterns here are rather simple (they are just strings), but it works nonetheless. The resulting pattern works like this:
(?^: ) # non-capture group w/o non-default flags for the sub pattern
201608 # literal 201608
(?: ) # non-capture group
2\d # literal 2 followed by a digit (0-9)
| # or
30 # literal 30
The (?^:) is explained in this part of perlre.
If you pass in more numbers, the resulting regex will look different. Of course this is not meant for dates, so with my simple 1 .. 9 expression we get all numbers in between. The .. is the range operator, and will return the list (1, 2, 3, 4, 5, 6, 7, 8, 9) for the aforementioned case.
So if you wanted to make sure that you only get valid dates, you could take this approach or this approach. Here's an example.
use strict;
use warnings;
use Regexp::Assemble;
use DateTime;
my $timestamp_late = DateTime->new( year => 2016, month => 9, day => 1 );
my $timestamp_early = DateTime->new( year => 2016, month => 8, day => 19 ); # -1 day
my $ra = Regexp::Assemble->new;
while ( $timestamp_early->add( days => 1 ) <= $timestamp_late ) {
$ra->add( $timestamp_early->ymd(q{}) );
}
print $ra->re;
This goes over to the next month and gives
(?^:20160(?:8(?:3[01]|2\d)|901))
which, only matches real dates, while the other, simpler, solution will include all numbers between them, including the 99th of August.
(?^:20160(?:8(?:2\d|3\d|4\d|5\d|6\d|7\d|8\d|9\d)|90[01]))
Solution by Сухой27, posted as a comment
my #file = grep { /$timestamp1/ .. /$timestamp2/ } readdir $dir_b;
A very nice example of use of the range operator
I favor some simpler approaches that are easy for someone to understand. The flip-flop is cool, but almost no one knows what it does.
You don't have to do everything in one operation:
my #file = grep {
my $this_date = ...;
$lower_date <= $this_date and $this_date <= $higher_date;
} #inputs;

How to use the Perl format function to print multiple columns

I have a Perl hash like %word. The key is the word and the value is its count. Now I want to display %word like:
the 20 array 10 print 2
a 18 perl 8 function 1
of 12 code 5
I search and Perl format can solve this, and I learn this page perlform, but I still don't how to do it.
I knew about format and that it could be vary handy to generate nice forms... at the time we still had a world where all was monospaced...
So, I researched it a bit and found the following solution:
use strict;
use warnings;
my %word = (
the => 20,
array => 10,
print => 2,
a => 18,
perl => 8,
function => 1,
of => 12,
code => 5,
);
my #word = %word; # turn the hash into a list
format =
#<<<<<<<<<<< #>>>> #<<<<<<<<<<< #>>>> #<<<<<<<<<<< #>>>>~~
shift #word, shift #word, shift #word, shift #word, shift #word, shift #word
.
write;
The nasty problem sits in the ~~ which makes the line repeating and that for each field in the format line you do need a corresponding scalar value... In order to get those scalar values, I shifted them off from the #word array.
There is a lot more to know about format and write.
Have fun!

How to manipulate output in perl

I'm new to perl and I can't find whether I can manipulate the output format in perl or not.
for a code like
print "$arOne[i] => $arTwo[i]\n";
I want the oputput to be like
8 => 9
10 => 25
7 => 456
If it is possible, then how to do it?
You want to use printf.
printf ("%2d => %-3d\n", $arOne[$i], $arTwo[$i]);
The formatting instructions are embedded between the % and a letter. In your case, you print numbers, so you need the letter d. The number left to the d specifies how many digits you want to reserve for the number. In your case, I made the assumption that the left number consists of at most two digits, while the right number consists of at most three digits. That might vary. Finally, the - in front of the 3d tells printf to left (rather than right) align the number.
In the spirit of TMTOWTDI-ness, there's also the old facility of perl formats:
#! /usr/bin/perl
use strict;
use warnings;
use List::MoreUtils qw(each_array);
my #arOne = (8, 10, 7);
my #arTwo = (9, 25, 456); # #arTwoDeeTwo ? #ceeThreePO ?
my ($one, $two);
format STDOUT =
#> => #<<
$one,$two
.
# Now write to the format we described above
my $next_pair = each_array(#arOne, #arTwo);
while (($one, $two) = $next_pair->()) {
write;
}
UPDATE
Note that this "report generation" capability is little-used in contemporary perl programming. The printf suggestion is typically more flexible (and less surprising). It seemed a pity, however, not to mention formats in perl in question about formatting in perl.

Convert utf-8 into html &...;

In Perl, how can I convert string containing utf-8 characters to HTML where such characters will be converted into &...; ?
First, split on an empty pattern to get a list of single characters. Then, map each character to itself, if it is ASCII, or its code, if it is not:
use Encode qw( decode_utf8 );
my $utf8_string = "\xE2\x80\x9C\x68\x6F\x6D\x65\xE2\x80\x9D";
my $unicode_string = decode_utf8($utf8_string);
my $html = join q(),
map { ord > 127 ? "&#" . ord . ";"
: $_
} split //, $unicode_string;
Just replace every symbol that is not printable and not low ASCII (that is, anything outside \x20 - \x7F region) with simple calculation of its ord + necessary HTML entity formatting. Perl regexp have /e flag to indicate that replacement should be treated as code.
use utf8;
my $str = "testТест"; # This is correct UTF-8 string right in the code
$str =~ s/([^[\x20-\x7F])/"&#" . ord($1) . ";"/eg;
print $str;
# testТест

How to convert hex to string of hex

I have a problem understanding and using the 'vec' keyword.
I am reading a logpacket in which values are stored in little endian hexadecimal. In my code, I have to unpack the different bytes into scalars using the unpack keyword.
Here's an example of my problem:
my #hexData1 = qw(50 65);
my $data = pack ('C*', #hexData1);
my $x = unpack("H4",$data); # At which point the hexadecimal number became a number
print $x."\n";
#my $foo = sprintf("%x", $foo);
print "$_-> " . vec("\x65\x50", $_, 1) . ", " for (0..15); # This works.
print "\n";
But I want to use the above statement in the way below. I don't want to send a string of hexadecimal in quotes. I want to use the scalar array of hex $x. But it won't work. How do I convert my $x to a hexadecimal string. This is my requirement.
print "$_-> " . vec($x, $_, 1).", " for (0..15); # This doesn't work.
print "\n";
My final objective is to read the third bit from the right of the two byte hexadecimal number.
How do I use the 'vec' command for that?
You are making the mistake of unpacking $data into $x before using it in a call to vec. vec expects a string, so if you supply a number it will be converted to a string before being used. Here's your code
my #hexData1 = qw(50 65);
my $data= pack ('C*', #hexData1);
The C pack format uses each value in the source list as a character code. It is the same as calling chr on each value and concatenating them. Unfortunately your values look like decimal, so you are getting chr(50).chr(65) or "2A". Since your values are little-endian, what you want is chr(0x65).chr(0x50) or "\x65\x50", so you must write
my $data= pack ('(H2)*', reverse #hexData1);
which reverses the list of data (to account for it being little-endian) and packs it as if it was a list of two-digit hex strings (which, fortunately, it is).
Now you have done enough. As I say, vec expects a string so you can write
print join ' ', map vec($data, $_, 1), 0 .. 15;
print "\n";
and it will show you the bits you expect. To extract the the 3rd bit from the right (assuming you mean bit 13, where the last bit is bit 15) you want
print vec $data, 13, 1;
First, get the number the bytes represent.
If you start with "\x50\x65",
my $num = unpack('v', "\x50\x65");
If you start with "5065",
my $num = unpack('v', pack('H*', "5065"));
If you start with "50","65",
my $num = unpack('v', pack('H*', join('', "50","65"));
Then, extract the bit you want.
If you want bit 10,
my $bit = ($num >> 10) & 1;
If you want bit 2,
my $bit = ($num >> 2) & 1;
(I'm listing a few possibilities because it's not clear to me what you want.)