Definiertheit einer Variable überprüfen,oder wie?

Definiertheit einer Variable überprüfen,oder wie?

am 14.03.2006 21:14:12 von frank

Hallo,

bei folgendem Codebruchstück habe ich ein Verständnisproblem:

my $dom_parser = new XML::DOM::Parser;
my $doc = $dom_parser->parsefile($LocalTPM);
my %RunFiles = Tpm::getListField($doc, "RunFiles");
my %DocFiles = Tpm::getListField($doc, "DocFiles");
[...]
foreach (\$RunFiles{"text"}, \$DocFiles{"text"}) {
next if (! ${$_});
print STDERR Dumper(${$_});
my @filelist = split(/\n/m,${$_});
next if (! @filelist);
print STDERR "not skipping\n";
foreach (@filelist) {CheckFileExistence($_)};
}

Es kann vorkommen, dass in der tpm(xml)-Datei DocFiles ein leeres
Element ist:


Damit wird $DocFiles{"text"} aber seltsamerweise kein leerer String,
sondern ein Array aus einem anderen Namensraum (?) und hintendran noch
ein Zeilenvorschub:

$VAR1 = 'ARRAY(0x872df7c)
';
not skipping
ARRAY(0x872df7c): Does not exist!

Wie man sieht, schlagen meine Versuche fehl, darauf zu prüfen (next
if...), weil ja irgendwas drinsteht. Wie mache ich es richtig?

TIA, Frank
--
> ich wusste mal einen Befehl, der die Ausgabe auf stdout noch zusätzlich
> in eine Datei umlenken konnte... Bitte helft mir auf die Sprünge.
"kaffee" war es nicht.

Re: Definiertheit einer Variable überprüfen, oder wie?

am 14.03.2006 21:35:36 von timwi

> bei folgendem Codebruchstück habe ich ein Verständnisproblem:
>
> my $dom_parser = new XML::DOM::Parser;
> my $doc = $dom_parser->parsefile($LocalTPM);
> my %RunFiles = Tpm::getListField($doc, "RunFiles");
> my %DocFiles = Tpm::getListField($doc, "DocFiles");
> [...]
> foreach (\$RunFiles{"text"}, \$DocFiles{"text"}) {
> next if (! ${$_});
> print STDERR Dumper(${$_});
> my @filelist = split(/\n/m,${$_});
> next if (! @filelist);
> print STDERR "not skipping\n";
> foreach (@filelist) {CheckFileExistence($_)};
> }

Bist du dir sicher, dass Dumper den Wert von $_ nicht verändert?

Warum überhaupt \$RunFiles{"text"} und dann ${$_} statt einfach
$RunFiles{"text"} und dann $_?

Timwi

Re: Definiertheit einer Variable überprüfen, oder wie?

am 14.03.2006 21:47:30 von timwi

Frank Küster wrote:
> Hallo,
>
> bei folgendem Codebruchstück habe ich ein Verständnisproblem:
>
> my $dom_parser = new XML::DOM::Parser;
> my $doc = $dom_parser->parsefile($LocalTPM);
> my %RunFiles = Tpm::getListField($doc, "RunFiles");
> my %DocFiles = Tpm::getListField($doc, "DocFiles");
> [...]
> foreach (\$RunFiles{"text"}, \$DocFiles{"text"}) {
> next if (! ${$_});
> print STDERR Dumper(${$_});
> my @filelist = split(/\n/m,${$_});
> next if (! @filelist);
> print STDERR "not skipping\n";
> foreach (@filelist) {CheckFileExistence($_)};
> }
>
> Es kann vorkommen, dass in der tpm(xml)-Datei DocFiles ein leeres
> Element ist:
>
>
> Damit wird $DocFiles{"text"} aber seltsamerweise kein leerer String,
> sondern ein Array aus einem anderen Namensraum (?) und hintendran noch
> ein Zeilenvorschub:
>
> $VAR1 = 'ARRAY(0x872df7c)
> ';
> not skipping
> ARRAY(0x872df7c): Does not exist!

