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

I have a script that prints data sucessfully to a file. The file is in the form of:
USER 1 LOGIN date MORE info MORE info USER 2 LOGIN date MORE info MORE info USER3 etc...
what I want to do, and this part is easy is to do is
__BEGIN__ USER 1 LOGIN date MORE info MORE info __END__ __BEGIN__ USER 2 LOGIN date MORE info MORE info __END__ __BEGIN__ USER3 etc... __END__
what im having trouble with at the moment is foreach set of __BEGIN__ and __END__ I want to process user1's info, then move onto user2, user3, etc...
Im thinking this is simple, but at the moment am coming up empty. I think Im thinking too hard as to what the problem actually is.
Thanks, Ted

**Update** I did see some nodes with similar problems, but Im not sure they are the same. (or) It could also be that Im just looking at it wrong.

Replies are listed 'Best First'.
Re: parsing a file
by Limbic~Region (Chancellor) on Jan 25, 2005 at 15:44 UTC
    tcf03,
    Your question was a little confusing to me so please forgive me if this solution doesn't do what you want. You said that the file is in the format of not having the __BEGIN__/__END__ blocks, so presumably you want to add them. You also want to process each users information before adding them. Something like the following should work
    #!/usr/bin/perl use strict; use warnings; { local $/ = ""; # paragraph mode while ( <DATA> ) { chomp; process_user( $_ ); print "__BEGIN__\n$_\n__END__\n"; } } sub process_user { my @lines = split /\n/, shift(); for ( @lines ) { # do something with line; print "$_\n"; } } __DATA__ USER 1 LOGIN date MORE info MORE info USER 2 LOGIN date MORE info MORE info USER3 LOGIN data MORE info MORE info

    Cheers - L~R

Re: parsing a file
by friedo (Prior) on Jan 25, 2005 at 15:36 UTC
    You could use a regex to grab each set of everything between __BEGIN__ and __END__.

    # untested my @users = ( $file =~ /__BEGIN__\n(.*?)\n__END__/gs );

    But if I may make a reccomendation, for dealing with this kind of data, I find YAML to be extremely useful and eady.

Re: parsing a file
by holli (Abbot) on Jan 25, 2005 at 16:37 UTC
    Limbic`s code is well written, but i take a different approach.
    Assumptions:
    a) USER, LOGIN and MORE is a kind of markup.
    b) You want to process the data somehow before writing it back.
    c) You are capable to write file in/output by yourself.

    That leads to the following code:
    #!/usr/bin/perl use strict; use warnings; { local $/ = ""; # paragraph mode while ( <DATA> ) { chomp; print process_user($_); } } sub process_user { my ($user, $login, @info) = split ("\n", shift); $user =~ s/USER //; $login =~ s/LOGIN //; @info = map { s/^MORE //; $_ } @info; #sample processing: add a star everywhere return join "\n", "__BEGIN__", "USER *$user", "LOGIN *$login", (map { "MORE *$_" } @info), "__END__\n"; } __DATA__ USER 1 LOGIN date MORE info MORE info USER 2 LOGIN date MORE info MORE info
    That produces this output:
    __BEGIN__ USER *1 LOGIN *date MORE *info MORE *info __END__ __BEGIN__ USER *2 LOGIN *date MORE *info MORE *info __END__
    Update:
    Corrected minor typo and Limbic`s "s".

    holli, regexed monk
Re: parsing a file
by dragonchild (Archbishop) on Jan 25, 2005 at 15:34 UTC
    Have your parsing code call a function that does the processing once it finally has all the data for user N. When building your parsing code, have your processing function print out the Data::Dumper output of the input to the function.

    You might want to look at the flip-flop operator within nested while-loops. Check out This code is just freaky...

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: parsing a file
by Elijah (Hermit) on Jan 25, 2005 at 18:26 UTC
    Unless you really want to for some other reason, there is really no need to add the __BEGIN__ and __END__'s.

    Here is an example of processing each block of info without adding these.

    #!/usr/bin/perl -w use strict; my $block; while (<DATA>) { if (!/^\n/) { $block .= $_; }else{ process($block); undef($block); } } process($block) if ($block); sub process { my $info = shift; print "\n",$info if ($info); } __DATA__ USER 1 LOGIN date MORE info MORE info USER 2 LOGIN date MORE info MORE info
Re: parsing a file
by Random_Walk (Prior) on Jan 25, 2005 at 17:57 UTC

    I am rather late to this party but assuming your records are all seperated by a single blank line and there are no superfluous blank lines then this rather obfu looking one liner will get you there

    perl -pe'BEGIN{print"__BEGIN__\n"}s/^\s*$/__BEGIN__\n__END__\n/;END{pr +int"__END__\n"}' <file>

    Or the mildly shorter but fiercly uglier

    perl -pe'$e="__END__\n";BEGIN{$b="__BEGIN__\n";print$b}s/^\s*$/$b$e/;E +ND{print$e}' <file>

    Cheers,
    R.

    Pereant, qui ante nos nostra dixerunt!
      Random_Walk,
      I believe in addition to adding the __BEGIN__/__END__ tags, tcf03 also wanted to "process" each user's information. The trouble I had was in figuring out what was meant by "process", so I just added a sub that could be changed according to suite one's needs.

      Cheers - L~R

Re: parsing a file
by borisz (Canon) on Jan 25, 2005 at 16:39 UTC
    What about this:
    local $/ = "\n\n"; @users = <DATA>; __DATA__ USER 1 LOGIN date MORE info MORE info USER 2 LOGIN date MORE info MORE info USER3 LOGIN data MORE info MORE info
    Boris
      what should it profit the OP to get this
      $VAR1 = [ 'USER 1 LOGIN date MORE info MORE info ', 'USER 2 LOGIN date MORE info MORE info ', 'USER3 LOGIN data MORE info MORE info ', ' ' ];
      structure?

      holli, regexed monk
        The data is split into peaces that start with every user. As the op requested.
        Boris
Re: parsing a file
by TedPride (Priest) on Jan 25, 2005 at 17:00 UTC
    use strict; my $in = 'sample.txt'; my $out = 'sampleout.txt'; my ($handle, $key, $val); open($handle, $in); # Messy, but I'm assuming a small file... split(/\n\n/, join('', <$handle>)); close($handle); open($handle, ">$out"); for (@_) { print $handle "__BEGIN__\n$_\n__END__\n"; my %hash; for (split(/\n/)) { ($key, $val) = split(/ /); $hash{$key} = $val; } # Do whatever with record data, now inside # %hash as key / value pairs } close($handle);