简体   繁体   中英

Perl: Using the flip flop function and extracting data from within the block read

I have an array called @mytitles which contains a lot of titles such as, say, title1 , title2 and so on. I have a file called " Superdataset " which has information pertaining to each title. However, the info related to title1 may be of 6 lines while the info for title2 may be 30 lines (its random). Each piece of information (for a titlex ) starts with " Reading titlex " and ends with " Done reading titlex ".

From these lines of information of each title, I need to extract some data. I think its lucky that this data I need is in the 2 lines just before " Done reading titlex " each time

So my " Superdataset " looks like:

Reading title1  
 random info line1
 random info line2
 random info line3
 random info line4
 random info line5
 my earnings are 6000
 my expenses are 1000
Done reading title1
Reading title2
 random info line6
 random info line7
 random info line8
 random info line9
 random info line10
 random info line11
 random info line12
 random info line13
 random info line14
 my earnings are 11000
 my expenses are 9000
Done reading title2

I need a total sum of expenses and a total sum of earnings. Any suggestions? PS-the array has complicated names, not something as simple as titlex

Here is a first pass at slurping the data into a usable form.

use warnings;
use strict;
use autodie;

my $input_filename = 'example';
open my $input, '<', $input_filename;
my %data;
{
  my $current_title;

  while(<$input>){
    chomp;
    if( /^Reading (.*?)\s*$/ ){ # start of section
      $current_title = $1;
    }elsif( not defined $current_title ){ # outside of any section
      # invalid data
    }elsif( /^Done reading (.*)/ ){ # end of section
      die if $1 ne $current_title;
      $current_title = undef;
    }else{ # add an element of section to array
      push @{ $data{$current_title} }, $_;
    }
  }
}
close $input;

Using the created data structure to determine the total earnings, and expenses.

my( $earnings, $expenses );
for my $list( values %data ){
  for( @$list ){
    if( /earnings are (\d+)/ ){
      $earnings += $1;
    }elsif( /expenses are (\d+)/ ){
      $expenses += $1;
    }
  }
}

print "earnings $earnings\n";
print "expenses $expenses\n";

To instead print it out in a form more useful to a computer.

use YAML 'Dump';
print Dump \%data;
---
title1:
  - ' random info line1'
  - ' random info line2'
  - ' random info line3'
  - ' random info line4'
  - ' random info line5'
  - ' my earnings are 6000'
  - ' my expenses are 1000'
title2:
  - ' random info line6'
  - ' random info line7'
  - ' random info line8'
  - ' random info line9'
  - ' random info line10'
  - ' random info line11'
  - ' random info line12'
  - ' random info line13'
  - ' random info line14'
  - ' my earnings are 11000'
  - ' my expenses are 9000'

Using the 'range' operator you could do:

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my $begin_stanza = qr/^Reading/i;
my $endof_stanza = qr/^Done reading/i;
my ( $title, @lines );
my ( $value, $total_earnings, $total_expenses );
while (<DATA>) {
    chomp;
    if ( m{$begin_stanza} .. m{$endof_stanza} ) {
        if ( m{$begin_stanza\s+(.+)} ) {
            $title = $1;
            @lines = ();
            next;
        }
        if ( m{$endof_stanza} ) {
            ($value) = ( $lines[0] =~ m{(\d+)} );
            $total_earnings += $value;
            ($value) = ( $lines[1] =~ m{(\d+)} );
            $total_expenses += $value;
            print join "\n", $title, @lines, "\n";
            next;
        }
        shift @lines if @lines == 2;
        push  @lines, $_;
    }
}
printf "Total Earnings = %7d\n", $total_earnings;
printf "Total Expenses = %7d\n", $total_expenses;
__DATA__
Reading title1
 random info line1
 random info line2
 random info line3
 random info line4
 random info line5
 my earnings are 6000
 my expenses are 1000
Done reading title1
Reading title2
 random info line6
 random info line7
 random info line8
 random info line9
 random info line10
 random info line11
 random info line12
 random info line13
 random info line14
 my earnings are 11000
 my expenses are 9000
Done reading title2

...which yields:

title1
 my earnings are 6000
 my expenses are 1000

title2
 my earnings are 11000
 my expenses are 9000

Total Earnings =   17000
Total Expenses =   10000

Unless you can predict what the line before the relevant lines are, the flip-flop operator won't do much good by way of optimization. I think it would be easier to work with a buffer array and just match for the line after the earnings and expenses.

#!/usr/bin/perl
use strict;
use warnings;

my @buffer;
my ($earnings, $expenses);

for my $line (<DATA>) {
    shift @buffer if @buffer > 2;
    push @buffer, $line;

    next if $line !~ /^Done reading/;

    $earnings += $1 if $buffer[0] =~ /(\d+)$/;
    $expenses += $1 if $buffer[1] =~ /(\d+)$/;
}
print "Total earnings: $earnings\n";
print "Total expenses: $expenses\n";

__DATA__
Reading title1  
 random info line1
 random info line2
 random info line3
 random info line4
 random info line5
 my earnings are 6000
 my expenses are 1000
Done reading title1
Reading title2
 random info line6
 random info line7
 random info line8
 random info line9
 random info line10
 random info line11
 random info line12
 random info line13
 random info line14
 my earnings are 11000
 my expenses are 9000
Done reading title2

Output:

Total earnings: 17000
Total expenses: 10000

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM