Take the 2-minute tour ×

I have a .sql file from which I am reading my input. Suppose the file contains the following input....

Message Fruits Fruit="Apple",Color="Red",Taste="Sweet";

Message Flowers Flower="Rose",Color="Red";

Now I have written a perl script to generate hash from this file..

use strict;
use Data::Dumper;

    my @stack;
    my %hash;
    push @stack,\%hash;
    my @file = <MYFILE>;
    foreach my $row(@file){
        if($row =~ /Message /){
            my %my_hash;
            my @words = split(" ",$row);
            my @sep_words = split(",",$words[2]);

            foreach my $x(@sep_words){
                my($key,$value) = split("=",$x);
                $my_hash{$key} = $value;
            push @stack,$stack[$#stack]->{$words[1]} = {%my_hash};
            pop @stack;
    print Dumper(\%hash);

I am getting the following output..

$VAR1 = {
          'Flowers' => {
                         'Flower' => '"Rose"',
                         'Color' => '"Red";'
          'Fruits' => {
                        'Taste' => '"Sweet";',
                        'Fruit' => '"Apple"',
                        'Color' => '"Red"'

Now here the hash is not preserving the order in which the input is read.I want my hash to be in the same order as in input file. I have found some libraries like Tie::IxHash but I want to avoid the use of any libraries.Can anybody help me out???

share|improve this question
Hashes by their nature do not preserve order. If you need the order preserved, look at using an array to keep the order. –  Joel Feb 4 '13 at 17:23
What do you have against CPAN modules? –  cjm Feb 4 '13 at 17:25
There is nothing against CPAN modules....just trying to find another way of doing it...without the usage of any libraries. –  Maverick Feb 4 '13 at 17:39
To add to @Joel's comment, the reason Tie::IxHash can preserve order is because it's not a hash at all. –  ikegami Feb 4 '13 at 19:10

3 Answers 3

For a low key approach, you could always maintain the keys in an array, which does have an order.

foreach my $x(@sep_words){
    $my_hash{$key} = $value;

And then to extract, iterate over the keys

foreach my $this_key (@list_keys) {
    # do something with $my_hash{$this_key}

But that does have the issue of, you're relying on the array of keys and the hash staying in sync. You could also accidentally add the same key multiple times, if you're not careful.

share|improve this answer
The right way of doing it –  Borodin Feb 4 '13 at 18:10
@Borodin, No, not always. It doesn't provide a good way of inserting and deleting keys. –  ikegami Feb 4 '13 at 19:08
Yes, I wouldn't want to use it outside of fairly simple scenarios but in those, I think it's dine –  Disco 3 Feb 4 '13 at 19:21

Joel has it correct - you cannot reliably trust the order of a hash in Perl. If you need a certain order, you'll have to store your information in an array.

share|improve this answer

A hash is a set of key-value pairs with unique keys. A set is never ordered per se.

An array is a sequence of any number of scalars. An array is ordered per se, but uniqueness would have to be enforced externally.

Here is my take on your problem:


use strict; use warnings;
use Data::Dumper;

local $/ = ";\n";

my @messages;

while (<DATA>) {
    my ($msg, $to, $what) = split ' ', $_, 3; # limit number of fragments.
    my %options;
    while($what =~ /(\w+) = "((?:[^"]++|\\.)*)" (?:,|$)/xg) {
        $options{$1} = $2;
    push @messages, [$to => \%options];

print Dumper \@messages;


I put the messages into an array, because it has to be sorted. Also, I dont do weird gymnastics with a stack I don't need.

I don't split on all newlines, because you could have quoted value that contain newlines. For the same reason, I don't blindly split on , or = and use a sensible regex. It may be worth adding error detection, like die if not defined pos $what or pos($what) != length($what); at the end (requires /c flag on regex), to see if we actually processed everything or were thrown out of the loop prematurely.

This produces:

$VAR1 = [
      [ 'Fruits',
          'Taste' => 'Sweet',
          'Fruit' => 'Apple',
          'Color' => 'Red'
      [ 'Flowers',
          'Flower' => 'Rose',                                               
          'Color' => 'Red'                                                  

(with other indenting, but that's irrelevant).

One gotcha exists: The file has to be terminated by a newline, or the last semicolon isn't caught.

share|improve this answer
This is unnecessarily complex. The OP didn't mention anything about the values containing newlines or escaped characters, or the data containing multi-line records. And what is [^"]++ about? Furthermore the end result is a data structure that has to be searched for the required entry instead of just being accessible by its key. –  Borodin Feb 4 '13 at 18:13
@Borodin That is right, there was no such mention, but I assumed the code in the question wasn't completely thought out; and forgetting the exact spec for the data when using split is a common error. The ++ prevents backtracking, but isn't actually neccessary here. In any case, it is trivial to remove these complexities, but I wouldn't want to answer with possibly dangerous code –  amon Feb 4 '13 at 18:19

Your Answer


