Στο ακόλουθο παράδειγμα θα προσπαθήσουμε να κάνουμε κάτι αρκετά πιο περίπλοκο. Θα ανοίξουμε το αρχείο Ex0204.txt. θα αντικαταστήσουμε κάποια από τα περιεχόμενά του και στη συνέχεια θα «φιλτράρουμε» όσα από αυτά μας ενδιαφέρουν, αποθηκεύοντάς τα σε ένα ξεχωριστό αρχείο.
#!/usr/bin/perl -w
use strict;
my $file = '/mnt/win_e/Ex0204.txt'; # To log file
my $statfile = 'data.txt'; # To target file
my @page_stats; # Statistika poy exoyn na kanoyn me web selides
open (LOGFILE, "$file") || die "can't open file: $!";
Όπως και προηγουμένως.
while (<LOGFILE>) {
Όσο λοιπόν το LOGFILE είναι ανοικτό και η Perl το διαβάζει γραμμή γραμμή το πρόγραμμά μας θα παίρνει μια μια τις γραμμές (που για τις ανάγκες της ανάγνωσης τοποθετούνται στην προσωρινή μεταβλητή $_) και
if ($_ =~ /gif/)
{
my $do_nothing=1;
}
Εάν η γραμμή περιέχει την ακολουθία χαρακτήρων gif τότε η παράμετρος $do_nothing παίρνει την τιμή 1.
elsif ($_ =~ /jpg/)
{
my $do_nothing=2;
}
Εάν η γραμμή περιέχει την ακολουθία χαρακτήρων jpg τότε η παράμετρος $do_nothing παίρνει την τιμή 2.
elsif ($_ =~ /swf/)
{
my $do_nothing=3;
}
Εάν η γραμμή περιέχει την ακολουθία χαρακτήρων swf τότε η παράμετρος $do_nothing παίρνει την τιμή 3.
elsif ($_ =~ /css/)
{
my $do_nothing=4;
}
Εάν η γραμμή περιέχει την ακολουθία χαρακτήρων css τότε η παράμετρος $do_nothing παίρνει την τιμή 4.
elsif ($_ =~ /exe/)
{
my $do_nothing=5;
}
Εάν η γραμμή περιέχει την ακολουθία χαρακτήρων exe τότε η παράμετρος $do_nothing παίρνει την τιμή 5.
Θα παρατηρήσατε ότι στο παραπάνω παράδειγμα δεν είχαμε να πούμε στην Perl: «Αν αυτό τότε Α, αν το άλλο τότε Β». Σε κάποιες περιπτώσεις η Perl δεν είχε να κάνει τίποτε και ευτυχώς δεν χρειάζεται να πράξουμε τέτοιες «κομπίνες» για να την ξεγελάσουμε. Αν δεν επιθυμούμε να συμβεί κάτι μπορούμε απλώς να αφήσουμε τα άγκιστρα κενά. Π.χ.
elsif ($_ =~ /exe/)
{
}
else
{
Εδώ βρίσκεται όλο το ζουμί της εργασίας μας.
$_ =~ s/.asp (\w)/.asp?$1/;
Αντικατέστησε (s) την ακολουθία χαρακτήρων:
.asp ακολουθούμενη από κενό διάστημα και μετά ένα γράμμα
με την ακολουθία:
.asp? ακολουθούμενη από το ίδιο γράμμα.
$_ =~ s/asp - /asp /;
Αντικατέστησε την ακολουθία:
asp κενό παύλα κενό
με την ακολουθία:
asp κενό
$_ =~ s/.htm -/.htm/;
Αντικατέστησε την ακολουθία:
.htm κενό παύλα
με την ακολουθία:
.htm
push (@page_stats, $_);
Στη συνέχεια τοποθέτησε τα (τροποποιημένα πλέον) περιεχόμενα της $_ στην array @page_stats. Κάθε $_ δηλαδή το περιεχόμενο μιας γραμμής του αρχικού αρχείου γίνεται ένα ξεχωριστό στοιχείο της λίστας @page_stats. (Η πρώτη γραμμή γίνεται το $page_stats[0], η δεύτερη γραμμή το $page_stats[1] κ.ο.κ.)
}
Τέλος της While
Το πρόγραμμα ολοκληρώνεται όπως και το προηγούμενο με την αποθήκευση των δεδομένων σε ένα αρχείο.
}
close (LOGFILE);
open (WRITEFILE, "+>$statfile") or die "can't open file: $!";
my $old_output = select;
select WRITEFILE;
print "@page_stats;\n";
select $old_output;
print "edo eimaste pali\n";
Μερικές επεξηγήσεις:
Για τις αντικαταστάσεις μας χρησιμοποιήσαμε μερικούς περίεργους χαρακτήρες όπως το \w και το $1. Ας δούμε τι σημαίνουν αυτά:
Στο παράδειγμά μας λοιπόν με το \w δείξαμε στην Perl ότι θέλουμε οποιοδήποτε χαρακτήρα. (Το γιατί το \w μπήκε σε παρένθεση εξηγείται παρακάτω.)
Μας κάνει λοιπόν τόσο το «.asp abcd» όσο και το «.asp dbca». Το πρόβλημα είναι ότι εμείς θέλουμε το «.asp abcd» να γίνει «.asp?abcd και το «.asp dbca» να γίνει «.asp?dbca». Δηλαδή, η Perl πρέπει να θυμάται ποιος είναι ο χαρακτήρας και να τον τοποθετεί και πάλι στη θέση του. Αυτή είναι η δουλειά του $1.
Η μεταβλητή $1 αποθηκεύει το περιεχόμενο της πρώτης παρένθεσης που χρησιμοποιήθηκε (στο παράδειγμά μας της \w). Αν υπήρχε και δεύτερη παρένθεση τα περιεχόμενό της θα είχε αποθηκευθεί (captured) στην $2 και ούτω καθ' εξής, μετρώντας από τις αριστερές παρενθέσεις και πηγαίνοντας προς τις δεξιές. Αν υπάρχουν παρενθέσεις μέσα σε παρενθέσεις, ξεκινούμε από μέσα και προχωρούμε το μέτρημα προς τα έξω. Π.χ.
Το (aaa(bbb)ccc) (ddd) μας δίνει: $1 = bbb, $2 = aaabbbccc, $3 = ddd
Η αυτοτέλεια των εργασιών στην $_
Προσπαθώντας να βρω την ακολουθία αντικαταστάσεων του παραδείγματος, αρχικά σκέφθηκα κάτι πιο απλό. Αυτό που ήθελα να κάνω ήταν όπου υπάρχει .asp να διαγράψω το κενό και την -, ενώ όπου υπάρχει .asp fdskjdfskjdfk (κάτι άλλο εκτός από -) να γίνει .asp?fdskjdfskjdfk.
Το απλούστερο λοιπόν θα ήταν να δηλώσω:
s/.asp /asp?/;
Δηλαδή, μετέτρεψε το asp ακολουθούμενο από κενό σε asp? (ακολουθούμενο από ερωτηματικό). Έτσι, θα είχα .asp?- και .asp?fdskjdfskjdfk.
s/.asp?-/asp/;
Δηλαδή, πάρε τώρα όλα τα .asp?- που δημιούργησες και διέγραψε το ?-
Το πρόβλημα με αυτή την προσέγγιση είναι ότι ζητώ από την Perl να κάνει μια εργασία και μετά να τροποποιήσει τα αποτελέσματα αυτής της εργασίας. Αυτό δεν μπορεί να γίνει όπως θα διαπιστώσετε όσοι δοκιμάσετε αυτό το παράδειγμα.
Οι εργασίες είναι διακριτές και τα αποτελέσματα της μιας δεν μπορούν να χρησιμοποιηθούν για τη δεύτερη. Φυσικά θα μπορούσα να σώσω τα αποτελέσματα σε ένα νέο αρχείο και μετά να ανοίξω αυτό το αρχείο και να κάνω τις αλλαγές μου. Αυτό θα δούλευε, αλλά δεν θα ήταν τόσο γρήγορο και αξιόπιστο όσο το να γίνει η δουλειά με τη μία. Προσπαθήστε λοιπόν να ορίζετε διακριτές εργασίες αντικατάστασης (substitution) χωρίς αλληλοεπικάλυψη ώστε η Perl με ένα πέρασμα να μπορεί να κάνει όλες τις δουλειές που επιθυμείτε.
Ολόκληρος ο κώδικας του παραπάνω προγράμματος είναι ο:
#!/usr/bin/perl -w
use strict;
my $file = '/mnt/win_e/Ex0204.txt'; # To log file
my $statfile = 'data.txt'; # To target file
my @page_stats; # Statistika poy exoyn na kanoyn me web selides
open (LOGFILE, "$file") || die "can't open file: $!";
while (<LOGFILE>) {
if ($_ =~ /gif/)
{
my $do_nothing=1;
}
elsif ($_ =~ /jpg/)
{
my $do_nothing=2;
}
elsif ($_ =~ /swf/)
{
my $do_nothing=3;
}
elsif ($_ =~ /css/)
{
my $do_nothing=4;
}
elsif ($_ =~ /exe/)
{
my $do_nothing=5;
}
else
{
$_ =~ s/.asp (\w)/.asp?$1/;
$_ =~ s/asp - /asp /;
$_ =~ s/.htm -/.htm/;
push (@page_stats, $_);
}
}
close (LOGFILE);
open (WRITEFILE, "+>$statfile") or die "can't open file: $!";
my $old_output = select;
select WRITEFILE;
print "@page_stats;\n";
select $old_output;
print "edo eimaste pali\n";