Schrift
[thread]6885[/thread]

Suchen und ersetzen - möglichst schnell u. korrekt



<< |< 1 2 >| >> 13 Einträge, 2 Seiten
stb2050
 2005-04-12 14:41
#53724 #53724
User since
2003-08-14
87 Artikel
BenutzerIn
[default_avatar]
Hi Ihr,

ich habe um die 130.000 Text-Dateien, in denen etwas ersetzt werden soll.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
opendir(DIR, "../userdaten/main");
foreach my $a (sort(readdir DIR))
{
next if $a eq '.' || $a eq '..' || $a eq 'todel';
push @members,$a;
}
closedir(DIR);

#Verzeichnis durchlaufen

foreach(@members)
{
#Daten-Datei öffnen

open(FILE, "../userdaten/main/$_");
@inhalt = <FILE>;
chomp(@inhalt);
close(FILE);

#UserID und Username auslesen

$userid = $inhalt[0];
$username = $inhalt[1];

#Textseiten einlesen

opendir(DIR, "../userdaten/main");
foreach my $a (sort(readdir DIR))
{
next if $a eq '.' || $a eq '..';
push @seiten,$a;
}
closedir(DIR);

foreach(@seiten)
{
open(FILE, "../userdaten/$userid/seiten/$_");
@content = <FILE>;
chomp(@content);
close(FILE);

@content =~ s/\userdaten\/$username\/bilder/userdaten\/$userid\/bilder/g;

open(FILE, ">../userdaten/$userid/seiten/$_");
foreach(@content) { print FILE $_ . "\n"; }
close(FILE);
}

@seiten = ();
}


Jetzt meine Fragen zum Code:

- Klappt das Suchen/Ersetzen (@content =~ s....) beim Ganzen Array, oder muss ich es doch Zeile für Zeile durchlaufen?
- Ist der Suchen/Ersetzen-Code so richtig? "userdaten/$username/bilder" soll durch "userdaten/$userid/bilder" ersetzt werden.
- Kann ich den Code noch optimieren, so dass er schneller durchläuft? Im Verzeichnis "../userdaten/main" sind ca. 13.000 Einträge, in den Verzeichnissen "../userdaten/$userid/seiten" auch ca. 10 Einträge. Es wären also 130.000 Dateien, in denen ersetzt werden muss. Und das soll ja keine 10 Stunden dauern.

Bin für jede Hilfe dankbar :-)

Liebe Grüße
Steffen
Crian
 2005-04-12 15:13
#53725 #53725
User since
2003-08-04
5866 Artikel
ModeratorIn
[Homepage]
user image
Statt

Code: (dl )
@content =~ s/\userdaten\/$username\/bilder/userdaten\/$userid\/bilder/g;


schreib besser

Code: (dl )
s/userdaten\/$username\/bilder/userdaten\/$userid\/bilder/g for @content;


(was soll das erste Backslashzeichen?) und am besten noch schöner (für die Augen):

Code: (dl )
s~userdaten/$username/bilder~userdaten/$userid/bilder~g for @content;


Brauchst Du /g hier wirklich? Ich vermute mal nein, dann lass es weg, das spart auch Zeit.

