How to divide string in perl for 2 parts - string

so I need to divide my string in perl for 2 parts. For example I have:
$string = "../dira/dirb/*.txt"
And I want to divide it on:
$stringA = "../dira/dirb"
$stringB = "*.txt"
But if I have:
$string = "dira/dirb/dirc/.../dirn/test.pl";
I want to divie it on:
$stringA = "dira/dirb/dirc/.../dirn"
$stringB = "test.pl"
Somebody have idea how can I do it? I tried to do something like:
$howmany++ while $string =~ m/\//g;
So I know how many slashes I have. But I have no idea what I can do more with this :/

Use Path::Tiny:
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
use Path::Tiny;
for my $path (qw( ../dira/dirb/*.txt
dira/dirb/dirc/.../dirn/test.pl
)) {
my $path_o = 'Path::Tiny'->new($path);
my $basename = $path_o->basename;
my $dirname = $path_o->dirname;
$dirname =~ s=/$==; # Remove the trailing slash.
say $basename, ' ', $dirname;
}

you can try something like this:
$string =~ m|^(.*)/(.*)$|;
($stringA,$stringB) = ($1,$2);
print "stringA = $stringA\n";
print "stringB = $stringB\n";
= edit: =
restrict to certain values of stringB:
if($string =~ m|^(.*)/(.*\.pl)$|) {
($stringA,$stringB) = ($1,$2);
print "stringA = $stringA\n";
print "stringB = $stringB\n";
}

You can use File::Basename functions to parse file paths:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Basename;
my $string = "../dira/dirb/*.txt";
my $stringA = dirname($string);
my $stringB = basename($string);
printf "String A: %-25sString B: %s\n", $stringA, $stringB;
$string = "dira/dirb/dirc/.../dirn/test.pl";
$stringA = dirname($string);
$stringB = basename($string);
printf "String A: %-25sString B: %s\n", $stringA, $stringB;

Related

Getting the characters of a string up to the first "."

I'm attempting to use Perl's gethostnamebyaddr function. The annoying thing is that it returns the entire domain name in scalar format. I want to parse out only the hostname and discard the rest.
I'm using split to divide the domain name into an array and then taking only the first value but this doesn't seem to work.
#!/usr/bin/perl
use Socket;
my $name;
my $hostname;
my #tmpStr;
$name = gethostbyaddr(inet_aton("192.168.2.3"), AF_INET);
print "$name\n";
#tmpStr = split ".", $name;
$hostname = $tmpStr[0];
print "Host name is $hostname\n";
When the above code is executed, I get the following:
dc1-ent.ent.ped.local
Host name is
According to this website the return value is not a string but is rather a scalar value and so my attempt at splitting it doesn't work.
I can't figure out how to convert it to a string before I can split it or parse out the hostname by itself.
The dot character has special meaning for regular expressions in Perl, and the 1st argument to split is a regular expression. You need to escape the dot:
use warnings;
use strict;
my $name = 'dc1-ent.ent.ped.local';
print "$name\n";
my #tmpStr = split /\./, $name;
my $hostname = $tmpStr[0];
print "Host name is $hostname\n";
This outputs:
dc1-ent.ent.ped.local
Host name is dc1-ent
I would write it like this
my $name = gethostbyaddr(inet_aton('192.168.2.3'), AF_INET);
my ($host) = $name =~ /([^.]+)/;
say $host;
Your problem is not related to gethostbyaddr() but by what follows.
Proof:
DB<1> $name = 'dc1-ent.ent.ped.local';
DB<2> #tmpStr = split ".", $name;
DB<3> print #tmpStr;
(nothing printed)
Try instead using split that way:
DB<8> $name = 'dc1-ent.ent.ped.local';
DB<9> #tmpStr = split(/\./, $name);
DB<10> print #tmpStr;
dc1-ententpedlocal
DB<11> print join(' ', #tmpStr);
dc1-ent ent ped local
DB<12> x #tmpStr;
0 'dc1-ent'
1 'ent'
2 'ped'
3 'local'
Or if you absolutely want a string and not a regex, protect the dot also as your string is still parsed as a regular expression (which is why being explicit with / / has its merits, it forces you to remember that some character have special meaning there, like the dot):
DB<1> $name = 'dc1-ent.ent.ped.local';
DB<2> #tmpStr = split('.', $name);
DB<3> print #tmpStr;
DB<4> #tmpStr = split('\.', $name);
DB<5> x #tmpStr
0 'dc1-ent'
1 'ent'
2 'ped'
3 'local'

What is the best way to convert a string to a string with separators in perl

How do I convert $var = "000000000" to $var = "0_0000_0000" in Perl ?
If the string is always 9 characters long, you can just use substr:
my $var = '000000000';
substr($var, 5, 0) = '_';
substr($var, 1, 0) = '_';
For formatting strings of arbitrary length you could use a function like this:
sub format_str {
my $str = reverse $_[0];
$str =~ s/(.{4})(?=.)/$1_/g;
return scalar reverse $str;
}
my $var = "000000000";
print format_str $var; # "0_0000_0000"
$var = "000000000";
$var2 = substr($var,0,1)."_".substr($var,1,4)."_".substr($var,5);
print $var2;
Assuming you're asking how to insert a _ after the first and fifth characters of a string, the following are a variety of straightforward solutions:
my $in = '000000000';
my $out = substr($var,0,1) . '_' . substr($var,1,4) . '_' . substr($var,5);
my $in = '000000000';
my $out = join('_', substr($var,0,1), substr($var,1,4), substr($var,5));
my $in = '000000000';
my $out = join('_', unpack('a1 a4 a4*', $in));
my $in = '000000000';
my $out = $in =~ s/^(.)(.{4})/${1}_${2}_/sr; # 5.14+
my $in = '000000000';
( my $out = $in ) =~ s/^(.)(.{4})/${1}_${2}_/s;
In-place:
my $var = '000000000';
$var =~ s/^(.)(.{4})/${1}_${2}_/s;
my $var = '000000000';
substr($var, 5, 0) = '_';
substr($var, 1, 0) = '_';
For a solution for any-length string and considering efficiency issues that arise for very-long strings, please see my previous question&answer: How to chunk text "from the back" in perl.
Per suggestion in comment, here is code using the idea in the linked question/answer which answers the OP question:
use integer;
my $la = length($var);
my $r = $la % 4;
my $q = $la / 4;
my $tr = $r ? "a$r" : "";
$var = join "_", unpack "$tr(a4)$q", $var;
Note: change all three 4s for a different grouping size.
If this is a commify problem that is solved in "How can I output my numbers with commas added?", available as perldoc -q 'commas added', then a similar solution will suffice, with extra parameters to define the separator and the size of the interval
You will want to read the perlfaq entry for other alternatives
use strict;
use warnings 'all';
print group_characters(1234567), "\n";
print group_characters('000000000', '_', 4), "\n";
print group_characters('0123456789ABCDEF', ' ', 4), "\n";
sub group_characters {
my ($s, $sep, $n) = #_;
$sep //= ',';
$n //= 3;
1 while $s =~ s/[^$sep]+\K(?=[^$sep]{$n})/$sep/;
$s;
}
output
1,234,567
0_0000_0000
0123 4567 89AB CDEF

How to read "<somestring>" in input string in perl

Below is my code. It still produces same string with no "<init>"
input string :
1: invokespecial #1 // Method java/lang/Object."<init>":()V
my $file = "Hello.javap";
open my $fh, '<', $file or die "Could not open '$file' $!";
while (my $line = <$fh>) {
if (index(uc($line), uc("Code:")) != -1) {
$code_block_started=1;
}
if(index($line,":")==-1)
{
if (my ($method) = $line =~ /.* \/\/ Method (.*);/) {
print "Method: $method\n";
}
print $line;
$code_block_started=0;
}
if($code_block_started){
if ($line =~/[0-9]/) {
my #num_strip = split(':',$line);
my #get_command = split(' ',$num_strip[1]);
# print "\n $get_command[0]";
$count{$get_command[0]}++;
}
}
Are you simply asking how to escape the " in perl? If so, write \"<init>\" just like in most languages.
Are you asking for a regular expression? If so, $str ~= /.* \/\/ Method (.*);/ will put java/lang/Object."<init>":()V into $1.
while (my $str = <>) {
if (my ($method) = $str =~ m{// Method (.*)}) {
print "$method\n";
}
}
when Perl sees the double-quote just before the word "name" it thinks that was the end of the string and then it complains about the word name being a bareword.
You might have already guessed, we need to escape the embedded " character:
use strict;
use warnings;
my $name = 'foo';
print "The \"name\" is \"$name\"\n";
http://perlmaven.com/quoted-interpolated-and-escaped-strings-in-perl

Perl - Process string with IDs and corresponding values in quotation marks

I'm working on a Perl project where i have a lot of strings containing ids and corresponding values in quotation marks, seperated by semicolons.
Example: main_id "1234567"; second_id "My_ID"; name "Andreas";
There is a blank behind every ID-name and behind every semicolon.
There are 2 problems I'm dealing with:
Problem 1: What is the fastest way to get the value (without quotation marks) to a specific id? My first try didn't work:
$id_list = 'main_id "1234567"; second_id "My_ID"; name "Andreas";';
$wanted_id = 'second_id';
($value = $id_list) =~ s/.*$wanted_id\w"([^"])";.*/$1/;
Problem 2: What is the fastest way to turn this string id into a hash for a specific id, that looks like this:
String: main_id "1234567"; second_id "My_ID"; name "Andreas";
Hash for "second_id":
hash{My_ID} = {main_id => 1234567, second_id => My_ID, name => Andreas}
What I tried:
$id_list = 'main_id "1234567"; second_id "My_ID"; name "Andreas";';
$wanted_id = 'second_id';
%final_id_hash;
%hash;
my #ids = split ";", $id_list;
foreach my $id (#ids) {
my ($a,$b)= split " ", $id;
$b =~ s/"//g;
$hash{$a} = $b;
}
$final_hash{$hash{$wanted_id}}= \%hash;
This worked, but is there a faster/better solution?
The Text::ParseWords module (part of the standard Perl distribution) makes this simple.e.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Text::ParseWords;
use Data::Dumper;
my %final_hash;
my $wanted_id = 'second_id';
my $id_list = 'main_id "1234567"; second_id "My_ID"; name "Andreas";';
my #words = parse_line '[\s;]+', 0, $id_list;
pop #words; # Lose the extra field generated by the ; at the end
my %hash = #words;
$final_hash{$hash{$wanted_id}} = \%hash;
say Dumper \%final_hash;
Problem 1,
my %hash = map {
map { s/ ^" | "$ //xg; $_ } split /\s+/, $_, 2;
}
split /;\s+/, qq{main_id "1234567"; second_id "My_ID"; name "Andreas"};
use Data::Dumper; print Dumper \%hash;

how to compare 2 strings by each characters in perl

basically I want to compare
$a = "ABCDE";
$b = "--(-)-";
and get output CE.
i.e where ever parentheses occur the characters of $a should be taken.
One of the rare uses of the bitwise or-operator.
# magic happens here ↓
perl -E'say (("ABCDE" | "--(-)-" =~ tr/-()/\377\000/r) =~ tr/\377//dr)'
prints CE.
Use this for golfing purposes only, AHA’s solution is much more maintainable.
Simple regex and pos solution:
my $str = "ABCDE";
my $pat = "--(-)-";
my #list;
while ($pat =~ /(?=[()])/g) {
last if pos($pat) > length($str); # Required to prevent matching outside $x
my $char = substr($str, pos($y), 1);
push #list, $char;
}
print #list;
Note the use of lookahead to get the position before the matching character.
Combined with Axeman's use of the #- variable we can get an alternative loop:
while ($pat =~ /[()]/g) {
last if $-[0] > length($str);
my $char = substr($str, $-[0], 1);
push #list, $char;
}
This is pretty much mentioned in the documentation for #-:
After a match against some variable $var :
....
$& is the same as substr($var, $-[0], $+[0] - $-[0])
In other words, the matched string $& equals that substring expression. If you replace $var with another string, you would get the characters matching the same positions.
In my example, the expression $+[0] - $-[0] (offset of end of match minus offset of start of match) would be 1, since that is the max length of the matching regex.
QED.
This uses the idea that you can scan one string for positions and just take the values of the other strings. #s is a reusable product.
use strict;
use warnings;
sub chars {
my $source = shift;
return unless #_;
my #chars = map { substr( $source, $_, 1 ) } #_;
return wantarray ? #chars, join( '', #chars );
}
my $a = "ABCDE";
my $b = "--(-)-";
my #s;
push #s, #- while $b =~ m/[()]/g;
my $res = chars( $a, #s );
Way faster than all the solutions except daxim's, and almost as fast as daxim's without preventing the use of characters 255 and above:
my $pat = $b =~ s/[^()]/.?/gr =~ s/[()]/(.?)/gr
my $c = join '', $a =~ /^$pat/s;
It changes
---(-)-
to
.?.?.?(.?).?(.?).?
Then uses the result as regex pattern to extract the desired characters.
This is easy to accomplish using each_array, each_arrayref or pairwise from List::MoreUtils:
#!/usr/bin/env perl
use strict;
use warnings;
use List::Util qw( min );
use List::MoreUtils qw( each_array );
my $string = 'ABCDE';
my $pattern = '--(-)-';
my #string_chars = split //, $string;
my #pattern_chars = split //, $pattern;
# Equalise length
my $min_length = min $#string_chars, $#pattern_chars;
$#string_chars = $#pattern_chars = $min_length;
my $ea = each_array #string_chars, #pattern_chars;
while ( my ( $string_char, $pattern_char ) = $ea->() ) {
print $string_char if $pattern_char =~ /[()]/;
}
Using pairwise:
{
no warnings qw( once );
print pairwise {
$a if $b =~ /[()]/;
} #string_chars, #pattern_chars;
}
Without using List::MoreUtils:
for ( 0 .. $#string_chars ) {
print $string_chars[$_] if $pattern_chars[$_] =~ /[()]/;
}
Thanks to TLP for discovering the set $# technique without which this solution will have been longer and complicated. :-)
#!/usr/bin/perl
use strict;
use warnings;
my $a = "ABCDE";
my $b = "--(-)-";
my ($i, $c, $x, $y) = 0;
$c .= $y =~ /\(|\)/ ? $x : "" while ($x = substr $a, $i, 1) && ($y = substr $b, $i++, 1);
print "$c\n";

Resources