http://qs1969.pair.com?node_id=1230292

syedasadali95 has asked for the wisdom of the Perl Monks concerning the following question:

I am a new monk and struggling with a piece of work. I want to extract data from a string, do addition operation on that data and then substitute the data in the same string. Here is the string I am working on:

chn:req mon:orig cmd:SDP_CMD_WRSIZEDFULL tag:0x3b9aca01 addr:0xdf7780 qospri:0 len:0xf

I want to extract the hexadecimal addr:0xdf7780 value, add 0x500000000 to it and then replace the result in the same string. Here is the piece of code I am using. I am able to print the extracted address values but not able to perform addition to it.

------------------------------------ #!usr/bin/perl unlink("ccix.sdp_trc"); $num = 0x500000000; open (IN_FILE, "$ARGV[0]") or die "Please provide an input file\n"; open (OUT_FILE, ">>ccix.sdp_trc"); while (my $line1 = <IN_FILE>) { if( ($line1 =~ /addr/) ) { $line1 =~ /addr:([a-z0-9-]+)\s+/; print "%$1\n"; my $one = ($num + $1); $line1 =~ s/addr\:0x.*qospri/addr\:$one qospri/; printf OUT_FILE ("$line1"); } else { printf OUT_FILE ("$line1"); } } ----------------------------------------------------
Any help is appreciated!

Replies are listed 'Best First'.
Re: Performing addition on hex value extracted from a string
by haukex (Archbishop) on Feb 21, 2019 at 10:49 UTC

    What's going wrong in your code is that $1 contains the string "0xdf7780", which Perl does not automatically convert to a number. You can use the hex function to convert it to a number first, and then use sprintf to convert the output back to hex:

    $line1 =~ /addr:([a-z0-9-]+)\s+/; my $one = $num + hex $1; $one = sprintf "0x%x", $one; $line1 =~ s/addr\:0x.*qospri/addr\:$one qospri/;

    However, it's also possible to do it all in one statement, using the special s///e, which runs the replacement part as Perl code and inserts its return value as the replacement, so you can use it to run sprintf directly. I've also used \K and a (?=...) lookahead to exclude those strings from the match, so they're not changed by the replacement.

    $line1 =~ s{ addr:0x \K ([0-9a-fA-F]*) (?=\s+qospri) } { sprintf "%x", hex($1)+$num }xe;

    By the way, you should always Use strict and warnings, and use proper indentation to make your code easier to read (perltidy can help with that).

    Update: I added the /x modifier to the regex to make it easier to read. Also switched from (?=\ qospri) to (?=\s+qospri) to make it equivalent to the original regex.

      Thank you so much for the explanation Haukex. It worked.
Re: Performing addition on hex value extracted from a string
by mr_ron (Chaplain) on Feb 21, 2019 at 18:45 UTC

    Hopefully a more complete and self contained example for haukex explanation:

    #!/usr/bin/env perl use v5.10; use strict; use warnings; use Math::BigInt; use constant offset => Math::BigInt->new('0x500000000'); while (my $line1 = <DATA>) { # if addr 0x10000 written like 0x010000 then # 0x(?:[[:xdigit:]]{2})+)\b may validate memory address my ($addr) = $line1 =~ /addr:(0x[[:xdigit:]]+)\b/; # print "addr is $addr\n"; if (defined $addr) { my $new_addr = sprintf "0x%x", hex($addr) + offset; if ($line1 =~ s/ \baddr: \K $addr (?=\s+qospri:) /$new_addr/x ) { print $line1; } else { print STDERR "unchanged\n"; print $line1; } } } __DATA__ chn:req mon:orig cmd:SDP_CMD_WRSIZEDFULL tag:0x3b9aca01 addr:0xdf7780 +qospri:0 len:0xf

    Update: requirement for even number of digits removed thanks to explanation from AnamalousMonk.

    Ron
      my ($addr) = $line1 =~ /addr:(0x(?:[[:xdigit:]]{2})+)/;

      If the hex value really is an address as  'addr' suggests, is there any guarantee the hex digits will always be paired, i.e., always be an even number of digits? If they are not paired, won't the lack of a boundary assertion sometimes cause an incorrect value to be extracted from the string?

      c:\@Work\Perl\monks>perl -wMstrict -le "my $line1 = 'addr:0xabcde'; my ($addr) = $line1 =~ /addr:(0x(?:[[:xdigit:]]{2})+)/; print qq{'$addr'}; " '0xabcd'


      Give a man a fish:  <%-{-{-{-<

Re: Performing addition on hex value extracted from a string
by jwkrahn (Abbot) on Feb 21, 2019 at 20:24 UTC
    #!usr/bin/perl unlink("ccix.sdp_trc");

    That should probably be:

    #!/usr/bin/perl use warnings; use strict; unlink("ccix.sdp_trc");
    I want to extract the hexadecimal addr:0xdf7780 value, add 0x500000000 to it and then replace the result in the same string.

    If the addr:0xdf7780 value is always six digits long then you can do a simple text substitution:

    $ perl -le'$x = "hexadecimal addr:0xdf7780 value"; print $x; $x =~ s/ +addr:0x \K (?= [[:xdigit:]]+ ) /500/x; print $x' hexadecimal addr:0xdf7780 value hexadecimal addr:0x500df7780 value

    If the addr:0xdf7780 value is always less than nine digits long then you can do:

    $ perl -le'$x = "hexadecimal addr:0xdf7780 value"; print $x; $x =~ s/ +addr:0x \K ( [[:xdigit:]]+ ) / $y = "500000000"; $l = length $1; subs +tr $y, -$l, $l, $1; $y /xe; print $x' hexadecimal addr:0xdf7780 value hexadecimal addr:0x500df7780 value

    Otherwise, this will work:

    $ perl -le'$x = "hexadecimal addr:0xdf7780 value"; print $x; $x =~ s/ +addr:0x \K ( [[:xdigit:]]+ ) / sprintf "%x", hex( $1 ) + 0x500000000 +/xe; print $x' hexadecimal addr:0xdf7780 value hexadecimal addr:0x500df7780 value