http://qs321.pair.com?node_id=80542

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

I have a Perl script that is being deployed at several different sites. To allow each site to customize the appearance of the HTML generated, I have an INI file with a number of NAME=VALUE pairs. What I'm wondering is if there is a way to assign a value to a variable name that is stored in a variable. For example, if I have the following in my INI file:

$NAME=John Smith

I'd like to be able to read $NAME into $var1 and John Smith into $var2, and then assign what's in $var2 to the variable name stored in $var1.

I can currently work around this. Here is my script thus far:

sub read_ini() { my @file = read_file("recorder.ini"); my $line = 0; my $pos = 0; my $var = ""; my $val; for(@file) { if(substr(@file[$line],0,1) ne "#") { $pos = index(@file[$line],"="); if($pos > 0) { $var = substr(@file[$line],0,$pos); $val = substr(@file[$line],$pos + 1, length(@file[$line]) - $pos); if($var eq "NAME") { $NAME = $val; } elsif($var eq "OFFICE") { $OFFICE = $val; } # And so on for other instances } } $line++; } }

Can I use a hash to accomplish what I described above? I'm sorry for sounding ignorant, but I'm still new to Perl.

Thanks!

Replies are listed 'Best First'.
Re: Reading from an INI file
by mothra (Hermit) on May 15, 2001 at 18:50 UTC
    Before you reinvent the wheel (and make all the same mistakes that somebody else already has along the way), you may well be better off using Config::IniFiles.

    HTH!

      And if you're using Perl on a Windows OS, as an alternative to Config::IniFiles I recommend that you look into Dave Roth's really useful Win32::AdminMisc module. It includes a ReadINI (and a WriteINI) function that "understands" section dividers, and ignores lines commented out with semi-colons. There's online documentation at http://www.roth.net/perl/adminmisc/, but here's an example of how it could be used:
      foreach my $section (Win32::AdminMisc::ReadINI ('some.ini', '', '')) { foreach my $key (Win32::AdminMisc::ReadINI ('some.ini', $section, '')) { my $value = Win32::AdminMisc::ReadINI ('some.ini', $section, $key); } }
      (Note: if you want to use AdminMisc, either use PPM (if you're using ActiveState), or else use the links at Roth's site. It's on CPAN but http://search.cpan.org for some reason doesn't return a match.)

      Finally, in the book Data Munging in Perl (written by davorg), there's a longish discussion about using the high-powered Parse::RecDescent module for parsing INI files.

      -- Frag.

      But, you do learn a lot reinventing the wheel, sometimes it is good to write it out... especially short things like this.

      Of course, reusable code is always a Good Thing too :)
                      - Ant

        But, you do learn a lot reinventing the wheel, sometimes it is good to write it out...

        True, but there are many more problems that people either a.) haven't solved, b.) have solved poorly and so could be solved in a better way. For a more thorough understanding of why one shouldn't reinvent the wheel, look here.

Re: Reading from an INI file
by suaveant (Parson) on May 15, 2001 at 18:47 UTC
    yes, there is, you can do
    ${'STR1'} = 'STR2';
    to assign string STR2 to $STR1, however, you are absolutely right that a hash can accomplish what you want, and is in fact a much better way to do it than assigning random variables, especially since it prevents you from overwriting your own program variables :)
    The way I would do it is this...
    my %info; for(@file) { next if /^#/; skip line if it starts with a hash chomp; # remove \n Update ACK! forgot this! Doh! my($name,$val) = split '=', $_, 2; #split line into two # values, on an = sign next unless $val; # make sure you got something $info{$name} = $val; } print "$info{OFFICE} - $info{NAME}\n";
    very simple. you can also check for values by doing something like
    if(exists $info{OFFICE}) { print "Ofiice: $info{OFFICE}\n"; }
    and you can get a list of all your info variables by doing
    @keys = keys %info;
    try it, and use this to see the results...
    for(sort keys %info) { print "$_: $info{$_}\n"; }
    That will print a sorted list of all your keys with their values Update forgot to chomp... fixed it... oops
                    - Ant
Re: Reading from an INI file
by jeroenes (Priest) on May 15, 2001 at 18:52 UTC
    Hashes are much better here! Main reason: you don't want to pollute your namespace with names that can pop up in those INI files. Well, actually, you want it now, but after some experience with the results, you will want that you didn't want it now. {grin}

    With an example:

    my %ini; @file = grep m/^[^#]/, @file; #skips comments for (@file){ chomp; #mpolo is right! my ($key, $val) = split /=/,2; #splits the line $ini{ $key } = $val if $key; #if we have a key, store }
    In the remainder of the code, use $ini{$key} to access the ini values.

    You can read up in perldata.

    Hope this helps,

    Jeroen
    "We are not alone"(FZ)

Re: Reading from an INI file
by mpolo (Chaplain) on May 15, 2001 at 18:52 UTC
    Yes, a hash can accomplish this: In your file, you can leave off the $ signs, as they will be unnecessary. Then:
    open FILE, "recorder.ini"||die "file open failed\n"; chomp(@lines=<FILE>); foreach(@lines) { ($key,$value)=split(/=/,$_); $hash{$key}=$value; }
    Now where you would have used $NAME, you can use $hash{'NAME'}. (If I were you, I'd lowercase the key values to make typing easier.)
Re: Reading from an INI file
by suaveant (Parson) on May 15, 2001 at 18:55 UTC
    Another thing altogether, when you do
    for(@file) { ... }
    each element of @file is placed one by one in order into the variable $_, so you don't need to keep the $line count or access the lines from the hash, you can just work on the value in $_

    you can also do

    for $line (@file) { ... }
    which will put the line in $line instead of $_

    when you put arrays in a for loop, be mindful that any changes you make to the variable will be made to the item in the array, sometimes that is good, sometimes its not what you want :)

    Also I should probably mention that in my previous response when I used /^#/ that is a regular expression which is acting on $_, the current line of the file. If you use my example and put the lines in $line, you would need to make /^#/ into $line =~ /^#/
    to make the regexp look at $line instead of $_
                    - Ant