-1

i want to read a config file with below format i can able to read the block names i.e. strings,buildid,major how ever i could not able to read values of each block i.e. variables.buildid of value=98 and path of the files igd_version.h ,readme and similarly for Major

[strings]
variables=BUILDID,MAJOR

[BUILDID]
VALUE=898
VERSION.H=/home/src/gd_version.h
readme=/home/src/readm.txt

[MAJOR]
VALUE=8
IGD2.H=/home/src/igd2.h
readme=/home/src/readm.txt
license=/usr/src/license.html

Here is my code

  while (<INFILE>) {
    my $cfgLine = $_;
    next unless ($cfgLine =~ s/\[([^]]+)\]// ) ;
    my $header = $1;
    }

Can some body suggest me any ideas , I have gone through the cpan (config::tiny , config::simple ) could not able because of the design change. Here "VALUE" is common variable for all the blocks and file names and number of files is different for blocks. Here how can we assign the values of IGD2.h and other file names and paths to variables to do verification of file existence.

3 Answers3

3

I agree that using an ini-parsing module as mentioned is preferable. Nevertheless, perhaps the following will be helpful:

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

my ( $block, %hash );

while (<DATA>) {
    $block = $1 if /\[(.+)\]/;
    $hash{$block}{$1} = $2 if /(\S+?)\s*=\s*(\S+)/ and defined $block;
}

print Dumper \%hash;
__DATA__
[strings]
variables=BUILDID,MAJOR

[BUILDID]
VALUE=898
VERSION.H=/home/src/gd_version.h
readme=/home/src/readm.txt

[MAJOR]
VALUE=8
IGD2.H=/home/src/igd2.h
readme=/home/src/readm.txt
license=/usr/src/license.html

Output:

$VAR1 = {
          'BUILDID' => {
                         'VERSION.H' => '/home/src/gd_version.h',
                         'VALUE' => '898',
                         'readme' => '/home/src/readm.txt'
                       },
          'MAJOR' => {
                       'VALUE' => '8',
                       'readme' => '/home/src/readm.txt',
                       'license' => '/usr/src/license.html',
                       'IGD2.H' => '/home/src/igd2.h'
                     },
          'strings' => {
                         'variables' => 'BUILDID,MAJOR'
                       }
        };

To access individual values, use the following pattern:

my $value = $hash{'block'}{'key'};

For example:

print $hash{'BUILDID'}{'VERSION.H'};

Output:

/home/src/gd_version.h

In case you would like to try a different ini module, Config::IniFiles will create the same HoH as above:

use strict;
use warnings;
use Config::IniFiles;

tie my %hash, 'Config::IniFiles', ( -file => 'config.ini' );
print $hash{'BUILDID'}{'VERSION.H'};

Output:

/home/src/gd_version.h

If you don't know the contents of the ini file, but still want to access the values, you can do the following--given the HoH from above:

for my $block ( keys %hash ) {
    print "$block:\n";
    for my $key ( keys %{ $hash{$block} } ) {
        print "$key => $hash{$block}{$key}\n";
    }
    
    print "\n";
}

Output:

BUILDID:
VERSION.H => /home/src/gd_version.h
VALUE => 898
readme => /home/src/readm.txt

MAJOR:
VALUE => 8
readme => /home/src/readm.txt
license => /usr/src/license.html
IGD2.H => /home/src/igd2.h

strings:
variables => BUILDID,MAJOR

Instead of using a tied hash, you can use Config::IniFiles's methods for data access:

use strict;
use warnings;
use Config::IniFiles;

my $cfg = Config::IniFiles->new( -file => 'File.txt' );

my @sections = $cfg->Sections;

for my $section (@sections) {
    print $section, "\n";
    my @parms = $cfg->Parameters($section);
    for my $param (@parms) {
        print "$param => ", $cfg->val( $section, $param ), "\n";
    }

    print "\n";
}

Output:

strings
variables => BUILDID,MAJOR

BUILDID
VALUE => 898
VERSION.H => /home/src/gd_version.h
readme => /home/src/readm.txt

