correction of a faulty hash

64 Views Asked by At

Hello my fellow keyboard artists,

I wrote a code that reads a my_hash_data.txt file and looks a specific value (i-signal) and saves its parents key in i-signal-output.txt, which works good so far with the hash testfile i've created. now i should use it for the big file to look for the right values to get the parent keys but it has entries in it which my programm cant work with and fails.

my test that works hash looks like this:

$mein_hash = {
               'CORRECT_1' => {
                                'Signalwert' => 'i-signal',
                                'Zeitstempel' => {
                                                   'stempel' => {
                                                                    'Signalwert' => 'new-sig',
                                                                  }
                                                 }
                              },
               'Data' => {
                               'Email' => '[email protected]',
                               'CORRECT_2' => {
                                                'Signalwert' => 'i-signal',
                                                'SignalTextTable' => 'asdf',
                        
                                              },
                               'Telefon' => '0123456789'
                             },
               'Alter' => 30,
               'Name' => 'Max Mustermann'
             };

implementing the faulty entry it looks like this:

$mein_hash = {
               'CORRECT_1' => {
                                'Signalwert' => 'i-signal',
                                'Zeitstempel' => {
                                                   'stempel' => {
                                                                    'Signalwert' => 'new-sig',
                                    'SignalTextTable' => $VAR1->{'VLAN_FAS'}{'NetworkPdus'},
                                                                  }
                                                 }
                              },
               'Data' => {
                               'Email' => '[email protected]',
                               'CORRECT_2' => {
                                                'Signalwert' => 'i-signal',
                                                'SignalTextTable' => 'asdf',
                        
                                              },
                               'Telefon' => '0123456789'
                             },
               'Alter' => 30,
               'Name' => 'Max Mustermann'
             };

note that 'SignalTextTable' => $VAR1->{'VLAN_FAS'}{'NetworkPdus'}, is faulty and should be removed to make it look like the hash above

this is my previous code that works to find the parent key in a from a not faulty data file:

use strict;
use warnings;
use Data::Dumper;
use File::Spec;

# standard library
chdir("U:\\tests");

# Searches a hash for a special keyword "i-signal".
my %gefunden;
sub finde_i_signal {
    my ($hash_ref, $parent_key) = @_;

    foreach my $key (keys %$hash_ref) {
        if (ref($hash_ref->{$key}) eq 'HASH') {
            finde_i_signal($hash_ref->{$key}, $key);
        } elsif ($hash_ref->{$key} eq 'i-signal') {
            $gefunden{$parent_key} = 1;
        }
    }
}

# Filename of the .txt file in the same folder
my $datei_name = "mein_hash_data.txt";

# Open and read the .txt file
if (open(my $datei_handle, '<', $datei_name)) {
    local $/;
    my $datei_inhalt = <$datei_handle>;
    close($datei_handle);

    # Remove the surrounding dollar signs and the variable name.
    $datei_inhalt =~ s/^\$mein_hash\s*=\s*//;

    # Convert the content into a hash reference
    my $datei_hash_ref = eval $datei_inhalt;
    die "Invalid format in the file $datei_name" if !$datei_hash_ref || ref($datei_hash_ref) ne 'HASH';

    # Now call the function and pass the hash reference
    finde_i_signal($datei_hash_ref, undef);

    # Save found i-signals in "HASH_i-signal-output.txt
    open(my $file_handle, '>', 'HASH_i-signal-output.txt') or die "Kann die Datei nicht öffnen: $!";
    foreach my $key (keys %gefunden) {
        print $file_handle "$key\n";
    }
    close($file_handle) or die "Error closing the file: $!";
} else {
    die "The file $datei_name could not be opened: $!";
}
1

There are 1 best solutions below

2
brian d foy On

I'm replacing my non-specific first answer with something meaty.

From the Data::Dumper docs:

The default output of self-referential structures can be evaled, but the nested references to $VARn will be undefined, since a recursive structure cannot be constructed using one Perl statement. You should set the Purity flag to 1 to get additional statements that will correctly fill in these references.

First, let's start by dumping a data structure where two keys use the same reference as their values:

#!/usr/bin/perl
use v5.10;
use Data::Dumper;

my $ref = [ qw(1 3 7) ];

my %hash = (
    foo => $ref,
    bar => $ref,
    baz => [ qw(a b d) ]
    );

my $dumped = Dumper( \%hash );

say "Dumped-----\n$dumped----";

The output is a string where Data::Dumper assigns your data structure to $VAR1. That's the default, but there are various ways to change that:

Dumped-----
$VAR1 = {
          'foo' => [
                     '1',
                     '3',
                     '7'
                   ],
          'baz' => [
                     'a',
                     'b',
                     'd'
                   ],
          'bar' => $VAR1->{'foo'}
        };
----

The problem is that inside the definition of $VAR1 is a reference to $VAR1. That variable doesn't have a value yet because Perl is still computing the right hand side to assign to $VAR1. If strict is not in place, that $VAR1->{'foo'} will be undef of you eval this.

@ikegami point to Purity in the comments:

local $Data::Dumper::Purity = 1;
my $dumped = Dumper( \%hash );

Now the output isn't a single assignment. It gets the main structure, then assigns to the foo key later:

Dumped-----
$VAR1 = {
          'bar' => [
                     '1',
                     '3',
                     '7'
                   ],
          'baz' => [
                     'a',
                     'b',
                     'd'
                   ],
          'foo' => []
        };
$VAR1->{'foo'} = $VAR1->{'bar'};
----

However, you shouldn't use Data::Dumper for this sort of thing. Since the thing you want to eval is Perl code (well, it has to be), there's a chance that you could run code that you don't intend. I go through some of the scenarios in Mastering Perl.