2013-03-18 2 views
2

Я хочу проанализировать текст файла, а затем помещать его в хэш. Мой файл выглядит по адресу:Perl - проанализировать текст файла в hash

key1 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val 
key2 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val 
key3 val 
key4 val,val 
key5 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val 

Мои ключи до того места, и мои ценности список элементов после пространства и перед каждой запятой. У меня есть несколько строк, где нет ключа, потому что значения продолжаются на нескольких строках.

Так что я хочу хэш подобное (я наиболее знаком в Python):

hash={'key1':[val,val,...],'key2':[val,val,...]} 

Мой код: `

my %hashNames; 
open INFILE, "./file.txt" or die $!; 
my @temp =(); 

while (my $line = <INFILE>) 
{ 

    my @names = split /[\t,]/, $line; 
    my $ID = $names[0]; 
    if ($line =~ /\t/) 
    { 

     my @temp=(); 
     for (my $i = 1; $i < @names; $i +=1) 
     { 
      push (@temp, $names[$i]); 
     } 

    } 
    else 
    { 

     for (my $i = 0; $i < @names; $i +=1) 
     { 
      push (@temp, $names[$i]); 
     }  
    } 
}` 
+1

показать нам, что вы пытались –

+0

Если вы знаете, как сделать это в Python, покажите нам это пожалуйста. – simbabque

+0

Я думаю, что я должен читать каждую строку, и если есть пробел, я должен создать новый ключ и нажать значения differents (в списке), а если нет места, я должен добавить значения в предыдущий ключ. – Tof

ответ

1

Трудность здесь в том, что ваши записи завершаются «новой линией, которой не предшествует запятая». К сожалению, разделитель входных записей $/ не может быть установлен в регулярное выражение. Это оставляет три удобных решения:

  1. Загрузить весь файл в память. Это не так плохо, как кажется, потому что в этом случае у нас есть тот же объем информации в хэше. Мы можем затем split /(?<!,)\n/ получить фактические записи.

    my %hash = do { 
        local $/; # set to undef, for slurp 
        map { 
        my ($key, $vals) = split /\s+/, $_, 2; # split on first whitespace, into two strings 
        $key => [ split /\s*,\s*/, $vals ]; # return a list of a key and a value array 
        } split /(?<!,)\n/, <FILE>;    # split the file into records 
    }; 
    
  2. Мы можем написать readline заменитель, который буферизует входной сигнал и может прекратить линии с регулярными выражениями.

  3. Мы можем рассматривать конечную запятую как символ продолжения линии.

    my %hash; 
    while(<FILE>) { 
        $_ .= <FILE> while /,\n\z/; 
        my ($key, $value) = split /\s+/, $_, 2; 
        push @{ $hash{$key} }, split /\s*,\s*/, $value; # allow multiple occurrences of one key, simply append values to list. 
    } 
    
+0

Большое спасибо за ответ yout. как я могу получить ключи и значения списка? – Tof

+0

@Tof Я не совсем понял этот вопрос. Для данного '$ key' вы можете получить' @ vals' как '@vals = @ {$ hash {$ key}}'. Вам нужно '@ {...}', потому что этот хеш может хранить только * ссылку * в массиве, а не сам массив *, поэтому мы должны разыменовать его обратно в массив. – amon

+0

Например, я бы получил массив для определенного ключа для его сканирования. – Tof

0

Здесь я пойти:

my %results; 
my $key; 
while(my $line = <INFILE>) { 
    chomp($line); 
    my @items = split(/, */, $line); 
    $key = shift @items; 
    $results{$key} = \@items; 
} 

Что работает для простого случая, за исключением вашего заявления:

У меня есть несколько строк, где нет ключа, потому что значения продолжаются на нескольких строках.

Чтобы справиться с этим, вы должны будете объяснить, как определить, является ли следующая строка ключом или значением. Если вы знаете, то вы можете поместить его в заявлении, если и использовать предыдущий ключ, чтобы добавить новые значения хэш:

