如何在Perl中将这些字符串转换为散列?

rta7y2nd  于 2023-03-03  发布在  Perl
关注(0)|答案(5)|浏览(212)

我想把一个包含多个分隔符的字符串转换成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;
}

其中“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=〉value对中,其名称可以是'ip'。(我无法在不使代码变得冗长和复杂的情况下完成此操作)。

6jygbczu

6jygbczu1#

你的配置数据结构应该是什么样子的?到目前为止,解决方案只记录最后一行,因为他们每次添加记录都会踩在相同的哈希键上。
下面的内容可能会让您更接近,但您仍然需要弄清楚数据结构应该是什么。

  • 我把文件句柄作为参数传入,这样子例程就不会被绑定到获取数据的特定方式上,它可以来自文件、字符串、套接字,甚至是__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'
                 }
        };

如果这不是你想要的结构,告诉我们你想得到什么,我们可以调整我们的答案。

bsxbgnwa

bsxbgnwa2#

我假设你想要读取和解析不止一行,所以我选择将值存储在一个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

克里斯

gcuhipw9

gcuhipw93#

下面假设定界符一定是〉,并且不可能出现在数据中。
我只是简单地基于'〉'拆分每一行,最后一个值将包含一个key=value对,然后是一个空格,然后是IP,所以在/ /上拆分一次(限制为2),你就得到了k=v和IP,将IP保存到哈希中,并将k=v对保存在数组中,然后遍历数组,在'='上拆分k=v。
填充hashref并将其推送到更高作用域的数组中,完成后,该数组将包含hashref。
(已将配置加载到阵列中)

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

jq6vz3qz4#

配置文件格式是次优的,我们可以这么说。也就是说,有更容易解析和理解的格式。[* 添加 *:但是格式已经被另一个程序定义了。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

冒号或分号作为分隔符是相当常规的;在列表中用括号括住逗号分隔的项目并不是一个令人愤慨的惯例。一致性是最重要的。爱默生说过“愚蠢的一致性是小头脑的妖怪,受到小政治家、哲学家和神学家的崇拜”,但计算机科学中的一致性对每个人都有很大的好处。

1dkrff03

1dkrff035#

有一个办法。

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

相关问题