ansonsten: Wo ist use strict; / use warnings;? (-> http://wiki.perl-community.de/bin/view/Wissensbasis/UseStrict )


Edit: Generell würd ich sowas an einer kopierten kleineren (Teil-) Menge Deiner Daten probelaufen lassen. Dann weißt Du auch, ob es funktioniert und wie lange es braucht.\n\n

<!--EDIT|Crian|1113304885-->
s--Pevna-;s.([a-z]).chr((ord($1)-84)%26+97).gee; s^([A-Z])^chr((ord($1)-52)%26+65)^gee;print;

use strict; use warnings; Link zu meiner Perlseite
pq
 2005-04-12 15:23
#53726 #53726
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
oh gott...
[quote=stb2050,12.04.2005, 12:41][/quote]
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
use strict;
use warnings;
opendir(DIR, "../userdaten/main") or die $!;
my @members = sort grep { !m/^\.\.?$/ && $_ ne 'todel' } readdir DIR;
closedir(DIR);

#Verzeichnis durchlaufen
foreach my $member (@members) {
  #Daten-Datei öffnen
  open(FILE, "../userdaten/main/$member") or die $!;
  chomp(my @inhalt = <FILE>);
  close(FILE);

  #UserID und Username auslesen
  my ($userid, $username) = @inhalt;

  #Textseiten einlesen

  opendir(DIR, "../userdaten/main") or die $!;
   my @seiten = sort grep { !m/^\.\.?$/ } readdir DIR;
  closedir(DIR);

  foreach my $seite (@seiten) {
     open(FILE, "../userdaten/$userid/seiten/$seite") or die $!;
     chomp(my @content = <FILE>);
     close(FILE);

     s#\userdaten/$username/bilder#userdaten/$userid/bilder#g for @content;

     open(FILE, ">../userdaten/$userid/seiten/$seite") or die $!;
     print FILE $_, "\n" for @content;
     close(FILE);
  }

}


wieso willst du eigentlich den string 'Serdaten' ersetzen? (s#\userdaten...\n\n

<!--EDIT|pq|1113305225-->
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
Strat
 2005-04-12 16:05
#53727 #53727
User since
2003-08-04
5246 Artikel
ModeratorIn
[Homepage] [default_avatar]
Code: (dl )
1
2
use strict;
use warnings;

fehlt am anfang des scriptes... das solltest du dir angewoehnen, weil es dich 1. zum saubereren programmieren zwingt und 2. bei der fehlersuche extrem hilfreich ist

@content =~ s.... funktioniert nicht, weil =~ die linke seite in einen skalaren kontext
bringt und somit das pattern nur auf die anzahl der elemente der liste angewendet wird (z.B. 25 =~ s...)
Code: (dl )
s/.../.../ for @content;

wuerde aber funktionieren

der ersetzungscode klappt meistens, aber nicht immer:
x) \u ist ein Sonderzeichen... du meinst wahrscheinlich /u ?
x) wenn du anstelle von s/.../.../ s|...|...| verwendest, sparst du dir ein paar backslashes
x) wenn z.B. in $username ein sonderzeichen vorkommt, wird das verwandelt (z.B. der punkt steht fuer ein (fast) beliebiges zeichen, und eine oeffnende klammer kann sogar einen fehler verursachen... also besser die $username mit \Q$username\E quoten, z.B.
Code: (dl )
$content =~ s|/userdaten/\Q$username\E/bilder|/userdaten/$userid/bilder|g;


zur optimierung:
1. warum liest du die userdaten sortiert ein? brauchst du die sortierung, oder kannst du darauf verzichten?

2. du list alle dateinamen ein, scheinst aber immer nur einen zu brauchen...
Code: (dl )
1
2
3
while (my $a = readdir(DIR)) {
 ...
}

liest immer nur eine datei ein (while hat einen skalaren kontext)

$a und $b als variablennamen besser nicht verwenden, weil die eigentlich fuer den sort-befehl reserviert sind, und von daher von "use strict;" nicht abgedeckt werden... und je sprechender ein variablenname ist, desto leichter versteht jemand (auch man selbst) das script... lediglich fuer Integer-Laufvariablen hat sich irgendwie $i, $j usw eingebuergert...

../userdaten/main ist IMHO ein Kandidat fuer eine eigene Variable... z.B.
Code: (dl )
1
2
use FindBin;
my $dir = "$FindBin::Bin/../userdaten/main";

(mit Findbin machst du aus einem pfad, der relativ zum ausgefuehrten perlscript angegeben ist, einen absoluten, sodass du das script auch von ausserhalb des verzeichnisses ausfuehren kannst\n\n

<!--EDIT|Strat|1113308290-->
perl -le "s::*erlco'unaty.'.dk':e,y;*kn:ai;penmic;;print"
http://www.fabiani.net/
sesth
 2005-04-12 16:06
#53728 #53728
User since
2005-02-01
181 Artikel
BenutzerIn
[default_avatar]
Ich würde grundsätzlich Variable in regulären Ausdrücken quoten, da man nie wissen kann, ob nicht auch Metasymbole in den Ausdrücken vorkommen:
Code: (dl )
s/userdaten\/\Q$username\E\/bilder/userdaten\/$userid\/bilder/
\n\n

<!--EDIT|sesth|1113307619-->
Gruß
Thomas
Dubu
 2005-04-12 22:10
#53729 #53729
User since
2003-08-04
2145 Artikel
ModeratorIn + EditorIn

user image
Neben dem, was schon genannt wurde:[quote=stb2050,12.04.2005, 12:41]
Code: (dl )
1
2
3
4
5
6
7
8
...
  $userid = $inhalt[0];
  $username = $inhalt[1];

  #Textseiten einlesen

  opendir(DIR, "../userdaten/main");
...
[/quote]
Das ist doch bestimmt nicht das richtige Verzeichnis und soll eigentlich so heissen:
Code: (dl )
opendir(DIR, "../userdaten/$userid/seiten") or die ...
stb2050
 2005-04-13 21:21
#53730 #53730
User since
2003-08-14
87 Artikel
BenutzerIn
[default_avatar]
Hi Ihr,

danke für Eure Antworten :-) Hat super geklappt mit den Test-Daten. Bin ja mal gespannt, wie es morgen mit den realen Datensätzen durchläuft.


@pq: Die Zeile "s#\userdaten/$username/bilder#userdaten/$userid/bilder#g for @content;" hat komischerweise nicht funktioniert, habe stattdessen die Zeile "s|/userdaten/\Q$username\E/bilder|/userdaten/$userid/bilder|g;" von strat eingesetzt, dann hat es gepasst :-)