my %results; 
my $key; 
while(my $line = <INFILE>) { 
    chomp($line); 
    my @items = split(/, */, $line); 
    my $tmpkey = shift @items; 
    if (is_real_key($tmpkey)) { 
     $key = shift @items; 
     $results{$key} = \@items; 
    } else { 
     push (@{$results{$key}}, $tmpkey, @items); 
    } 
} 
+0

Фактически у меня есть значения, которые продолжаются на нескольких строках, и поэтому нет ключа. – Tof

+0

Да, я справился с этим. Но вы не сказали *, как вы знаете, когда значения останавливаются и начинается новый ключ. –

3

Ваша проблема заключается в том, что новые строки больше не разделять ваши записи. Таким образом, способ справиться с этим, чтобы отключить недействителен ввода по умолчанию разделитель $/ и эмулировать действует один:

use strict; 
use warnings; 
use Data::Dumper; 

my %hash; 
my $file; 
{ 
    local $/;   # disable input record separator 
    $file = <DATA>; # entire file here now! 
} 

for my $line (split /^(?=\S+)/m, $file) { # records begin this way now 
    $line =~ s/\n//g;      # remove newlines 
    my ($key, $val) = split ' ', $line, 2; # divide into two fields 
    $hash{$key} = [ split /,/, $val ];  # store the data 
} 

print Dumper \%hash; 

__DATA__ 
key1 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val 
key2 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val 
key3 val 
key4 val,val 
key5 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val 

Пояснения:

  • Расщепление на /^(?=\S+)/m с помощью модификатора /m означает ^ теперь совпадение строк новой строки внутри строки, которое будет эмулировать разделитель входных данных.
  • Разбиение строки в двух областях осуществляется путем добавления LIMIT 2 к split
  • Мы расщепляется непосредственно в хэш, используя анонимный массив [ ... ] с раздельным утверждением внутри него.
0
#!/usr/bin/perl 

use strict; 
use warnings; 
use feature 'say'; 

use Data::Dumper; 

my $res_hash = {}; 
my ($current_key, $values); 
my $push_again; 
while (my $line = <DATA>) { 
    chomp $line; 
    push (@{ $res_hash->{$current_key} }, split(/,/, $values)) if ($current_key and $values and (index($line, ' ') > 0)); 
    if (index($line, ' ') > 0){ 
    $push_again = 0; 
    ($current_key, $values) = split(/\s/, $line);  
    } else { 
    $values .= $line; 
    $push_again = 1; 
    } 

}; 
push (@{ $res_hash->{$current_key} }, split(/,/, $values)) if $push_again; 

say "result:".Dumper($res_hash); 



__DATA__ 
key1 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val 
key2 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val 
key3 val 
key4 val,val 
key5 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val 
+1

У вас отсутствует последняя запись. Вы также должны нажать буфер после последней итерации цикла. – TLP

+0

вы правы, thx - однако, я предпочитаю ваше решение больше –

+0

Спасибо, Тюдор. – TLP

2

Parse::RecDescent Использование модуля

#! /usr/bin/env perl 

use strict; 
use warnings; 

use Parse::RecDescent; 

our %hash; 
my $p = Parse::RecDescent->new(q! 
    hash: entry(s?) 
    entry: key value(s /,/) { $::hash{$item[1]} = [ @{ $item[2] } ] } 
    key: /\S+/ 
    value: /([^,\n]|\\,])+/ 
!); 
die "$0: failed to create parser" unless defined $p; 

my $text = do {{ local $/; <DATA> }}; 
$p->hash($text) or die "$0: parse failed"; 

for (sort keys %hash) { 
    print "$_ => val x ", scalar @{ $hash{$_} }, "\n"; 
} 

__DATA__ 
key1 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val 
key2 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val 
key3 val 
key4 val,val 
key5 val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val,val, 
val,val,val,val,val,val,val,val,val,val,val,val,val,val,val 

Выход:

key1 => val x 22 
key2 => val x 22 
key3 => val x 1 
key4 => val x 2 
key5 => val x 52