MAJOR
VALUE => 8
IGD2.H => /home/src/igd2.h
readme => /home/src/readm.txt
license => /usr/src/license.html
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Kenosis
  • 6,196
  • 1
  • 16
  • 16
  • Kenosis:useful information you have provided however in each and everyblock only "VALUE" is the fixed parameter name and others are random i mean it can be anything in that case how can i assign that to a fixed parameter there can be many number of files in one block. – user3237127 Jan 26 '14 at 22:38
  • @user3237127 - Will add code to show you how to access these values. – Kenosis Jan 26 '14 at 22:41
  • Kenosis: thank so much can i have your mail id i need to have more information on accessing the key values and validating the values based on keys.. – user3237127 Jan 27 '14 at 00:24
  • @user3237127 - You're most welcome. However, since others may benefit from this dialog, let's continue in comments and perhaps the extended chat. – Kenosis Jan 27 '14 at 00:30
  • Config::IniFiles is good to use and easy for maintenance however the script is going to run on many platform (different client machines )here recommended to use the minimal Perl modules ,Can you give me some more methods to extract the values based on keys. – user3237127 Jan 27 '14 at 01:32
  • @user3237127 - Accessing different parts of a HoH has been addressed multiple times at SO. The following, which deals with a HOH structure like your, may be helpful: [How to extract key name from a hash of hash?](http://stackoverflow.com/questions/16796501/how-to-extract-key-name-from-a-hash-of-hash/16796617). [Config::IniFiles](http://search.cpan.org/~shlomif/Config-IniFiles-2.82/lib/Config/IniFiles.pm) is written in Perl, so it's likely you'll have little problem with its use on different client machines. Have added some different access methods above, from the module's site. – Kenosis Jan 27 '14 at 03:45
2

If parsing that configuration is not your central task, please consider using a module from CPAN. Config::Ini or Config::Simple instead of reinventing that particular wheel.

If you still want to do the parsing yourself, there are three kinds of lines that you'll want to recognize:

  1. Sections which can be described by a regexp similar to ^\s*\[(.*)\]\S*$
  2. Declarations which can be described by a regexp similar to ^\s*(.*?)\s*=\s*(.*?)\s*$
  3. Everything else.

(Strictly speaking, you'll want to use a different character class than . for section names, keys, values.)

Here's what I have come up with:

my $section;
while (<STDIN>) {
    chomp;
    if (/^\s*\[(.*)\]\S*$/) {
        $section = $1;
        next;
    } elsif (/^\s*(.*?)\s*=\s*(.*?)\s*$/) {
        my ($key, $value) = ($1, $2);
        say "--- $section . $key = $value";
    } else {
        # invalid
        next;
    }
}
hillu
  • 9,423
  • 4
  • 26
  • 30
0

Take a look at this program lines 328 to 371. This is me parsing a file in Windows INI format.

The SECTION_LINE and PARAMTER_LINE are constants defined on lines #14 and #15. I did this because I wanted to make sure this hook would work with just the standard Perl modules. Otherwise, I too would have used Config::INI to do the parsing. This is especially true if you truly believe this is an important part of your program.

The important thing to note is that I compare lines to two different regular expressions. One is the section heading line ([...]) and one is the parameter line (...=....). I also have to track when a section begins and ends (if I get another section heading line, the previous section ends, or if I hit the end of file), and tracking which parameters went with which sections. I use Perl objects for keeping the parameters with their sections, if you don't use objects, you'll have to use complex Perl hashes of hashes to track the values.

Also don't forget error tracking. Take a good look at my code, and then realize it's much easier to use Config::INI to handle this task for you.

As I said, the only reason I didn't use something like Config::INI is I had to make sure my code works with just the standard Perl modules. This code runs on systems that the user doesn't control and it can be very difficult to add optional CPAN Perl modules in this situation. You usually have to make a request, wait for it to be filled, and in many situations, depend upon people who don't know how to do this.

David W.
  • 105,218
  • 39
  • 216
  • 337