我希望将具有多个分隔符的单个字符串转换为key =>值哈希结构。有没有一种简单的方法来实现这一目标?我目前的实施是:
sub readConfigFile() {
my %CONFIG;
my $index = 0;
open(CON_FILE, "config");
my @lines = <CON_FILE>;
close(CON_FILE);
my @array = split(/>/, $lines[0]);
my $total = @array;
while($index < $total) {
my @arr = split(/=/, $array[$index]);
chomp($arr[1]);
$CONFIG{$arr[0]} = $arr[1];
$index = $index + 1;
}
while ( ($k,$v) = each %CONFIG ) {
print "$k => $v\n";
}
return;
}
其中'config'包含:
pub=3>rec=0>size=3>adv=1234 123 4.5 6.00
pub=1>rec=1>size=2>adv=111 22 3456 .76
最后的数字也需要删除,并保存在一个单独的key =&gt;值对中,其名称可以是'ip'。 (如果不使代码过于冗长和复杂,我无法做到这一点。)
答案 0 :(得分:4)
您的配置数据结构应该是什么样的?到目前为止,解决方案只记录最后一行,因为每次添加记录时它们都会在相同的散列键上踩踏。
这可能会让您更接近,但您仍需要弄清楚数据结构应该是什么。
我将文件句柄作为参数传递,因此我的子例程与获取数据的特定方式无关。它可以来自文件,字符串,套接字,甚至是 DATA 下面的东西。
在解析字符串后,我没有修复问题,而是在解析字符串之前将字符串修复为“ip”元素。一旦我这样做,“ip”元素不是一个特例,它只是一个双重分裂的问题。这是一项非常重要的技术,可以节省大量的工作和代码。
我在子例程中创建一个哈希引用,并在完成后返回该哈希引用。我不需要全局变量。 :)
use warnings; use strict; use Data::Dumper; readConfigFile( \*DATA ); sub readConfigFile { my( $fh ) = shift; my $hash = {}; while( <$fh> ) { chomp; s/\s+(\d*\.\d+)$/>ip=$1/; $hash->{ $. } = { map { split /=/ } split />/ }; } return $hash; } my $hash = readConfigFile( \*DATA ); print Dumper( $hash ); __DATA__ pub=3>rec=0>size=3>adv=1234 123 4.5 6.00 pub=1>rec=1>size=2>adv=111 22 3456 .76
这为您提供了一个数据结构,其中每一行都是一个单独的记录。我选择记录的行号($.
)作为顶级键,但您可以使用您喜欢的任何内容。
$VAR1 = { '1' => { 'ip' => '6.00', 'rec' => '0', 'adv' => '1234 123 4.5', 'pub' => '3', 'size' => '3' }, '2' => { 'ip' => '.76', 'rec' => '1', 'adv' => '111 22 3456', 'pub' => '1', 'size' => '2' } };
如果这不是您想要的结构,请告诉我们您最终想要的结果,我们可以调整我们的答案。
答案 1 :(得分:2)
我假设您想要阅读和解析超过1行。所以,我选择将值存储在AoH中。
#!/usr/bin/perl use strict; use warnings; my @config; while (<DATA>) { chomp; push @config, { split /[=>]/ }; } for my $href (@config) { while (my ($k, $v) = each %$href) { print "$k => $v\n"; } } __DATA__ pub=3>rec=0>size=3>adv=1234 123 4.5 6.00 pub=1>rec=1>size=2>adv=111 22 3456 .76
这导致下面的打印输出。 (上面的while循环从DATA读取。)
rec => 0 adv => 1234 123 4.5 6.00 pub => 3 size => 3 rec => 1 adv => 111 22 3456 .76 pub => 1 size => 2
克里斯
答案 2 :(得分:1)
配置文件格式是次优的,我们应该说。也就是说,有更简单的格式来解析和理解。 [已添加:但格式已由其他程序定义。 Perl足够灵活,可以解决这个问题。]
当没有实际需要时,您的代码会篡改文件。
您的代码只关注文件中的最后一行数据(正如Chris Charley在我输入时所说的那样)。
您也不允许使用注释行或空行 - 在任何配置文件中都是一个好主意,并且它们很容易支持。 [添加:再次,使用预定义格式,这几乎不相关,但是当您设计自己的文件时,请记住它。]
这是将你的功能改编成更为惯用的Perl。
#!/bin/perl -w
use strict;
use constant debug => 0;
sub readConfigFile()
{
my %CONFIG;
open(CON_FILE, "config") or die "failed to open file ($!)\n";
while (my $line = <CON_FILE>)
{
chomp $line;
$line =~ s/#.*//; # Remove comments
next if $line =~ /^\s*$/; # Ignore blank lines
foreach my $field (split(/>/, $line))
{
my @arr = split(/=/, $field);
$CONFIG{$arr[0]} = $arr[1];
print ":: $arr[0] => $arr[1]\n" if debug;
}
}
close(CON_FILE);
while (my($k,$v) = each %CONFIG)
{
print "$k => $v\n";
}
return %CONFIG;
}
readConfigFile; # Ignores returned hash
现在,您需要更清楚地解释最后一个字段的结构是什么,以及为什么没有key = value表示法的'ip'字段。一致性使每个人的生活更轻松。您还需要考虑应该如何处理多行。我会探索使用更正统的符号,例如:
pub=3;rec=0;size=3;adv=(1234,123,4.5);ip=6.00
结肠或分叉作为分隔符是相当传统的;列表中逗号分隔项周围的括号不是一个令人发指的惯例。一致性至关重要。爱默生说:“愚蠢的一致性是小脑袋的大人物,受到小政治家,哲学家和神职人员的崇拜”,但计算机科学的一致性对每个人都是一个很大的好处。
答案 3 :(得分:1)
以下假设分隔符保证为&gt;,并且数据中不会出现这种情况。
我只是根据'&gt;'拆分每一行。最后一个值将包含一个键=值对,然后是一个空格,然后是IP,因此将此分配在/ /上一次(限制2),然后得到k = v和IP。将IP保存到散列并保持数组中的k = v对,然后遍历数组并在'='上拆分k = v。
填写hashref并将其推送到更高范围的数组。这将在完成后包含您的hashrefs。
(将配置加载到数组中)
my @hashes;
for my $line (@config) {
my $hash; # config line will end up here
my @pairs = split />/, $line;
# Do the ip first. Split the last element of @pairs and put the second half into the
# hash, overwriting the element with the first half at the same time.
# This means we don't have to do anything special with the for loop below.
($pairs[-1], $hash->{ip}) = (split / /, $pairs[-1], 2);
for (@pairs) {
my ($k, $v) = split /=/;
$hash->{$k} = $v;
}
push @hashes, $hash;
}
答案 4 :(得分:0)
这是一种方式。
foreach ( @lines ) { chomp; my %CONFIG; # Extract the last digit first and replace it with an end of # pair delimiter. s/\s*([\d\.]+)\s*$/>/; $CONFIG{ip} = $1; while ( /([^=]*)=([^>]*)>/g ) { $CONFIG{$1} = $2; } print Dumper ( \%CONFIG ); }