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

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

Hello, I'm trying to parse without success a file like this into an hash:
--> file begins # This is a comment and after is an empty line # Each key starts on a new line KEY1 => 'VALUE1' # This also is a comment KEY2 => { 'VALUE 21', 'VALUE 22', # Spaces in value are relevant 'VALUE 23' } --> file ends
I can remove comments and empty lines:
my $input_string = ''; while (@lines) { my $line = shift @lines; chomp $line; if ($line) { my @parts = split(/#/,$line); $input_string .= $parts[0]; } }
But I don't know how to split the resulting string into the hash, so that it becomes:
my %inp = ( KEY1 => 'VALUE1', KEY2 => { 'VALUE 21', 'VALUE 22', 'VALUE 23' } )
Thanks for suggestions.

Replies are listed 'Best First'.
Re: Initialize an hash with string
by Eliya (Vicar) on Jun 21, 2012 at 09:41 UTC
    my %inp = ( KEY1 => 'VALUE1', KEY2 => { 'VALUE 21', 'VALUE 22', 'VALUE 23' } )

    Note that hashes hold key-value pairs, i.e. the above structure would be more like

    my %inp = ( KEY1 => 'VALUE1', KEY2 => { 'VALUE 21' => 'VALUE 22', 'VALUE 23' => undef } )

    Not sure if that's what you want.  Maybe you rather want an array(ref) (i.e. KEY2 =>  [ ... ]) here?

      Yes, sorry I mistyped the value of KEY2 that is an array ref as you pointed out.
Re: Initialize an hash with string
by rovf (Priest) on Jun 21, 2012 at 09:30 UTC
    You did not describe the *general* syntax of your file (for example, how keys can look like, and how spacing and lineout can vary), but from your posting, your code looks already very close to Perl. One possibility might be to transform the file into *real* Perl code (putting a comma between the hash elements, and placing a pair of curly brackets around the whole text) and then using do to evaluate the file.

    -- 
    Ronald Fischer <ynnor@mm.st>
      The idea is that the input file has each key on a new line, that the key is a single word and that the values can include spaces that are significant. I can require that all values are within apostrophes.
      If possible, I would not require the comma at the end of the right bracket, that I can probably add when parsing with
      $input_string =~ s/}/},/g;
      I'm trying with eval or with split approaches but I cannot find the solution...
        Assuming (a) the modification proposed by Eliya, and (b) that you placed correctly a comma at the correct places, you end up with a string $s containing the whole content of the file, modified like this:

        " KEY1 => 'VALUE1', \n KEY2 => [ 'VALUE21',\n 'VALUE22'\n'VALUE23' + ]"
        If you manage to come so far, you can easily get a reference to this hash with
        my $hashref=eval "{${s}}"; if($@) { print STDERR "Error in input: $@\n"; } else { # Dump the data just read print("Key $_ has value ",$hashref->{$_},"\n") for keys %$s; }

        So perhaps the most tricky part is to get the commas in the right place. To do this, I would first place a comma in front of every /\b(\S+?\s*=>)/, which leaves us one extra comma near the start of the string, which you have to remove afterwards. Well, maybe there is also a simpler solution to this. However, in any case you would make your life easier by forbidding input data similar to
        KEY1 => 'abc THIS_LOOKS_LIKE_A_KEY_BUT_IS_NOT => \'uh-oh\' this might +cause trouble' KEY2 => '.....'
        -- 
        Ronald Fischer <ynnor@mm.st>
Re: Initialize an hash with string
by Anonymous Monk on Jun 21, 2012 at 12:58 UTC
Re: Initialize an hash with string
by zeni (Beadle) on Jun 21, 2012 at 09:48 UTC
    As i understand, you are splitting firstly for the key-value pair. While storing you can further split this string with "=>" and store them in a hash. But i am still confused about the way your code is extracting the key-value pair.
    Life is a box of chocolates.. Perhaps you get to eat very few best ones!!