Как я могу преобразовать эти строки в хеш в Perl? - PullRequest
3 голосов
/ 08 ноября 2008

Я хочу преобразовать одну строку с несколькими разделителями в хэш-структуру 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

Последние цифры также должны быть удалены и храниться в отдельной паре ключ => значение, имя которой может быть 'ip'. (Я не смог сделать это, не сделав код слишком длинным и сложным).

Ответы [ 5 ]

4 голосов
/ 08 ноября 2008

Как должна выглядеть ваша структура данных конфигурации? Пока решения записывают только последнюю строку, потому что они нажимают на одни и те же хэш-ключи каждый раз, когда добавляют запись.

Вот кое-что, что может приблизить вас, но вам все еще нужно выяснить, какой должна быть структура данных.

  • Я передаю дескриптор файла в качестве аргумента, чтобы моя подпрограмма не была привязана к определенному способу получения данных. В данном случае это может быть файл, строка, сокет или даже что-то ниже 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'
                 }
        };

Если это не та структура, которую вы хотите, покажите нам, чем вы хотите закончить, и мы можем скорректировать наши ответы.

2 голосов
/ 08 ноября 2008

Я предполагаю, что вы хотите прочитать и проанализировать более 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

Chris

1 голос
/ 10 ноября 2008

Ниже предполагается, что разделитель гарантированно будет>, и нет никаких шансов, что он появится в данных.

Я просто разбил каждую строку на основе '>'. Последнее значение будет содержать пару ключ = значение, затем пробел, затем IP, поэтому разделите это на / / ровно один раз (предел 2), и вы получите k = v и IP. Сохраните IP-адрес в хэш и сохраните пару k = v в массиве, затем просмотрите массив и разделите k = v на '='.

Заполните 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;
}
1 голос
/ 08 ноября 2008

Формат файла конфигурации является неоптимальным, скажем так. То есть существуют более простые форматы для разбора и понимания. [ Добавлено : но формат уже определен другой программой. Perl достаточно гибок, чтобы справиться с этим.]

Ваш код забивает файл, когда в этом нет реальной необходимости.

Ваш код обращает внимание только на последнюю строку данных в файле (как заметил Крис Чарли, пока я это печатал).

Вы также не разрешили комментировать строки или пустые строки - и то, и другое - хорошая идея в любом конфигурационном файле, и их легко поддерживать. [ Добавлено : опять же, с предопределенным форматом, это едва уместно, но когда вы создаете свои собственные файлы, запомните это.]

Вот адаптация вашей функции в несколько более идиоматический 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

Теперь вам нужно более четко объяснить, какова структура последнего поля и почему у вас есть поле 'ip' без обозначения ключ = значение. Последовательность делает жизнь проще для всех. Вам также нужно подумать о том, как несколько строк должны обрабатываться. И я хотел бы изучить использование более ортодоксальной нотации, такой как:

pub=3;rec=0;size=3;adv=(1234,123,4.5);ip=6.00

Двоеточие или точка с запятой в качестве разделителей довольно условны; Скобки вокруг элементов в списке, разделенных запятыми, не являются возмутительным соглашением. Последовательность имеет первостепенное значение. Эмерсон сказал: «Глупая последовательность - это хобгоблин маленьких умов, обожаемый маленькими государственными деятелями, философами и богословами», но последовательность в компьютерных науках приносит большую пользу каждому.

0 голосов
/ 08 ноября 2008

Вот один из способов.


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 );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...