What is the best way to parse INI file in Perl and convert it to hash?
问题:
回答1:
I prefer to use Config::IniFiles module.
回答2:
If you like more perlish style then try Tie::Cfg
. Sample:
tie my %conf, 'Tie::Cfg', READ => "/etc/connect.cfg";
$conf{test}="this is a test";
回答3:
The best way is to make use of available modules in CPAN as what others have suggested. Below is just for your own understanding, let's say you have ini file like this:
$ more test.ini
[Section1]
s1tag1=s1value1 # some comments
[Section2]
s2tag1=s2value1 # some comments
s2tag2=s2value2
[Section3]
s3tag1=s3value1
you can do your own parsing w/o modules by using just Perl's regex (or string methods) + data structures like hashes.
Sample Code:
$ini="test.ini";
open (INI, "$ini") || die "Can't open $ini: $!\n";
while (<INI>) {
chomp;
if (/^\s*\[(\w+)\].*/) {
$section = $1;
}
if (/^\W*(\w+)=?(\w+)\W*(#.*)?$/) {
$keyword = $1;
$value = $2 ;
# put them into hash
$hash{$section} = [ $keyword, $value];
}
}
close (INI);
while( my( $k, $v ) = each( %hash ) ) {
print "$k => " . $hash{$k}->[0]."\n";
print "$k => " . $hash{$k}->[1]."\n";
}
output
$ perl perl.pl
Section1 => s1tag1
Section1 => s1value1
Section3 => s3tag1
Section3 => s3value1
Section2 => s2tag2
Section2 => s2value2
回答4:
Config::Tiny is very easy and straightforward to use.
$Config = Config::Tiny->read( 'file.conf' );
my $one = $Config->{section}->{one};
my $Foo = $Config->{section}->{Foo};
回答5:
Try this module from CPAN: Config::INI::Reader
回答6:
Nitpicking on the above:
Tie::Cfg, as downloaded from CPAN, does not handle sections and keys that may have spaces in them. It needs to be changed by adding quotes (") around the "keys" when setting the hash entries for both the section and the keys within the sections. The files I'm trying to read were generated by MS Windows' folks, and thus have plenty of spaces to go around.
Config::Tiny, Config::IniFiles are fussy about the format. If any line is not of the form [section] or key=val, they throw an error and one can't get to the hashes, which, at least in Config::Files, are filled in properly anyway. It would be nice to have an ignore error option. The files I'm trying to read have some spurious M4 lines in it, that I could run through m4 to get rid of, but that is not necessary in what I'm trying to do with this particular script.
回答7:
reading and write function for ini file edit:
sub iniRead
{
my $ini = $_[0];
my $conf;
open (INI, "$ini") || die "Can't open $ini: $!\n";
while (<INI>) {
chomp;
if (/^\s*\[\s*(.+?)\s*\]\s*$/) {
$section = $1;
}
if ( /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ ) {
$conf->{$section}->{$1} = $2;
}
}
close (INI);
return $conf;
}
sub iniWrite
{
my $ini = $_[0];
my $conf = $_[1];
my $contents = '';
foreach my $section ( sort { (($b eq '_') <=> ($a eq '_')) || ($a cmp $b) } keys %$conf ) {
my $block = $conf->{$section};
$contents .= "\n" if length $contents;
$contents .= "[$section]\n" unless $section eq '_';
foreach my $property ( sort keys %$block ) {
$contents .= "$property=$block->{$property}\n";
}
}
open( CONF,"> $ini" ) or print("not open the file");
print CONF $contents;
close CONF;
}
sample usage:
read conf file and saved to hash
$conf = iniRead("/etc/samba/smb.conf");
change the your config attribute or added new config attributes
edit
$conf->{"global"}->{"workgroup"} = "WORKGROUP";
added new config
$conf->{"www"}->{"path"}="/var/www/html";
saved your new configuration to file
iniWrite("/etc/samba/smb.conf",$conf);