如何将这些字符串转换为Perl中的哈希?

时间:2008-11-08 18:52:19

标签: perl string parsing hash

我希望将具有多个分隔符的单个字符串转换为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'。 (如果不使代码过于冗长和复杂,我无法做到这一点。)

5 个答案:

答案 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 );
}