http://www.perlmonks.org?node_id=1209067


in reply to Re^3: Create output from Perl hash
in thread Create output from Perl hash

Hi Laurent_R, I ran your code, if there are multiple records in the file only the first is printed. I think the problem is the print "$line\n"; Also the 91436903000 should be 1/1/1/0-436903000 Thank you again, this is a real help for me to get back into perl.

<BEGINFILE>
<SUBBEGIN IMSI=232191400010332; MSISDN=436906901235; CF=CFU-ALL-PROV-NONE-YES-NO-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO-N +O-NO-NO; CF=CFB-ALL-PROV-NONE-YES-YES-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO- +NO-NO-NO; CF=CFNRY-ALL-PROV-NONE-YES-YES-NONE-YES-65535-NO-NO-NO-NO-NO-NO-N +O-NO-NO-NO; CF=CFNRC-ALL-PROV-NONE-YES-NO-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO +-NO-NO-NO; CF=CFD-TS10-ACT-91436903000-YES-YES-25-YES-65535-YES-YES-NO-NO-NO +-YES-YES-YES-YES-NO; <SUBEND <BEGINFILE> <SUBBEGIN IMSI=232191400010339; MSISDN=436906901231; CF=CFU-ALL-PROV-NONE-YES-NO-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO-N +O-NO-NO; CF=CFB-ALL-PROV-NONE-YES-YES-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO- +NO-NO-NO; CF=CFNRY-ALL-PROV-NONE-YES-YES-NONE-YES-65535-NO-NO-NO-NO-NO-NO-N +O-NO-NO-NO; CF=CFNRC-ALL-PROV-NONE-YES-NO-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO +-NO-NO-NO; CF=CFD-TS10-ACT-91436903000-YES-YES-25-YES-65535-YES-YES-NO-NO-NO +-YES-YES-YES-YES-NO; <SUBEND

