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

Hello , Can someone help me fix this , I read a file input contain the following :
-- Name: Menu -- TOYS: car monkey moon blblblb blblb
I need to get the name and the toyes , since they can be one word or more , I am putting it into array : I have done the following :
#set some local variables local ($name,$toys); open ( INPUT , "<input" ) or die " couldn't open\n"; while(<INPUT>) { @name=split(" ",$_) if /^-- NAME:\s(\w+)/; @toys=split(" ",$_) if /^-- TOYS:\s(\w+)/; } close(INPUT); foreach $name (@name) { print "the name $name\n"; } foreach $toys (@toys) { print "the toys $toys\n"; }
it get me all the other junk , here is the output:
the name -- the name NAME: the name Menu the neme the toys -- the toys TOYS: the toys CAR
I don't need NAME: and TOYS: and --- to be in the array .. HELP ?

Replies are listed 'Best First'.
Re: array issue
by Zaxo (Archbishop) on Jan 23, 2004 at 19:37 UTC

    By your description, you want a hash of arrays. You can decode your data lines with a single regex like so,

    my %data; while (<INPUT>) { if (/^-- (\w+):(.*)/) { push @{$data{$1}}, split " ", $2; } }
    The messy-looking array argument of push makes the value an array reference and avoids throwing away previous values. That way your data is permitted to continue a key later.

    After Compline,
    Zaxo

Re: array issue
by Aragorn (Curate) on Jan 23, 2004 at 19:51 UTC
    First of all, local doesn't do what you'd expect. Change the local to my and you'll be ok. See local and my for the details.

    You're inadvertently just splitting the input line on a space and putting the elements in an array. What you need to do is split the value of $1 (which is the string matched by the (\w+) part when you find a line that matches. So that part could be rewritten as

    if (/^-- NAME:\s(.+)/) { @name = split(" ", $1); } if (/^-- TOYS:\s(.+)/) { @toys = split(" ", $1); }
    I'm assuming that after "NAME" there can also be multiple names.

    Arjen

      First of all, local doesn't do what you'd expect.

      I was going to point this out myself, if you hadn't already done so. The comment seems to indicate that the OP thinks local scopes lexically, which is wrong.

      Change the local to my and you'll be ok.

      Actually, in this specific case, I don't think the scoping of those variables matters. Unless there's a lot of stuff going on we don't know about in parts of the code that weren't shown, they could just as well be left in package scope. Still, the OP would do well to learn the difference between lexical and dynamic scoping (my and local, respectively), since in many cases they do indeed have drastically different effects.


      $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
Re: array issue
by Limbic~Region (Chancellor) on Jan 23, 2004 at 19:43 UTC
    Anonymous Monk,
    This code assumes that you will only have one "NAME" and one "TOYS" line per file since you don't say otherwise. It also uses a HoA for the datastructure since it fits your data more closely.
    #!/usr/bin/perl -w use strict; my $file = 'file.txt'; open (INPUT , '<' , $file) or die "Unable to open $file for reading : +$!"; my %data; while ( <INPUT> ) { if ( /-- Name:/ || /-- TOYS:/ ) { chomp; my $key = /-- Name:/ ? 'name' : 'toys'; my @temp = split /\s+/; push @{ $data{$key} } , @temp[2 .. $#temp]; last if exists $data{name} && exists $data{toys}; } } for my $key ( qw(name toys) ) { print "$key :\n"; print "\t$_\n" for @{ $data{$key} }; } __END__ name : Menu toys : car monkey moon
    Cheers - L~R
Re: array issue
by derby (Abbot) on Jan 23, 2004 at 19:40 UTC
    Well sure it gives you the rest of the junk ... that's what you asked for:

    while lines in file if line begins with '-- NAME:' and space followed by words split line on space and add to name array

    You're going to have to get rid of the junk - either before you split (ala split on ':', then split on space. Or splice the array if you now the first two items are always going to contain junk.

    -derby