LINUX.ORG.RU

Добавление элементов в hash reference в рекурсивной функции

 ,


0

2

Есть такая задача - парсить текстовые файлы и сохранять значения ключей и значений. Текстовые файлы могут включать ссылки на другие текстовые файлы, которые тоже надо парсить. Делается это так:

#!/usr/bin/perl -w

use strict;
use warnings;

my $file = "file.txt";

sub read_recur($$);
sub read_recur($$)
{
   my ($filename, $hash_ref) = @_;
   open (my $fh, '<', $filename) or die "Could not open file $filename\n";

   while (my $row = <$fh>) {
      if ($row !~ /^\s*#/)
      {
         chomp($row);
         if ($row =~ m/(\w+)\s+"(.*)"/)
         {
            $hash_ref->{$1} = $2;
         }
	     if ($row =~ m/%LOAD%\s+"(.*)"/)
	     {
		    my $new_filename = $1;
			$new_filename =~ s/\%(\w+)\%/$ENV{$1}/g;
	        read_recur($new_filename, $hash_ref);
	     }
      }
   }
   close ($fh) or die "Could not close $filename\n";
}

sub read($)
{
   my $filename = shift;
   my $hash_ref = {};
   read_recur($filename, $hash_ref);
   return $hash_ref;
}


my $hash_ref = read($file);
while( my ($k, $v) = each %$hash_ref ) {
        print "key: $k, value: $v.\n";
    }


Все было бы хорошо, но значения считываются только с первого файла, значения из рекурсивного вызова не добавляются в хеш, Подскажите, ЧЯДНТ?

Ответ на: комментарий от anonymous

Исправлено, вот она сама себя вызывает

read_recur($new_filename, $hash_ref);

by_zero ()
Ответ на: комментарий от anonymous

Функция считывает все вложенные файлы, но данные в референс точно не добавляются

by_zero ()

Добавьте use Data::Dumper; print Dumper $hash_ref перед окончанием sub read_recur.

Невооружённым глазом не видно, где %$hash_ref копируется. На всякий случай попробуйте ещё убрать прототипы функций, они необязательны.

AITap ★★★★★ ()

Еще можно попробовать, что бы read_recur() принимала имя файла, возвращала ссылку на хэш, а когда будет найдена ссылка на файл, то мержить новый хэш к результату.

joy4eg ★★★★★ ()
Последнее исправление: joy4eg (всего исправлений: 1)
Ответ на: комментарий от by_zero

Попробуй еще при вызове печатать адрес $hash_ref, и адрес из $_[1]

joy4eg ★★★★★ ()
Ответ на: комментарий от by_zero

Я бы всё таки разобрался почему не добавляется. Вангую, что просто не доходит до добавления. В качестве ультра теста, можешь связать (tie) hash_ref с каким-нибудь hash-логгером.

anonymous ()

А рекурсивные вызовы точно выполняются? С первого взгляда всё ок

  sub read_recur($$);
  sub read_recur($$){
    read_recur(...)

Много раз повторяется, можно просто:

  sub read_recur{
    __SUB__->(...)

disarmer ★★★ ()

Текстовые файлы могут включать ссылки на другие текстовые файлы, которые тоже надо парсить

Ну и зачем тут рекурсия? Складывай пути к файлам которые пропарсил в hash, а те что в очереди на парсинг в другой hash. В цикле while (hash) вызывай парсер, но никак не рекурсивно.

outtaspace ★★★ ()
Ответ на: комментарий от Deleted

там такое дело, что лоады файлов могут быть в середине, и их надо считывать именно в таком порядке. К примеру:

file1.txt

val1 "1"
val2 "2" 
%LOAD% "file2.txt"
val3 "3"

file2.txt

val1 "lol"
val3 "ololo"

В итоге в хеше должны быть такие значения:

val1 "lol"
val2 "2"
val3 "3"
by_zero ()

Расходимся, нам нерабочий код подсунули

$ perl t.pl 
Ambiguous call resolved as CORE::read(), qualify as such or use & at t.pl line 42.
Not enough arguments for read at t.pl line 42, near "$file)"
Execution of t.pl aborted due to compilation errors.
Olegymous ★★ ()
Ответ на: комментарий от Deleted

защита от рекурсии уже добавлена.

sub read_recur($$$)
{
   my ($filename, $hash_ref, $files_timestamps) = @_;
   my @stat = stat($filename);
   $files_timestamps->{$filename} = $stat[9];
   open (my $fh, '<', $filename) or die "Could not open file $filename\n";

   while (my $row = <$fh>) {
      if ($row !~ /^\s*#/)
      {
         chomp($row);
         if ($row =~ m/(\w+)\s+"(.*?)"\s*#*/)
         {
            $hash_ref->{$1} = $2;
         }
	     if ($row =~ m/%LOAD%\s+"(.*)"/)
	     {
		    my $new_filename = $1;
			$new_filename =~ s/\%(\w+)\%/$ENV{$1}/g;
			die "Recursive %LOAD% \"$1\" in file $filename\n" if exists  $files_timestamps->{$new_filename};
	        read_recur($new_filename, $hash_ref, $files_timestamps);
	     }
      }
   }
   close ($fh) or die "Could not close $filename\n";
}

sub read_hash($)
{
   my ($filename) = shift;
   my $files_timestamps = {};
   my $hash_ref = {};
   read_recur($filename, $hash_ref, $files_timestamps);
   return ($hash_ref, $files_timestamps);
}

by_zero ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.