Replies are listed 'Best First'.
Re^5: Create output from Perl hash
by Laurent_R (Canon) on Feb 13, 2018 at 16:46 UTC
    if there are multiple records in the file only the first is printed.
    Sure, you did not describe your input data and only gave one sample. I based the suggested program on your data sample. The first sample you gave only had one "CF" line, so I made the first program based on that sample. And it worked.

    Then you gave another sample with several "CF" lines (but still only one record block), so I changed my program to fit this new data sample. And it worked.

    Now, you're saying that there can be several record blocks. How were we supposed to guess that? Well, in fact, I sort of guessed it would plausibly be the case, but I could not write a program based on an inexistent data sample.

    So this is the third version:

    use strict; use warnings; my $line; while (<DATA>) { if (/^\s*MSISDN=(\d+);/) { print "$line\n" if defined $line; $line = $1 ; } if (/\s*CF=([\w-]+?-(?:NONE|\d+))/) { my $add = $1; $add =~ s{(\d+)$}{1/1/1/$1}; $add =~ s{NONE}{1/1/1/0}; $line .= ",$add"; } } print "$line\n"; __DATA__ # --> Here, the data with two record blocks you presented just above
    And this processes your two record blocks and prints:
    436906901235,CFU-ALL-PROV-1/1/1/0,CFB-ALL-PROV-1/1/1/0,CFNRY-ALL-PROV- +1/1/1/0,CFNRC-ALL-PROV-1/1/1/0,CFD-TS10-ACT-1/1/1/91436903000 436906901231,CFU-ALL-PROV-1/1/1/0,CFB-ALL-PROV-1/1/1/0,CFNRY-ALL-PROV- +1/1/1/0,CFNRC-ALL-PROV-1/1/1/0,CFD-TS10-ACT-1/1/1/91436903000
    This is presumably what you want, except for the number at the end of the line, which is discussed just below.
    Also the 91436903000 should be 1/1/1/0-436903000
    OK, fine, but you're not telling how to derive 436903000 from 91436903000. Should we just remove the first two digits in all cases? Or should we remove 91 when the number starts with 91? Or should we remove any digits until we find 43? Or is it something else? How are we supposed to know if you don't tell us?

    So I did not change that, because did not specify the rule to be applied to derive the number you want to print. I guess it is probably fairly easy and you can probably make the change yourself.

      I know, sorry for not being precious and thank you for your time again.

      Also the 91436903000 should be 1/1/1/0-436903000

      We should remove 91 from the beginning of 91436903000, so the result should look like this.

      436906901235,CFU-ALL-PROV-1/1/1/0,CFB-ALL-PROV-1/1/1/0,CFNRY-ALL-PROV +-1/1/1/0,CFNRC-ALL-PROV-1/1/1/0,CFD-TS10-ACT-1/1/1/436903000 436767817451,CFU-ALL-PROV-1/1/1/0,CFB-ALL-PROV-1/1/1/0,CFNRY-ALL-PROV +-1/1/1/0,CFNRC-ALL-PROV-1/1/1/0,CFU-TS10-ACT-1/1/1/4369050045021,CFD- +TS10-REG-1/1/1/91436903000 4369060900384,CFU-ALL-PROV-1/1/1/0,CFB-ALL-PROV-1/1/1/0,CFNRY-ALL-PROV +-1/1/1/0,CFNRC-ALL-PROV-1/1/1/0,CFD-TS10-ACT-1/1/1/436903000

      I would like to ask you a couple of questions about your code so I can learn

      my $line; while (<DATA>) { if (/^\s*MSISDN=(\d+);/) { print "$line\n" if defined $line; $line = $1 ; } if (/\s*CF=([\w-]+?-(?:NONE|\d+))/) { my $add = $1; $add =~ s{(\d+)$}{1/1/1/$1}; $add =~ s{NONE}{1/1/1/0}; $line .= ",$add"; } } print "$line\n";

      In the first if statement you search for MSISDN and a group containing 1 or more digits. I do not understand what you are doing with print "$line\n" if defined $line;

      In the second if statement you use a non capturing group, could you please explain?

      Could you please explain the use of curly braces in your code ?

        Hi gbwien,

        The program is populating the $line variable progressively. First with the MSISDN, and then with the data from the CF lines.

        When the program meets a new MSISDN line, it means that we are starting to process a new record block; we need to print out everything that has been stored in $line and reinitialize $line for the next record block. However, we don't want to print $line the first time (because we haven't started yet to populate it).

        There is a capture using ([\w-]+?-(?:NONE|\d+)). I simply don't need the nested parentheses (?:NONE|\d+) to capture anything, there are there just to clarify what the alternative match should be (either NONE or a sequence of digits).

        As for curlies in the s/// operator, I am using them because the substitution strings contain slashes that would be interpreted as part of the substitution operator.

        Using s{...}{...} instead of s/.../.../ makes it possible to avoid having to escape the / in the sustitution strings.

Re^5: Create output from Perl hash
by poj (Abbot) on Feb 13, 2018 at 17:40 UTC

    My advice would be as step 1 - extract the data you need into a structure. Multiple records with a common key suggests a HashOfArrays. For example

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $key1 = 'MSISDN'; my $key2 = 'CF'; my $sep = ','; # input my $rec = {}; while (my $line = <DATA>){ next if $line =~ /BEGINFILE/; #skip first line chomp $line; $line =~ s/^\s+|;$//g; # remove leading whitespace and ; if ($line =~ /SUBBEGIN/){ $rec = {}; # start new record } elsif ($line =~ /SUBEND/){ if (defined $rec->{$key1} && defined $rec->{$key2}){ output_record($rec) ; } } else { my ($key,$value) = split /=/,$line; push @{$rec->{$key}},$value if ($key); } } # output sub output_record { my $rec = shift; print Dumper \$rec; } __DATA__

    If that works, then step 2 work on your transformation and output code to replace Dumper.

    sub output_record { my $rec = shift; # print Dumper \$rec; my $MSISDN = $rec->{$key1}[0]; # single my @CF = @{$rec->{$key2}}; # multiple for (@CF){ s{(CF.*-ALL-PROV)-NONE.*}{$1-1/1/1/0}; s{(CF.*-TS10-(?:REG|ACT))-91(\d*).*}{$1-1/1/1/0-$2}; } print join $sep,$MSISDN,@CF; print "\n"; }
    poj