Perl: Element aus array löschen und in Datei zurückschreiben

Sadik

Mitglied
Moin allemiteinander!

Also ich schreib grad ein kleines Perlscript, das Formulardaten speichern und löschen soll.

Die Einträge speicher ich mit einer eindeutigen ID (Unix-Timestamp (denk ich, dieses Sekunden-seit-1970-Ding), da es unwahrscheinlich ist, das 2 Einträge gleichzeitig entstehen.

In die Textdatei schreib ich das nach dem Schema

ID|Formulardaten

Das schreiben geht wunderbar.

Nun zu meiner Lösch-Idee: Ich übergebe den Timestamp und den Array mit den Daten aus der Datei (gespeichert in einem Array) an die Prozedur, suche den Timestamp raus, lösche den Inhalt der Zeile und schreibe den Array dann praktisch mit einer Leerzeile zurück in die Datei. Mein Prob is jetzt nur das er alles löscht, also nach dem Vorgang in der Textdatei garnichts mehr drinsteht.

Unten mein Code, ich hoffe jemand kann mich auf meinen Denkfehler hinweisen, mir gehen nämlich die Ideen aus. Zur Info: Ich beschäftige mich erst eine Woche auf meiner Arbeit mit Perl, also verzeiht bitte Anfängerfehler :)



Code:
sub loeschevorgang {
# Argumente übernehmen, array und timestamp-id
my $tstamp = shift;
my @datei = shift;

#hilfsvariablen initialisieren
$r=0; 
$check=0;

#array durchlaufen, einzeleinträge in einen idteil und einen datenteil trennen, dabei hilfsvariable hochzählen, für die stelle des arrays an dem wir sind
foreach $eintraege (@datei) { 

	($id,$daten)=split(/\|/,$eintraege); 

#überprüfen ob idteil mit der übergebenen id übereinstimmt, wenn ja hilfsvariable setzen
	if ($id == $tstamp) {
		$check=$r;
	}
	$r++;	
}

#kleiner check ob die id überhaupt gefunden wurde (code fehlt, irrelevant)
#if ($check == 0) {
#}

#inhalt des arrays an der oben ausgewählten stelle löschen
$datei[$check] = '';

#datei zum schreiben öffnen
open (FILE, '>temp/formdaten');

#jeden arrayeintrag rückschreiben
foreach $eintrag (@datei) {
	print FILE "$eintrag";
}
#datei schließen
close FILE;

}
 
Hallo Sadik, ein paar Gedanken zu deiner Sub.

sub loeschevorgang($$) {

Heist: Zwei Übergabe-Parameter werden erwartet.
(Ist zwar nicht notwendig, über hilfreich um ggf. Probleme zu erkennen).

my $tstamp = shift;
my @datei = shift;

Ich vermute mal Du erwartest, dass alle Einträge deines Arrays in @data stehen.
Wenn Du aber zum Testen z.B. mal ausprobierst:

print $datei[0] . " (0)\n";
print $datei[1] . " (1)\n";
print $datei[2] . " (2)\n";

dann erkennst Du, dass nur der erste Eintrag angekommen ist :-(

Nun gibt's da mehrere Möglichkeiten, z.B.:

my $tstamp = shift;
my @datei = @_;

Aber so sollte man des besser nicht machen, da jeder Eintrag kopiert als Parameter übergeben werden muss.
(Stell dir vor was passiert, wenn in du einige tausend Einträge hast)

Besser ist es, wenn Du einfach eine Referenz auf den Array aus dem Hauptprogramm an loeschevorgang übergibst.
Könnte z.B. so aussehen:

Code:
#!/usr/bin/perl -w

use strict;
# -w und strict zeigen Probleme an ...

sub loeschevorgang($$) {
	my $tstamp = shift;
	my $refDatei = shift;
	
	my $check = 0;
	for (my $r = $#$refDatei; $r >= 0; $r--) { 
		my ($id) = split(/\|/,$$refDatei[$r]); 
		if ($id eq $tstamp) {
			# gefundenes Element löschen
			$$refDatei[$r] = '';             
			$check = 1;
		}
	}
	# 0:  Nein, # 1:  Ja
	return $check;
}

# Testdaten ...
my $aTimeStamp = 3;
my @TestData = (
	'1|Data1',
	'2|Data2',
	'3|Data3',
	'4|Data4',
	'5|Data5'
);

# Aufruf loeschevorgang, 
# wichtig: \ liefert eine Referenz auf @TestData
if (loeschevorgang($aTimeStamp, \@TestData)) {
	print $aTimeStamp . " wurde gelöscht\n";
} else {
	print $aTimeStamp . " wurde nicht gelöscht\n";
}

# zum Testen nur ausdrucken:
print map "$_\n", @TestData;

# oder in Datei schreiben:
open (FILE, '>temp/formdaten') 
|| die "Fehler beim Datei öffnen.\n";
print FILE map "$_\n", @TestData;
close FILE;

exit;
__END__
 
benutze einfach das Modul Tie::File

Code:
#! /usr/bin/perl

use strict;
use warnings; # besser als -w
use Tie::File;

my $datei = '/pfad/zu/text.datei';
my $searchID = <STDIN>; # gebe die zu löschende ID ein...
chomp $searchID;

tie my @array,'Tie::File', $datei or die $!;
@array = grep{$_ !~ /^$searchID\|/}@array;
untie @array;
 
Sehr elegante Lösung, gefällt mir :)

Was ich auch schon öfter benutzt habe ist das Module DB_File.

Code:
#!/usr/bin/perl

use strict;
use warnings;

use DB_File;

my %DataHash;
my $DataFile = "BerkleyHash.db";

# öffnen und am Ende automatisch schliessen
tie %DataHash, "DB_File", $DataFile, O_CREAT, 0666, $DB_HASH or die $!;
END { untie %DataHash; }

# speichern
$DataHash{'1'} = 'TestData1';
$DataHash{'2'} = 'TestData2';

# löschen
delete $DataHash{'1'};

# alles ausgeben (zum testen)
while (my ($aKey,$value) = each %DataHash) {
	print $aKey . '=' . $value . "\n";
}

exit;
__END__

Da hat man auch noch Optimierungs-Optionen (da Berkley DB basiert ...).

Gruss
Micha
 
Hehe danke für die Antworten :)

Ich hatte inzwischen eine andere Lösung genommen, als ich herumexperimentiert hab hab ich das Array einfach in der Sub neu einlesen lassen (also aus der Datei) und ab da an gings wunderbar.

Wußte einfach nicht das beim übergeben eines Arrays so nur der erste Eintrag übergeben wird.

Vielen dank dennoch Jungs :)
 

Neue Beiträge

Zurück