@strat: Danke, werde deine Hinweise auf jeden Fall beim nächsten Script beachten.

@Dubu: Thx für den Hinweis, ist aber schon korrekt so :) Die Verzeichnis-Struktur ist nen bisschen eigenwillig.

Liebe Grüße,
Steffen
pq
 2005-04-13 22:08
#53731 #53731
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
[quote=stb2050,13.04.2005, 19:21]@pq: Die Zeile "s#\userdaten/$username/bilder#userdaten/$userid/bilder#g for @content;" hat komischerweise nicht funktioniert, habe stattdessen die Zeile "s|/userdaten/\Q$username\E/bilder|/userdaten/$userid/bilder|g;" von strat eingesetzt, dann hat es gepasst :-)[/quote]
ich zitiere mich nochmal: "wieso willst du eigentlich den string 'Serdaten' ersetzen? (s#\userdaten..."

einfach den backslash wegnehmen. hast du verstanden, warum
es nicht geklappt hat? es ist egal, ob du # oder | als regex-trenner
verwendest
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
Crian
 2005-04-14 11:26
#53732 #53732
User since
2003-08-04
5866 Artikel
ModeratorIn
[Homepage]
user image
Ich bin für ~
s--Pevna-;s.([a-z]).chr((ord($1)-84)%26+97).gee; s^([A-Z])^chr((ord($1)-52)%26+65)^gee;print;

use strict; use warnings; Link zu meiner Perlseite
ptk
 2005-04-14 12:52
#53733 #53733
User since
2003-11-28
3645 Artikel
ModeratorIn
[default_avatar]
Ich bin fuer s{...}{...}. Wollen wir ein Voting machen? :-)
<< |< 1 2 >| >> 13 Einträge, 2 Seiten



View all threads created 2005-04-12 14:41.