Google gibt den folgenden Quelltext für die Funktion getListField:

sub getListField {
my ($doc, $f) = @_;

my %s = getTextField($doc, $f);
my $str = $s{"text"};
$str =~ s/^\n*//;
$str =~ s/\n*$//;
$str =~ s/\n/ /gomsx;
@{$s{"text"}} = split(" ", $str);
return %s;
}

Demzufolge sind $RunFiles{"text"} und $DocFiles{"text"} also arrayrefs,
und ${$_} also auch. Es sollte also immer ein Array zurückliefern, nicht
nur bei leeren Tags in deiner Eingabe-XML-Datei.

Timwi

Re: Definiertheit einer Variable überprüfen,oder wie?

am 14.03.2006 22:51:36 von frank

Arne 'Timwi' Heizmann wrote:

>> bei folgendem Codebruchstück habe ich ein Verständnisproblem:
>> my $dom_parser = new XML::DOM::Parser;
>> my $doc = $dom_parser->parsefile($LocalTPM);
>> my %RunFiles = Tpm::getListField($doc, "RunFiles");
>> my %DocFiles = Tpm::getListField($doc, "DocFiles");
>> [...]
>> foreach (\$RunFiles{"text"}, \$DocFiles{"text"}) {
>> next if (! ${$_});
>> print STDERR Dumper(${$_});
>> my @filelist = split(/\n/m,${$_});
>> next if (! @filelist);
>> print STDERR "not skipping\n";
>> foreach (@filelist) {CheckFileExistence($_)};
>> }
>
> Bist du dir sicher, dass Dumper den Wert von $_ nicht verändert?

Nein, aber ich dachte dass er außer "stringify" nichts machen sollte?
Wo sollte ich nachlesen?

> Warum überhaupt \$RunFiles{"text"} und dann ${$_} statt einfach
> $RunFiles{"text"} und dann $_?

Das war ein Überbleibsel, als ich nach CheckFileExistence die
nicht-existierenden Dateien löschen wollte. Ich habe mich aber
stattdessen entschieden, lieber eine Fehlermeldung auszugeben.

Gruß, Frank
--
> ich wusste mal einen Befehl, der die Ausgabe auf stdout noch zusätzlich
> in eine Datei umlenken konnte... Bitte helft mir auf die Sprünge.
"kaffee" war es nicht.

Re: Definiertheit einer Variable überprüfen,oder wie?

am 14.03.2006 22:55:09 von frank

Arne 'Timwi' Heizmann wrote:

> Demzufolge sind $RunFiles{"text"} und $DocFiles{"text"} also
> arrayrefs, und ${$_} also auch. Es sollte also immer ein Array
> zurückliefern, nicht nur bei leeren Tags in deiner Eingabe-XML-Datei.

Hm, ja. Das dachte ich zuerst auch, frage mich allerdings, warum es
dann an anderer Stelle nicht funktioniert hat. Ich werde das nochmal in
Ruhe ansehen müssen...

Danke, Frank
--
> ich wusste mal einen Befehl, der die Ausgabe auf stdout noch zusätzlich
> in eine Datei umlenken konnte... Bitte helft mir auf die Sprünge.
"kaffee" war es nicht.

Re: Definiertheit einer Variable überprüfen,oder wie?

am 15.03.2006 00:44:12 von frank

Arne 'Timwi' Heizmann wrote:

> Frank Küster wrote:
>> Hallo,
>> bei folgendem Codebruchstück habe ich ein Verständnisproblem:
>> my $dom_parser = new XML::DOM::Parser;
>> my $doc = $dom_parser->parsefile($LocalTPM);
>> my %RunFiles = Tpm::getListField($doc, "RunFiles");
>> my %DocFiles = Tpm::getListField($doc, "DocFiles");
>> [...]
>> foreach (\$RunFiles{"text"}, \$DocFiles{"text"}) {
>> next if (! ${$_});
>> print STDERR Dumper(${$_});
>> my @filelist = split(/\n/m,${$_});
>> next if (! @filelist);
>> print STDERR "not skipping\n";
>> foreach (@filelist) {CheckFileExistence($_)};
>> }
>> Es kann vorkommen, dass in der tpm(xml)-Datei DocFiles ein leeres
>> Element ist:
>>
>> Damit wird $DocFiles{"text"} aber seltsamerweise kein leerer String,
>> sondern ein Array aus einem anderen Namensraum (?) und hintendran noch
>> ein Zeilenvorschub:
>> $VAR1 = 'ARRAY(0x872df7c)
>> ';
>> not skipping
>> ARRAY(0x872df7c): Does not exist!
>
> Google gibt den folgenden Quelltext für die Funktion getListField:

Da irrt sich Google. Die aktuelle Datei ist unter
http://tug.org/svn/texlive/trunk/Build/tools/Tpm.pm?view=mar kup&rev=1535
zu finden, und die Definition ist:

sub getTextField {
my ($doc, $f) = @_;
my $nodelist = $doc->getElementsByTagName("TPM:$f");

my %s = ( );
return %s if ($nodelist->getLength <= 0);
my $node = $nodelist->item(0);
return %s if (! $node);
foreach my $k (@{$node->getAttributes->getValues}) {
$k = $k->getName;
$s{$k} = $node->getAttribute($k);
}
$node = $node->getFirstChild();
return %s if (! $node);
my $str = $node->toString;
$str = $node->expandEntityRefs($str);
$s{"text"} = $str;
return %s;
}


> Demzufolge sind $RunFiles{"text"} und $DocFiles{"text"} also
> arrayrefs, und ${$_} also auch. Es sollte also immer ein Array
> zurückliefern, nicht nur bei leeren Tags in deiner Eingabe-XML-Datei.

Also ist es schon normal, dass ich einen String kriege. Wie kann es
sein, dass da mit einem Mal ein Array drinsteht, wenn das Element leer
ist?

Ich habe die Funktion mal etwas "verschönert":

sub getTextField {
my ($doc, $f) = @_;
my $nodelist = $doc->getElementsByTagName("TPM:$f");

my %s = ( );
print STDERR "\n I'm here\n";
return %s if ($nodelist->getLength <= 0);
print STDERR "I'm still here\n";
my $node = $nodelist->item(0);
return %s if (! $node);
print STDERR "I'm still here 2\n";
foreach my $k (@{$node->getAttributes->getValues}) {
$k = $k->getName;
print STDERR "$k ";
$s{$k} = $node->getAttribute($k);
}
$node = $node->getFirstChild();
return %s if (! $node);
print STDERR "\n I'm still here 3\n";
my $str = $node->toString;
$str = $node->expandEntityRefs($str);
$s{"text"} = $str;
return %s;
}

Jetzt erhalte ich:

I'm here
I'm still here
I'm still here 2
size
I'm here
I'm still here
I'm still here 2
size
I'm still here 3

I'm here
I'm still here
I'm still here 2
size ARRAY(0x86f90a4): Does not exist!

I'm here

Im Fehlerfall ist also der Rückgabewert von $node->getFirstChild()
gleich Null. Die Methode stammt aus XML::DOM:

sub getFirstChild
{
my $kids = $_[0]->[_C];
defined $kids ? $kids->[0] : undef;
}

Das ist mir zu hoch.

Allerdings gibt es einen Workaround. Wenn ich in der geparsten Datei
ein Linebreak einfüge:

-
+
+


Dann geht es plötzlich (Leerzeilen werden anderswo im Skript
eliminiert). Nicht dass ich mich richtig gut fühlen würde, aber
zumindest ist es reproduzierbar.

Gruß, Frank
--
> ich wusste mal einen Befehl, der die Ausgabe auf stdout noch zusätzlich
> in eine Datei umlenken konnte... Bitte helft mir auf die Sprünge.
"kaffee" war es nicht.