3

我希望将具有多个分隔符的单个字符串转换为 key=>value 哈希结构。有没有一种简单的方法可以做到这一点?我目前的实现是:

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;
}

其中“配置”包含:

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=>value 对中,其名称可以是 'ip'。(如果不使代码过于冗长和复杂,我无法做到这一点)。

4

5 回答 5

4

您的配置数据结构应该是什么样的?到目前为止,解决方案只记录最后一行,因为它们每次添加记录时都会踩到相同的哈希键。

这可能会让你更接近,但你仍然需要弄清楚数据结构应该是什么。

  • 我将文件句柄作为参数传入,因此我的子例程与获取数据的特定方式无关。在这种情况下,它可以来自文件、字符串、套接字,甚至是DATA下方的内容。

  • 我没有在解析字符串后进行修复,而是在解析字符串之前修复了字符串以具有“ip”元素。一旦我这样做了,“ip”元素就不是特例了,它只是一个双重分割的问题。这是一项非常重要的技术,可以节省大量工作和代码。

  • 我在子例程中创建了一个哈希引用,并在完成后返回该哈希引用。我不需要全局变量。:)

使用警告;
使用严格;

使用 Data::Dumper;

读取配置文件(\*DATA);

子读取配置文件
    {
    我的($fh)= 班次;

    我的 $hash = {};

    而( <$fh> )
        {
        咀嚼;

        s/\s+(\d*\.\d+)$/>ip=$1/;

        $哈希->{ $. } = { 地图 { 拆分 /=/ } 拆分 />/ };
        }

    返回$哈希;
    }

我的 $hash = readConfigFile(\*DATA);

打印自卸车($hash);

__数据__
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',
                   '酒吧' => '3',
                   '大小' => '3'
                 },
          '2' => {
                   'ip' => '.76',
                   'rec' => '1',
                   'adv' => '111 22 3456',
                   '酒吧' => '1',
                   '大小' => '2'
                 }
        };

如果这不是您想要的结构,请向我们展示您最终想要的结构,我们可以调整我们的答案。

于 2008-11-08T20:14:22.283 回答
2

我假设您要阅读和解析超过 1 行。因此,我选择将值存储在 AoH 中。

#!/usr/bin/perl
使用严格;
使用警告;

我的@config;

而(<数据>){
    咀嚼;
    推@config,{拆分/[=>]/};
}

对于我的 $href (@config) {
    而(我的($k,$v)=每个%$href){
        打印 "$k => $v\n";
    }
}

__数据__
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 中读取。)

记录 => 0
进阶 => 1234 123 4.5 6.00
酒吧 => 3
大小 => 3
记录 => 1
进阶 => 111 22 3456 .76
酒吧 => 1
大小 => 2

克里斯

于 2008-11-08T20:21:28.340 回答
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

冒号或分号作为分隔符是相当传统的;列表中以逗号分隔的项目周围的括号并不是一个离谱的约定。一致性是最重要的。爱默生说:“愚蠢的一致性是小头脑的妖精,受到小政治家、哲学家和神学家的崇拜”,但计算机科学中的一致性对每个人都有很大的好处。

于 2008-11-08T20:36:21.070 回答
1

下面假设分隔符保证为 >,并且不可能出现在数据中。

我只是根据'>'分割每一行。最后一个值将包含一个 key=value 对,然后是一个空格,然后是 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;
}
于 2008-11-10T12:03:34.037 回答
0

这是一种方法。

foreach (@lines) {
  咀嚼;
  我的%配置;
  # 先提取最后一个数字,并用结尾替换
  # 对分隔符。
  s/\s*([\d\.]+)\s*$/>/;
  $CONFIG{ip} = $1;
  而 ( /([^=]*)=([^>]*)>/g ) {
    $CONFIG{$1} = $2;
  }
  打印自卸车(\%CONFIG);
}
于 2008-11-08T19:23:11.840 回答