Εισαγωγή στη Γλώσσα Προγραματισμού Perl


Πίνακας Περιεχομένων

Scalar variables *

Τα είδη των quotes (εισαγωγικών) *

Η λειτουργία for *

If elsif else *

Μια συντομογραφία των if else (Ο conditional ?) *

Τελικό αποτέλεσμα – έλεγχος ? τιμή αν είναι σωστό : τιμή αν δεν είναι *

Ψευδοκώδικας (while – until μέρος πρώτο) *

while – until (μέρος δεύτερο) *

Λίγη θεωρία *

Αν ισχύει αυτό τότε εκείνο *

Το πρόγραμμα *

Μια πιο κλασική εφαρμογή της while *

Μια while με μενού επιλογών *

Η «μαγική» while *

Λήψη δεδομένων από το prompt – η @ARGV *

while() do last *

Do *

Arrays (σειρές δεδομένων) και Lists *

Range - assignment *

Η θέση των περιεχομένων μιας array και οι λειτουργίες shift, unshift, push και pop *

Defined – Undefined scalars και arrays *

Η λειτουργία foreach *

Ένα προσωπικό σχόλιο πριν προχωρήσετε παρακάτω *

Εύρεση και διαχείριση αρχείων *

Regular Expressions *

Χρήση Regular Expressions αντί κλασικών προγραμματιστικών εργασιών *

Split και Join (και λίγο substitution) *

Greed (Τραβώντας URLs από Web σελίδες) *

Συγκρίσεις τιμών και ακολουθιών χαρακτήρων (strings) *

Τι είναι τα modules *

Παράδειγμα module (Φέρνοντας web σελίδες από το Internet) *

Μια πιο ευέλικτη εφαρμογή λήψης web σελίδων *

Προσθήκη (concatenation), μαθηματικές πράξεις, συντομογραφίες *

printf και sprintf (στρογγυλοποιήσεις) *

Μεταβλητές Hash *

Συναρτήσεις της Perl *

Η συνάρτηση exists (διαγραφή διπλοτύπων) *

Η μεταβλητή $_ *

Slice – «Κόβοντας» arrays και hashes *

File Handles - Λήψη δεδομένων από αρχεία κειμένου (text files) και επεξεργασία τους χωρίς όμως να πειράξουμε το αρχικό αρχείο. *

Αποθήκευση των αποτελεσμάτων ενός προγράμματος σε ένα αρχείο (print redirection) *

Print to file – Append to file *

Τοποθετώντας δεδομένα σε καινούρια αρχεία *

Αντικαταστάσεις (substitution operations) *

Αντικαταστάσεις μέσα σε λίστες *

Grep και sort (και ένα παράδειγμα κατάργησης διπλοτύπων) *

Ένα απλό configuration file *

Ένα απλό CGI script *

System commands (system function) *

Λαμβάνοντας μέρη μιας web σελίδας *

Δημιουργία και κλήση υποπρογραμμάτων (subroutines) *

Εισαγωγή στο scope (εφαρμογές σε subroutines) *

Λαμβάνοντας τιμές από subroutines *

Δίνοντας τιμές σε μια subroutine – Η @_ array *

Η χρήση της local *

Arrays μέσα σε Arrays *

Hashes που περιέχουν Arrays *

Hashes που περιέχουν hashes *

Παραδείγματα διαχείρισης δεδομένων από text files *

Πώς τοποθετούμε τα περιεχόμενα ενός ολόκληρου αρχείου μέσα σε μια scalar *

Last, Next, Redo *

Loop labels *

References (παραπομπές) *


Scalar variables

Θα δημιουργήσουμε ένα πιο περίπλοκο πρόγραμμα το οποίο λαμβάνει τιμές από διάφορες μεταβλητές (variables) και τις διαχειρίζεται. Η πιο απλή μορφή μεταβλητής στην Perl είναι οι Scalar variables οι οποίες μπορούν να περιέχουν οποιαδήποτε τιμή (αριθμός, κείμενο κ.λπ.), αλλά μόνο μία.

Στο παράδειγμα που ακολουθεί θα δημιουργήσουμε ένα πρόγραμμα το οποίο λαμβάνει μια τιμή σε Fahrenheit και την μετατρέπει σε βαθμούς Κελσίου (Centigrade). Για να γίνει αυτό πρέπει να γνωρίζουμε ότι για να μετατραπεί μια θερμοκρασία από Fahrenheit σε Κελσίου παίρνουμε την τιμή Fahrenheit αφαιρούμε 32 πολλαπλασιάζουμε με το 5 και διαιρούμε με το 9. Ο τύπος είναι:

C = (F-32)*5/9

Όπως γνωρίζουμε και από τα μαθηματικά, τα C και F είναι μεταβλητές και μπορούν να πάρουν οποιαδήποτε τιμή. Για τις ανάγκες του προγράμματός μας τα C και F θα ονομαστούν $cen και $fah αντίστοιχα. Προσέξτε ότι τα ονόματα των μεταβλητών μας ξεκινούν με το σύμβολο $. Έτσι η Perl καταλαβαίνει ότι της ζητούμε να διαχειριστεί scalar variables. Κατά τα άλλα θα μπορούσαν να έχουν όποιο όνομα θέλουμε (π.χ. $foufoutos για τις τιμές σε Fahrenheit και $canivalos για τις τιμές σε Κελσίου), αρκεί να είναι μικρότερο από 255 χαρακτήρες (γράμματα, αριθμούς και την underscore) και να μην αρχίζει από αριθμό ή ειδικό χαρακτήρα. Προτιμούμε φυσικά ένα όνομα που να μας βοηθάει να καταλάβουμε αμέσως τι περιέχει η μεταβλητή. (Έχετε υπ’ όψιν σας πάντως ότι τα ονόματα των μεταβλητών είναι case sensitive. Για την Perl το $canivalos και το $Canivalos είναι δύο διαφορετικές μεταβλητές.)

Με βάση τα παραπάνω λοιπόν η εξίσωση θα γίνει:

$cen = ($fah-32)*5/9

Ας προχωρήσουμε τώρα στη δημιουργία ολόκληρου του προγράμματος:

#!/usr/bin/perl -w

use strict;

Αν και δεν είναι υποχρεωτικές, όλα τα προγράμματά μας πρέπει να ξεκινούν με αυτές τις δύο γραμμές. Η πρώτη (#!/usr/bin/perl –w) δηλώνει στην Perl ότι πρέπει να μας εμφανίζει όλα τα προειδοποιητικά μηνύματα όταν τρέχει το πρόγραμμά μας. Έτσι, μας είναι ευκολότερο να αναγνωρίζουμε τα λάθη μας, ενώ ακόμη και να το πρόγραμμα λειτουργεί κανονικά γνωρίζουμε σε ποια σημεία του υπάρχει κάτι περίεργο που μπορεί κάποτε να δημιουργήσει προβλήματα (π.χ. αν το πρόγραμμα τρέξει σε άλλη μηχανή).

Έχετε υπ’ όψιν σας πάντως ότι οι συμβουλές που θα λάβετε δεν πρέπει να θεωρούνται ως η απόλυτη αλήθεια. Μπορεί το μήνυμα λάθους να αναφέρει ότι το πρόβλημα είναι στη γραμμή 12 και τελικά αυτό να βρίσκεται στη γραμμή 10 ή ακόμη και στην 7. Τέλος, να θυμάστε ότι τα πιο συνηθισμένα λάθη σας θα είναι να ξεχνάτε το ; στο τέλος των γραμμών ή θα χάνετε το λογαριασμό και δεν θα κλείνετε κάποιο άγκιστρο (}). Γι’ αυτό και στην Perl συνηθίζουμε να τοποθετούμε κάθε νέο άγκιστρο που ανοίγει 4 διαστήματα πιο μέσα από το προηγούμενο και να γράφουμε στην ίδια κάθετο με αυτό όλο τον κώδικα που περιέχει. Έτσι, μας είναι οπτικά πιο εύκολο να βλέπουμε ποιος κώδικας περιέχεται σε άγκιστρα και να τα κλείνουμε όλα όπου πρέπει.

Η δεύτερη (use strict) ενημερώνει τον compiler ο οποίος θα διαχειριστεί το πρόγραμμά μας ότι πρέπει να εμποδίσει τη χρήση «επικίνδυνων» εντολών ή συντακτικού. Η Perl είναι μια πολύ ευέλικτη γλώσσα και μας επιτρέπει να κάνουμε πολλά λάθη χωρίς να σταματήσει να λειτουργεί.

Η use strict μας υποχρεώνει να γράφουμε με πιο δομημένο και σωστό τρόπο. Σας συνιστώ λοιπόν ανεπιφύλακτα τη χρήση της.

print "Welcome to the Fahrenheit to Centigrade script\n";

print "Please type a temperature in Fahrenheit:\n";

Ακολουθούν δύο μηνύματα που θέλουμε να εμφανιστούν στην οθόνη του χρήστη και λειτουργούν όπως και το Hello World. Το πρώτο τον καλωσορίζει στην εφαρμογή, ενώ το δεύτερο του ζητά να πληκτρολογήσει μια τιμή σε βαθμούς Fahrenheit.

my $fah;

Στη συνέχεια δηλώνουμε την scalar variable η οποία θα «κρατάει» την τιμή της θερμοκρασίας σε Fahrenheit. Στην Perl δεν είναι υποχρεωτικό να δηλώσουμε ανεξάρτητα κάποια μεταβλητή. Το πρόγραμμα θα λειτουργούσε άριστα και χωρίς το my $fah επειδή η Perl αναγνωρίζει αυτόματα τις μεταβλητές ακόμη και αν τις χρησιμοποιήσουμε «ξαφνικά» χωρίς προηγούμενη δήλωση. Για λόγους σωστής δομής των προγραμμάτων μας όμως σας συνιστώ πάντοτε να τις ορίζετε.

(Η use strict; απαιτεί οπωσδήποτε τη χρήση της my. Για λόγους ταχύτητας όμως μπορείτε να την ορίζετε ταυτόχρονα με την τιμή της. Π.χ. my $price = 15.)

Αξίζει να παρατηρήσουμε ότι στα μικρά προγράμματα που δοκιμάζουμε εδώ είναι πολύ εύκολο να καταλάβουμε τι συμβαίνει και η αναγκαιότητα της δομής δεν φαίνεται τόσο μεγάλη. Αν όμως δημιουργήσουμε ένα σχετικά μεγάλο πρόγραμμα τότε (αν το ξανακοιτάξουμε μετά από μερικές εβδομάδες) θα δυσκολευτούμε πολύ να καταλάβουμε τι συμβαίνει μέσα σε αυτό, ενώ θα είναι σχεδόν αδύνατον για κάποιον άλλον να καταλάβει τον κώδικά μας και να μας βοηθήσει σε κάποιο πρόβλημα.

Σημειώστε τέλος ότι δεν χρειάστηκε να δηλώσουμε στην Perl τι είδους δεδομένα εμπεριέχονται στις μεταβλητές μας. Το $fah θα μπορούσε να είναι ακέραιος αριθμός (θετικός ή αρνητικός), δεκαδικός ή κείμενο χωρίς αυτό να δημιουργήσει κάποιο πρόβλημα. Η Perl αναγνωρίζει αυτόματα το περιεχόμενο και το διαχειρίζεται ανάλογα. Φυσικά εμείς έχουμε την υποχρέωση να μη ζητήσουμε παράλογα πράγματα (π.χ. αφαίρεση μεταβλητής που περιέχει αριθμό από άλλη που περιέχει κείμενο).

$fah = <STDIN>;

chomp ($fah);

Με τις παραπάνω εντολές δηλώνουμε στην Perl ότι θα λάβει την τιμή σε Fahrenheit μέσω του πληκτρολογίου.

Το $fah = <STDIN> σημαίνει ότι η μεταβλητή $fah θα πάρει την τιμή της από το STDIN (standard input), δηλαδή από το πληκτρολόγιο. Για να γίνει αυτό ο χρήστης θα πρέπει να πληκτρολογήσει την τιμή και να πατήσει Enter. Πατώντας το Enter όμως ο υπολογιστής δεν θα λάβει μόνο την τιμή που πληκτρολογήθηκε, αλλά και το πάτημα του Enter (δηλαδή την εντολή για αλλαγή γραμμής). Εμείς όμως θέλουμε μόνο την τιμή και όχι την αλλαγή γραμμής, γι’ αυτό και χρησιμοποιούμε την chomp () για να «καθαρίσουμε» τα δεδομένα που δίνουμε στην Perl από αλλαγές γραμμών.

my $cen = ($fah-32)*5/9;

Ακολουθεί ο ορισμός της μεταβλητής $cen όπου πολύ απλά δηλώνουμε ότι η $cen είναι ίση με την εξίσωση ($fah-32)*5/9. Αφού η Perl γνωρίζει ήδη την τιμή της $fah, η $cen υπολογίζεται άμεσα και εύκολα. Το μόνο που μας μένει λοιπόν είναι να δώσουμε το αποτέλεσμα στον χρήστη.

print "The equivalent of $fah degrees Fahrenheit is $cen Celcious\n";

Όπως μπορείτε να δείτε, μέσα στην print μπορούμε να τοποθετήσουμε τις μεταβλητές μας. Έτσι ο χρήστης θα δει την τιμή που πληκτρολόγησε ο ίδιος, αλλά και το αποτέλεσμα που υπολόγισε το πρόγραμμα.

print "Thank you for using this program\n";

Τέλος, το πρόγραμμα τελειώνει με ένα αποχαιρετιστήριο μήνυμα. Στην Perl δεν χρειάζεται να δηλώσουμε κάποια ειδική εντολή τέλους. Όταν εκτελεστεί και η τελευταία γραμμή κώδικα, το πρόγραμμα θα σταματήσει από μόνο του.

Ο πλήρης κώδικας λοιπόν της εφαρμογής μας είναι ο ακόλουθος:

#!/usr/bin/perl -w

use strict;

print "Welcome to the Fahrenheit to Centigrade script\n";

print "Please type a temperature in Fahrenheit:\n";

my $fah;

$fah = <STDIN>;

chomp ($fah);

my $cen = ($fah-32)*5/9;

print "The equivalent of $fah degrees Fahrenheit is $cen Celcious\n";

print "Thank you for using this program\n";

Επιστροφή στην Κορυφή

Τα είδη των quotes (εισαγωγικών)

Θα έχετε παρατηρήσει ίσως ότι κάποιες φορές χρησιμοποιούμε διπλά εισαγωγικά («»), ενώ κάποιες άλλες μονά (‘’). Η κύρια διαφορά μεταξύ αυτών των δύο είναι ότι στα διπλά εισαγωγικά η Perl πραγματοποιεί variable interpolation. Δηλαδή:

Print “The number of pupils is $students\n”;

Το αποτέλεσμα της εκτύπωσης θα είναι:

The number of pupils is 25.

Δηλαδή η Perl κατάλαβε πως η $students είναι μια scalar και εκτύπωσε την τιμή της. Με άλλα λόγια αντικατέστησε (interpolation) τη μεταβλητή (variable).

Αν είχαμε χρησιμοποιήσει μονά εισαγωγικά (αποστρόφους):

Print ‘The number of pupils is $students’;

Τότε η εκτύπωση θα μας έδινε:

The number of pupils is $students

Για να τυπώσουμε έναν ειδικό χαρακτήρα στα διπλά εισαγωγικά (π.χ. @, $, %, \, “ κ.λπ.) θα πρέπει να υπάρχει μια backslash πριν από αυτόν. Για παράδειγμα:

Print “The amount is $amount \$\n”;

Τότε η εκτύπωση θα μας έδινε:

The amount is 25 $

Φυσικά backslash χρειάζεται και πριν από τον εαυτό της αν θέλουμε να εμφανίζουμε μια \ με την print.

Στα μονά εισαγωγικά δεν χρειάζονται παρόμοια κόλπα (χρήση escape characters όπως είναι η σωστή τους ονομασία) με μόνη εξαίρεση την ίδια την backslash και τις αποστρόφους που πρέπει να δηλώνονται ως \’ και \\ για να τυπωθούν κανονικά (π.χ. για να καταλάβει η Perl ότι υπάρχει κανονική απόστροφος και δεν τελειώνει εδώ η ακολουθία χαρακτήρων που πρέπει να τυπώσει).

Επιστροφή στην Κορυφή

Η λειτουργία for

Η συλλογιστική με την οποία λειτουργεί η for είναι:

Έχουμε αρχική τιμή Χ

 


Αν το Χ εκπληρώνει κάποια συνθήκη (π.χ. X > 500)

 

Κάνε αυτό

 


Τροποποίησε το Χ

Δίνουμε δηλαδή μια αρχική τιμή και μετά όσο ισχύει μια συνθήκη επαναλαμβάνουμε τις ίδιες πράξεις.

Η γενική σύνταξη της for είναι:

For (Αρχική τιμή; Συνθήκη; Μετά το τέλος τροποποίηση της αρχικής τιμής)

{ κάνε αυτό }

Στο ακόλουθο παράδειγμα έχουμε ένα είδος προς πώληση και ελέγχουμε το υπόλοιπο της αποθήκης μας μετά από κάθε παραγγελία:

#!usr/bin/perl -w

use strict;

Τα απαραίτητα αρχικά.

my $forsale = 10000;

Το περιεχόμενο της αποθήκης μας. (Με το my δηλώνουμε ότι θα κάνουμε χρήση της μεταβλητής, κάτι απαραίτητο όταν λειτουργούμε σε use strict.)

my $sold;

Ο αριθμός των πωληθέντων (φυσικά ξεκινάμε από 0, αλλά δεν δίνουμε εδώ τιμή διότι θα το κάνουμε παρακάτω μέσα στην for).

my $order =0;

Ο αριθμός των παραγγελιών που έχουμε λάβει μέχρι τώρα. (0 αφού τώρα ξεκινάμε).

for ($sold =0; $sold < $forsale; $sold = $sold + $order)

$sold =0 είναι η αρχική τιμή μας

$sold < $forsale είναι η συνθήκη μας (Τα πουλημένα τεμάχια πρέπει να είναι λιγότερα από όσα έχουμε συνολικά προς πώληση.)

$sold = $sold + $order

Μετά το πέρας κάθε εκτέλεσης των εργασιών που περιέχονται στα άγκιστρα (και ακολουθούν αμέσως μετά) η $sold θα αλλάζει (θα προσαυξάνεται κατά την order).

{

my $available = $forsale - $sold;

Βρίσκουμε πόσα τεμάχια απομένουν ακόμη.

print "Apomenoyn akomh $available. Posa thelete;\n";

$order = <STDIN>;

chomp ($order);

Ενημερώνουμε για τη διαθεσιμότητα τον χρήστη και λαμβάνουμε την παραγγελία του. Τώρα θα ενημερωθεί η $sold για τη νέα ποσότητα που παραγγέλθηκε και οι εντολές μέσα στα άγκιστρα θα αρχίσουν τα εκτελούνται από την αρχή.

}

Φυσικά το «πρόγραμμά» μας έχει πολλά κενά (δεν στέλνουμε πουθενά τις παραγγελίες, δεν ελέγχουμε αν ο χρήστης ζητάει κάτι λογικό και δεν πληκτρολογεί ασυναρτησίες κ.λπ.). Πρόκειται όμως για μια καλή εισαγωγική εφαρμογή.

Ολόκληρος ο κώδικας του προγράμματος είναι:

#!usr/bin/perl -w

use strict;

my $forsale = 10000;

my $sold;

my $order =0;

for ($sold =0; $sold < $forsale; $sold = $sold + $order) {

my $available = $forsale - $sold;

print "Apomenoyn akomh $available. Posa thelete;\n";

$order = <STDIN>;

chomp ($order);

}

Επιστροφή στην Κορυφή

If elsif else

Η λειτουργικότητα του προγράμματος με το οποίο παρουσιάσαμε τις scalar variables είναι φυσικά πολύ περιορισμένη. Μπορούμε να μετατρέψουμε βαθμούς Fahrenheit σε Κελσίου, αλλά όχι το αντίθετο. Έχουμε βέβαια πλέον τις γνώσεις να δημιουργήσουμε ένα δεύτερο πρόγραμμα που να μετατρέπει βαθμούς Κελσίου σε Fahrenheit, αλλά θα ήταν πολύ πιο λειτουργικό, αν μπορούσαμε να ενσωματώσουμε και τις δύο λειτουργίες μέσα σε ένα πρόγραμμα.

Σε αυτή την περίπτωση όμως όταν ο χρήστης δίνει μια τιμή θα πρέπει με κάποιο τρόπο να δηλώνει αν η τιμή είναι Κελσίου ή Fahrenheit και το πρόγραμμα να προσαρμόζεται ανάλογα. Ας δούμε πώς θα το επιτύχουμε αυτό:

#!/usr/bin/perl -w

use strict;

print "Welcome to the Fahrenheit - Centigrade script\n";

print "Please type the temperature you want to convert:\n";

my $fah; # Oi bathmoi se Fahrenheit

my $cen; # Oi bathmoi se Kelsiou

my $temp; # Temperature

my $forc; # f if Fahrenheit or c if Centigrade

$temp = <STDIN>;

chomp ($temp);

Αποφασίσαμε ότι ο τρόπος με τον οποίο θα λειτουργεί το πρόγραμμά μας είναι ο ακόλουθος:

Ο χρήστης θα πληκτρολογεί μια θερμοκρασία και μετά θα λέει στο πρόγραμμα αν η θερμοκρασία που έδωσε είναι βαθμοί Κελσίου (άρα θέλει να μετατραπούν σε Fahrenheit) ή το αντίθετο.

Οι παράμετροι που θα χρησιμοποιήσουμε είναι:

  • my $fah = Οι βαθμοί σε Fahrenheit
  • my $cen = Οι βαθμοί σε Κελσίου
  • my $temp = Η θερμοκρασία που έδωσε ο χρήστης
  • my $forc = Η πληροφορία για το είδος της θερμοκρασίας που δήλωσε ο χρήστης (f αν είναι Fahrenheit και c αν είναι Κελσίου)

Προσέξτε ότι:

Στο προηγούμενο πρόγραμμα η θερμοκρασία που έδινε ο χρήστης ήταν πάντοτε Fahrenheit. Εδώ όμως αυτό δεν ισχύει. Έτσι, τη θερμοκρασία που έδωσε ο χρήστης την ονομάζουμε $temp. Με τον τρόπο αυτό όμως το πρόγραμμα δεν γνωρίζει τι είδους θερμοκρασία μας έδωσε ο χρήστης. Γι’ αυτό, προσθέσαμε μια ακόμη μεταβλητή η οποία λέει στο πρόγραμμα αν η θερμοκρασία είναι Fahrenheit ή Κελσίου. Τη μεταβλητή αυτή την ονομάσαμε $froc δηλαδή f or c (f αν είναι Fahrenheit και c αν είναι Κελσίου). Όταν $froc = f τότε ο χρήστης μας έδωσε θερμοκρασία σε Fahrenheit, ενώ όταν $froc = c τότε η θερμοκρασία που μας έδωσε θα είναι σε Κελσίου.

Τώρα οι μεταβλητές μας έγιναν περισσότερες και πιο περίπλοκες, απ’ ό,τι στο προηγούμενο πρόγραμμα. Για παράδειγμα πρέπει να θυμόμαστε ότι αποφασίσαμε πως αν ο χρήστης πληκτρολογήσει c (βλέπε παρακάτω) αυτό σημαίνει ότι δήλωσε βαθμούς Κελσίου, ενώ αν πληκτρολογήσει f αυτό σημαίνει ότι δήλωσε βαθμούς Fahrenheit. Επειδή όμως αν χρειαστεί να επανεξετάσουμε αυτό το πρόγραμμα στο μέλλον (ή να το δώσουμε σε κάποιον άλλον) πιθανώς να μην θυμόμαστε αυτά τα στοιχεία, αποφασίσαμε να κρατήσουμε μερικές σημειώσεις.

Αυτά τα σχόλια μπαίνουν μέσα στο πρόγραμμα και τα ξεχωρίζουμε επειδή ξεκινούν με το σύμβολο # (δίεση ή hash). Ένα σχόλιο μπορεί να τοποθετηθεί είτε στην προέκταση μιας γραμμής όπως εδώ:

my $forc; # f if Fahrenheit or c if Centigrade

ή μπορεί να ξεκινάει από την αρχή της γραμμής:

# F to C or C to F program. © 2005 Giorgos Epitidios

Αν θέλουμε το σχόλιο να εκτείνετε σε περισσότερες από μια γραμμές, κάθε μια τους πρέπει να ξεκινάει με το #

# F to C or C to F program. Copyright 2001 Giorgos Epitidios

# Feel free to copy and modify this program

# Long live free software !!!

Συνεχίζοντας το πρόγραμμά μας, ο χρήστης έχει ήδη πληκτρολογήσει τη θερμοκρασία. Τώρα λοιπόν πρέπει να μας πει αν αυτό που μας έδωσε είναι βαθμοί Κελσίου ή Fahrenheit:

print "Type f if what you typed is in Fahrenheit or c if it is in Centigrade:\n";

$forc = <STDIN>;

chomp ($forc);

Με το print του εξηγήσαμε τι είναι αυτό που του ζητάμε (να γράψει f αν η θερμοκρασία που πληκτρολόγησε είναι Fahrenheit και c αν είναι Κελσίου), ενώ με τον κώδικα που ακολουθεί λαμβάνουμε και αποθηκεύουμε (<STDIN>) καθαρό (chomp) τον χαρακτηρισμό της θερμοκρασίας (c ή f).

Τώρα αρχίζουν τα δύσκολα (που όμως όπως θα δείτε δεν είναι και τόσο τρομερά). Το πρόγραμμά μας πρέπει να εκτελέσει μια σειρά από λογικές λειτουργίες και συγκεκριμένα τις ακόλουθες:

  1. Αν ο χρήστης δήλωσε f τότε πρέπει να πάρω την τιμή που έχω καταχωρημένη στη μεταβλητή $temp, να την μετατρέψω σε Κελσίου και να του δώσω το αποτέλεσμα.
  2. Αν ο χρήστης δήλωσε c τότε πρέπει να πάρω την τιμή που έχω καταχωρημένη στη μεταβλητή $temp, να την μετατρέψω σε Fahrenheit και να του δώσω το αποτέλεσμα.
  3. Αν ο χρήστης πληκτρολόγησε κάτι διαφορετικό από c ή f τότε πρέπει να του παρουσιάσω ένα μήνυμα «διαμαρτυρίας».

Φυσικά τα 1 και 2 θα μπορούσαν να τοποθετηθούν και αντίστροφα, ενώ είμαι βέβαιος ότι μπορείτε να σκεφθείτε πολλές άλλες λογικές λειτουργίες που δεν καλύπτονται εδώ (π.χ. μετά το τέλος του προγράμματος να ερωτάται ο χρήστης αν θέλει να μετατρέψει και κάποια άλλη τιμή). Αυτά όμως θα τα δούμε παρακάτω. Εδώ το συντακτικό μοντέλο με το οποίο λειτουργούμε είναι:

  1. Αν συμβαίνει το Α τότε κάνε το Χ (if)
  2. Αν δεν συμβαίνει το Α, αλλά συμβαίνει το Β τότε κάνε Ψ (elsif)
  3. Διαφορετικά κάνε το Ω (else)

Το καλό με την Perl είναι ότι ο δημιουργός της (Larry Wall) είναι γλωσσολόγος. Έτσι, το συντακτικό της Perl θυμίζει πολύ φυσική γλώσσα. Στη ζωή δεν θα λέγαμε ποτέ:

  1. Βγάλε αεροπορικό εισιτήριο για Θεσσαλονίκη και ΚΤΕΛ για Γρεβενά
  2. Αν δεν υπάρχει αεροπλάνο αλλά υπάρχει ΚΤΕΛ βγάλε εισιτήριο για Γρεβενά
  3. Διαφορετικά έλα να με πάρεις με το αυτοκίνητό σου.

Επειδή τα ευκόλως εννοούμενα (το «Αν δεν υπάρχει αεροπλάνο») παραλείπονται θα λέγαμε:

  1. Βγάλε αεροπορικό εισιτήριο για Θεσσαλονίκη και ΚΤΕΛ για Γρεβενά
  2. Διαφορετικά βγάλε εισιτήριο ΚΤΕΛ για Γρεβενά
  3. Διαφορετικά έλα να με πάρεις με το αυτοκίνητό σου.

Αντίστοιχα λοιπόν και στην Perl θα πούμε:

  1. Αν συμβαίνει το Α τότε κάνε το Χ (if)
  2. Διαφορετικά αν συμβαίνει το Β τότε κάνε Ψ (elsif)
  3. Διαφορετικά κάνε το Ω (else)

Όπως και στη φυσική γλώσσα λοιπόν η Perl εκτελεί την πρώτη εντολή (if) και αδιαφορεί για τις υπόλοιπες. Μόνο αν δεν ισχύει η πρώτη θα ασχοληθεί με τη δεύτερη (elsif), την τρίτη (elsif) κ.ο.κ. μέχρι την τελευταία (if) την οποία χρησιμοποιούμε για να της πούμε τι να κάνει όταν δεν ισχύει κανένα από τα παραπάνω (if και elsif).

Σημειώστε ότι μπορούμε να έχουμε όσα elsif θέλουμε (εκτελούνται σειριακά). Στο παράδειγμά μας όμως χρησιμοποιούμε μόνο ένα.

if ($forc eq 'f') {

Αν (if) η παράμετρος που μας λέει τι είδους θερμοκρασία πληκτρολόγησε ο χρήστης ($forc) είναι ίση (eq δηλαδή equal) με f (δηλαδή μας έδωσε θερμοκρασία σε Fahrenheit) τότε … (Σημειώστε ότι αν θέλαμε η forc να είναι διαφορετική από f θα χρησιμοποιούσαμε τον τελεστή ne που σημαίνει non equal. Επίσης προσέξτε ότι βάλαμε το f μέσα σε single quotes δηλαδή σε μονά εισαγωγικά.)

Προσέξτε ότι όλη η δήλωση που σχετίζεται με το if βρίσκεται μέσα σε παρένθεση ($forc eq 'f'). Επίσης, οι εντολές που εξηγούν στο πρόγραμμα τι πρέπει να κάνει αν είναι αληθές το if περικλείονται μέσα σε άγκιστρα ( { ). Το άγκιστρο που ανοίγει (και μετά το οποίο ξεκινούν οι εντολές) βρίσκεται στην ίδια γραμμή με το if ενώ το άγκιστρο που κλείνει (αφού έχουμε δώσει όλες τις εντολές) βρίσκεται ή τελευταίο σε μια γραμμή μόνο του (αν δεν υπάρχει κάτι που πρέπει να γίνει μετά) ή στην αρχή της γραμμής στην οποία δηλώνουμε την επόμενη εντολή (όπως θα δούμε παρακάτω, στην περίπτωσή μας αυτή είναι η elsif).

Σημειώστε ότι τα άγκιστρα (ειδικά αυτά που κλείνουν μια σειρά εντολών) αποτελούν ένα από τα συνηθέστερα πράγματα που ξεχνούν οι προγραμματιστές. Προσέξτε λοιπόν να μην τα ξεχνάτε. Επίσης, θυμηθείτε πάντοτε να βάζετε τις τιμές των παραμέτρων μέσα σε αποστρόφους. Προσέξετε ότι γράψαμε $forc eq 'f' και όχι $forc eq f

$cen = int(($temp-32)*5/9);

Αφού αυτό που θέλουμε είναι οι βαθμοί σε Κελσίου, υπολόγισέ τους χρησιμοποιώντας την εξίσωση $cen = ($temp-32)*5/9.

Στο σημείο αυτό αποφάσισα να προσθέσω κάτι ακόμη στην εξίσωση. Όπως ίσως θα παρατηρήσατε στο προηγούμενο παράδειγμα η μετατροπή από Fahrenheit σε Κελσίου μας έδινε την τιμή σε δεκαδικά. Επειδή όμως έχουμε συνηθίσει να διαβάζουμε τη θερμοκρασία σε ακέραιους αριθμούς, γι’ αυτό και χρησιμοποιήσαμε τη συνάρτηση int () χάρη στην οποία το τελικό αποτέλεσμα θα παρουσιάζεται ως ακέραιος αριθμός. Περικλείοντας λοιπόν όλη τη μαθηματική πράξη ($temp-32)*5/9 με τη συνάρτηση int () έχουμε int(($temp-32)*5/9) και το τελικό αποτέλεσμα (το $cen) θα εμφανίζεται ως ακέραιος αριθμός.

print "The equivalent of $temp degrees Fahrenheit is $cen Centigrade\n";

Αφού με την εξίσωση βρήκαμε την τιμή που επιθυμούσαμε (το $cen) το μόνο που μας μένει είναι να την τυπώσουμε και να την εμφανίσουμε στον χρήστη.

} elsif ($forc eq 'c') {

Όπως αναφέραμε και παραπάνω, πρώτη μας δουλειά είναι να κλείσουμε το άγκιστρο της προηγούμενης σειράς εντολών. Η έκφραση γραμμές κώδικα είναι πιο ακριβής μια και όλες οι γραμμές κώδικα δεν είναι εντολές. Από τώρα και στο εξής λοιπόν θα χρησιμοποιούμε αυτή την ορολογία.

Σημειώστε ότι δεν είναι υποχρεωτικό το άγκιστρο που κλείνει να βρίσκεται στην ίδια γραμμή με την επόμενη γραμμή. Θα μπορούσε να αποτελεί μόνο του μια γραμμή κώδικα οπότε η σύνταξη θα γινόταν

}

elsif ($forc eq 'c') {

ή να αποτελεί το κλείσιμο της προηγούμενης γραμμής (print "The equivalent of $temp degrees Fahrenheit is $cen Centigrade\n"};)

Γενικά η Perl μας δίνει μεγάλες ελευθερίες στον τρόπο σύνταξης κάθε προγράμματος. Προσπαθήστε πάντως να αφήνετε αρκετό ελεύθερο χώρο και να μη «στριμώχνετε» τις γραμμές κώδικα. Κάτι τέτοιο δεν θα επηρεάσει την εκτέλεση του προγράμματος από την Perl, αλλά θα κάνει τη δική σας ζωή πιο εύκολη όταν στο μέλλον θα χρειαστεί να διαβάσετε ξανά τον κώδικά σας. Σας συνιστώ λοιπόν να ακολουθείτε τη σύνταξη που προτείνεται σε αυτό το κείμενο.

Ο μετά το κλείσιμο του άγκιστρου κώδικας

elsif ($forc eq 'c') {

είναι εύκολα κατανοητός με βάση τα παραπάνω.

Αν δεν ισχύουν τα προηγούμενα (elsif) και ο χρήστης έχει πληκτρολογήσει c ($forc eq 'c') τότε …

$fah = int($temp*9/5+32);

print "The equivalent of $temp degrees Centigrade is $fah Fahrenheit\n";

Και εδώ ο κώδικας είναι εύκολα κατανοητός. Κάνουμε τη μετατροπή από Fahrenheit σε Κελσίου (σε ακέραια μορφή) με το $fah = int($temp*9/5+32) και τυπώνουμε το αποτέλεσμα.

} else {

Αν τώρα ούτε η προϋπόθεση του if ($forc eq 'f') αλλά ούτε και εκείνη του elsif ($forc eq 'c') δεν ικανοποιούνται, αυτό σημαίνει ότι ο χρήστης πληκτρολόγησε κάτι διαφορετικό από f ή c. Έτσι, λέμε στο πρόγραμμα να εμφανίσει ένα μήνυμα διαμαρτυρίας.

print "You should have typed f or c. Quiting program\n";

}

Το πρόγραμμα θα σταματήσει να λειτουργεί από μόνο του, οπότε εμείς δεν χρειάζεται να κάνουμε κάτι για να το τερματίσουμε.

Ο πλήρης κώδικας του προγράμματός μας είναι:

#!/usr/bin/perl -w

use strict;

print "Welcome to the Fahrenheit - Centigrade script\n";

print "Please type the temperature you want to convert:\n";

my $fah; # Oi bathmoi se Fahrenheit

my $cen; # Oi bathmoi se Kelsiou

my $temp; # Temperature

my $forc; # f if Fahrenheit or c if Centigrade

$temp = <STDIN>;

chomp ($temp);

print "Type f if what you typed is in Fahrenheit or c if it is in

Centigrade:\n";

$forc = <STDIN>;

chomp ($forc);

if ($forc eq 'f') {

$cen = int(($temp-32)*5/9);

print "The equivalent of $temp degrees Fahrenheit is $cen

Centigrade\n";

} elsif ($forc eq 'c') {

$fah = int($temp*9/5+32);

print "The equivalent of $temp degrees Centigrade is $fah

Fahrenheit\n";

} else {

print "You should have typed f or c. Quiting program\n";

}

Επιστροφή στην Κορυφή

Μια συντομογραφία των if else (Ο conditional ?)

Αν βαριώσαστε να χρησιμοποιήσετε τα if και else, μια πιο σύντομη εφαρμογή τους γίνεται μέσω του συμβόλου ? (με την βοήθεια και του : ). Η σύνταξη είναι:

Τελικό αποτέλεσμα – έλεγχος ? τιμή αν είναι σωστό : τιμή αν δεν είναι

Για παράδειγμα:

my $a = 7;

my $b = 12;

my $minimum = $a < $b ? $a : $b;

print "$minimum\n";

Εδώ η εκτύπωση θα μας δώσει το 7. Η αναλυτική σύνταξη είναι:

Η $minimum θα πάρει την τιμή $a αν είναι αληθές το $a < $b. Διαφορετικά θα πάρει την τιμή $b.

Επιστροφή στην Κορυφή

Ψευδοκώδικας (while – until μέρος πρώτο)

Όσοι δεν έχουν γνώσεις προγραμματισμού πιθανότητα θα συναντήσουν δυσκολίες στην κατανόηση του επόμενοι πιο προχωρημένου προγράμματος υπολογισμού θερμοκρασίας Fahrenheit ή Κελσίου. Ο λόγος γι’ αυτό είναι ότι η λογική δομή που θα ακολουθήσουμε είναι πιο προχωρημένη και απαιτεί μεγαλύτερη ανάλυση.

Συνήθως, τα προγράμματά σας θα είναι τόσο περίπλοκα που πριν ξεκινήσετε την εκτέλεσή τους θα πρέπει να αναλύσετε προσεκτικά τις εργασίες που θα επιτελούν. Αυτό γίνεται είτε με διάγραμμα ροής (γραφική παράσταση των λειτουργιών) είτε με ψευδοκώδικα (λεκτική περιγραφή των λειτουργιών).

Στα πρώτα προγράμματά μας η εκτέλεση ήταν σειριακή και η δημιουργία ψευδοκώδικα μάλλον περιττή.

Στο Hello World ο ψευδοκώδικας ήταν:

  • Γράψε Hello World ! (πιο απλό δεν γίνεται)

Στο πρώτο πρόγραμμα μετατροπής θερμοκρασίας ήταν:

  • Πάρε τη θερμοκρασία από τον χρήστη (από το STDIN)
  • Μετέτρεψέ την σε Κελσίου
  • Δώσε το αποτέλεσμα (και τέλος)

Στο δεύτερο πρόγραμμα μετατροπής θερμοκρασίας οι εργασίες έγιναν περισσότερες, αλλά φαινομενικά παρέμειναν σειριακές:

  • Πάρε τη θερμοκρασία από τον χρήστη (από το STDIN)
  • Μάθε αν είναι Fahrenheit ή Κελσίου (από το STDIN)
  • Αν είναι Fahrenheit μετέτρεψέ την σε Κελσίου και τύπωσε το αποτέλεσμα (και τέλος)
  • Αν είναι Κελσίου μετέτρεψέ την σε Fahrenheit και τύπωσε το αποτέλεσμα (και τέλος)
  • Διαφορετικά γράψε ένα μήνυμα διαμαρτυρίας (και τέλος)

Μια πιο προσεκτική ματιά θα μας δείξει όμως ότι και εδώ στην πραγματικότητα χρησιμοποιήσαμε blocks κώδικα. Ας ξαναδιαβάσουμε τον ψευτοκώδικα λίγο διαφορετικά:

  • Πάρε τη θερμοκρασία από τον χρήστη (από το STDIN)
  • Μάθε αν είναι Fahrenheit ή Κελσίου (από το STDIN)
  • Αν είναι Fahrenheit
    • Μετέτρεψέ την σε Κελσίου
    • Τύπωσε το αποτέλεσμα (και τέλος)
  • Αν είναι Κελσίου
    • Μετέτρεψέ την σε Fahrenheit
    • Τύπωσε το αποτέλεσμα (και τέλος)
  • Διαφορετικά
    • Γράψε ένα μήνυμα διαμαρτυρίας (και τέλος)

Όπως βλέπουμε υπάρχει το block:

    • Μετέτρεψέ την σε Κελσίου
    • Τύπωσε το αποτέλεσμα (και τέλος)

το block:

    • Μετέτρεψέ την σε Fahrenheit
    • Τύπωσε το αποτέλεσμα (και τέλος)

και το block

    • Γράψε ένα μήνυμα διαμαρτυρίας (και τέλος)

κάθε ένα από τα οποία είναι αυτόνομο και αδιαφορεί για το άλλο. Το πιο block θα εκτελεστεί καθορίζεται από τις ανώτερες από αυτά επιλογές:

  • Αν είναι Fahrenheit
  • Αν είναι Κελσίου
  • Διαφορετικά

Σε διάγραμμα ροής αυτό γίνεται:

Στο επόμενο (τρίτο) πρόγραμμα μετατροπής θερμοκρασίας τα πράγματα θα είναι αρκετά πιο περίπλοκα και δεν θα μπορούσαμε να φτιάξουμε εύκολα κάτι παρόμοιο αν δεν είχε προηγηθεί ψευδοκώδικας. Για να κατανοήσουμε καλύτερα το τελικό αποτέλεσμα θα δημιουργήσουμε τον ψευδοκώδικα σε τμήματα (από τα πιο περιεκτικά στα πιο αναλυτικά) και μετά θα προχωρήσουμε στην κατασκευή του προγράμματος:

Η συνοπτική ανάλυση – περιγραφή μας δίνει τα ακόλουθα:

  1. Έναρξη προγράμματος (καλωσόρισμα χρήστη)
  2. Δώσε την τιμή της θερμοκρασίας
  3. Δώσε το είδος της θερμοκρασίας (Fahrenheit ή Κελσίου)
  4. Δώσε το αποτέλεσμα
  5. Ξανάρχισε από την αρχή

Αυτό περίπου μας έχει ζητήσει ο τελικός χρήστης. Εμείς όμως γνωρίζουμε περισσότερα και του εξηγούμε ότι χρειάζεται επίσης έναν τρόπο για να εγκαταλείπει το σύστημα, καθώς και ένα τρόπο για να ελέγχει ότι η τιμή (μηδέν ή θετικός ακέραιος) και το είδος (Fahrenheit ή Κελσίου) της θερμοκρασίας που έδωσε ο χρήστης είναι σωστά.

Σημείωση: Η τιμή της θερμοκρασίας δεν είναι απαραίτητο να είναι θετικός ακέραιος ή μηδέν . Το πρόγραμμα λειτουργεί και με αρνητικές τιμές, αλλά για λόγους που θα φανούν παρακάτω επέλεξα για εδώ αυτόν τον περιορισμό)

Με βάση τα παραπάνω ο ψευδοκώδικάς μας γίνεται:

  • Έναρξη προγράμματος (καλωσόρισμα χρήστη)
    • Δώσε την τιμή της θερμοκρασίας
      • Αν πληκτρολόγησες q
        • Τέλος προγράμματος
      • Αν πληκτρολόγησες θετικό ακέραιο ή μηδέν
        • Δώσε το είδος της θερμοκρασίας (Fahrenheit ή Κελσίου)
          • Αν είναι Fahrenheit
            • Υπολόγισε το αποτέλεσμα
            • Τύπωσε το αποτέλεσμα
            • Ξανάρχισε από την αρχή (ζήτα την τιμή της θερμοκρασίας)
          • Αν είναι Κελσίου
            • Υπολόγισε το αποτέλεσμα
            • Τύπωσε το αποτέλεσμα
            • Ξανάρχισε από την αρχή (ζήτα την τιμή της θερμοκρασίας)
          • Αν είναι κάτι άλλο
            • Τύπωσε μήνυμα διαμαρτυρίας
            • Ξανάρχισε από την αρχή (ζήτα την τιμή της θερμοκρασίας)
      • Αν πληκτρολόγησες κάτι άλλο
        • Ξανάρχισε από την αρχή (ζήτα την τιμή της θερμοκρασίας)

Από τα επίπεδα των στηλών του ψευδοκώδικα καταλαβαίνουμε περίπου ποιες ομάδες εργασιών (blocks κώδικα) πρέπει να δημιουργηθούν.

Επιστροφή στην Κορυφή

while – until (μέρος δεύτερο)

Λίγη θεωρία

Το σύνολο των γραμμών κώδικα που εμπεριέχονται μέσα σε δύο άγκιστρα αποκαλούνται block. Όποιες μεταβλητές ορίσουμε μέσα στο block υπάρχουν και ισχύουν μόνο μέσα σε αυτό και θεωρούνται άγνωστες και ανύπαρκτες για το υπόλοιπο πρόγραμμα.

Η γενική σύνταξη ενός block είναι:

Αν ισχύει αυτό τότε εκείνο

Για παράδειγμα:

if [αν] ($forc eq 'f') [αυτό] τότε

{$cen = int(($temp-32)*5/9);

print "The equivalent of $temp degrees Fahrenheit is $cen Centigrade\n";

} [εκείνο]

Χρησιμοποιώντας τον τελεστή while μια εργασία (το block που ορίζεται από αυτόν) θα εκτελείται για όσο χρονικό διάστημα μια τιμή είναι αληθής. Αντίθετα με τον τελεστή until μια εργασία θα εκτελείται μέχρι κάποια προκαθορισμένη τιμή να είναι αληθής.

Το πρόγραμμα

Ένα από τα μειονεκτήματα του προγράμματος μετατροπής θερμοκρασίας είναι ότι μπορεί να χρησιμοποιηθεί μόνο μια φορά. Αν θέλουμε να μετατρέψουμε μια σειρά από τιμές θα πρέπει για κάθε μια να ξεκινούμε το πρόγραμμα από την αρχή. Αυτό που θέλουμε είναι το πρόγραμμα να μπορεί να χρησιμοποιηθεί επαναλαμβανόμενες φορές μέχρι εμείς να του πούμε ότι δεν το χρειαζόμαστε άλλο.

#!/usr/bin/perl -w

use strict;

print "Welcome to the Fahrenheit - Centigrade script\n";

Κατά τα γνωστά.

my $quit = 0;

until ($quit) {

Για την Perl μια τιμή είναι ψευδής όταν περιέχει 0 (μηδέν), κενό ή αν είναι άδεια. Σε όλες τις άλλες περιπτώσεις είναι αληθής. Στην περίπτωσή μας ορίσαμε τη μεταβλητή $quit την οποία θα χρησιμοποιήσουμε για να μας δηλώνει ο χρήστης πότε θέλει να διακόψει τη χρήση του προγράμματος. Ορίζουμε από την πρώτη στιγμή την τιμή της quit ως 0 (δηλαδή ψευδής). Όταν αλλάξει τότε το πρόγραμμα θα σταματήσει να λειτουργεί.

print "Please type the temperature you want to convert or q to quit:\n";

my $fah; # Oi bathmoi se Fahrenheit

my $cen; # Oi bathmoi se Kelsiou

my $temp; # Temperature

my $forc; # f if Fahrenheit or c if Centigrade

chomp ($temp = <STDIN>);

if ($temp eq 'q') {

$quit =1;

}

Διαφορετικές από τα προηγούμενα είναι μόνο οι δύο τελευταίες γραμμές οι οποίες μας λένε ότι αν ο χρήστης πληκτρολογήσει q τότε η $quit γίνεται ίση με 1 άρα η τιμή της είναι αληθής και η until θα τερματιστεί. Προσέξτε επίσης ότι αλλάξαμε την αφαίρεση του Enter από την πληκτρολόγηση του χρήστη. Αντί για

$temp = <STDIN>;

chomp ($temp);

τώρα έχουμε

chomp ($temp = <STDIN>);

Ένα από τα βασικά ρητά της Perl είναι το: There is more than one way to do it. Εδώ βλέπουμε μια πολύ απλή εφαρμογή αυτού του ρητού.

elsif ($temp =~ m/^\d+$/) {

Ένα πρόγραμμα πρέπει να λειτουργεί σωστά όχι μόνο κάτω από φυσιολογικές συνθήκες, αλλά κάτω από οποιεσδήποτε περιστάσεις. Έτσι, είμαστε υποχρεωμένοι να προβλέψουμε και να διαχειριστούμε ακόμη και τα πιο ανορθόδοξα πράγματα. Ο καλύτερος τρόπος για να το κάνουμε αυτό είναι να ελέγχουμε με ποιο τρόπο οι χρήστες επικοινωνούν με τις εφαρμογές μας.

Στην προκειμένη περίπτωση το πρόγραμμα θα λειτουργήσει μόνο αν ο χρήστης δώσει κάποιον θετικό αριθμό. Για να το επιτύχουμε αυτό χρησιμοποιούμε τον match operator της Perl η γενική σύνταξη του οποίου είναι m// μέσα στα // τοποθετούμε τα κριτήρια αποδοχής που επιθυμούμε. Στην προκειμένη περίπτωση αυτά είναι:

Το \d Σημαίνει «ταίριαξε οποιοδήποτε ψηφίο» (αλλά μόνο ένα). Προσθέτοντας και ένα + αυτό γίνεται \d+ και σημαίνει ταίριαξε μια σειρά από ένα ή περισσότερα ψηφία.

Το ^ όταν τοποθετείται στην αρχή μιας ακολουθίας το ^ σημαίνει «ταίριαξε την αρχή της ακολουθίας».

Το $ Σημαίνει «ταίριαξε το τέλος της ακολουθίας».

Συνεπώς το =~ m/^\d+$ σημαίνει Ταίριαξε από την αρχή και πέρα (^) μόνο ψηφία (\d+) και ταυτόχρονα ταίριαξε από το τέλος και προς τα εμπρός ($)μόνο ψηφία (\d+). Προφανώς, αυτό σημαίνει ότι για να είναι αληθής η τιμή πρέπει ο χρήστης να πληκτρολόγησε μόνο ψηφία (δηλαδή αριθμούς). Έτσι εξασφαλίζουμε ότι ο χρήστης δεν πληκτρολογήσει κατά λάθος γράμματα ή κάτι άλλο στη θέση της θερμοκρασίας.

print "Type f if what you typed is in Fahrenheit or c if it is in Centigrade:\n";

chomp ($forc = <STDIN>);

Ο χρήστης δηλώνει f ή c ανάλογα με τι αντιπροσωπεύει το νούμερο που έδωσε προηγουμένως.

if ($forc eq 'f') {

$cen = int(($temp-32)*5/9);

print "The equivalent of $temp degrees Fahrenheit is $cen Centigrade\n";

}

Υπολογισμός θερμοκρασίας σε Κελσίου.

elsif ($forc eq 'c') {

$fah = int($temp*9/5+32);

print "The equivalent of $temp degrees Centigrade is $fah Fahrenheit\n";

}

Υπολογισμός θερμοκρασίας σε Fahrenheit.

else {

print "You should have typed f or c. Restarting program\n";

}

Ο χρήστης πληκτρολόγησε κάτι διαφορετικό από f ή c, γι’ αυτό και το πρόγραμμα τυπώνει απλώς το μήνυμα διαμαρτυρίας και αφού φθάσαμε στο τέλος του block (f ή c ή κάτι άλλο) το πρόγραμμα θεωρεί ότι ολοκλήρωσε την εργασία του και θα ξεκινήσει από την αρχή.

}

else {

print "You should have typed q (for quit) or a valid equal or greater than zero integer. Restarting program\n";

}

Φθάσαμε στο τέλος του block (q ή ακέραιος μεγαλύτερος ίσος του μηδενός ή κάτι άλλο) το πρόγραμμα θεωρεί ότι ολοκλήρωσε την εργασία του και θα ξεκινήσει από την αρχή.

}

print "Thank you for using this program\n";

Το πρόγραμμα τελειώνει με ένα αποχαιρετιστήριο μήνυμα.

Σε περίπτωση που θέλουμε να χρησιμοποιήσουμε την while αντί για την until τότε οι αλλαγές στο πρόγραμμα θα είναι:

my $quit = 1;

while ($quit) {

αντί για:

my $quit = 0;

until ($quit) {

και:

if ($temp eq 'q') {

$quit =0;

αντί για:

if ($temp eq 'q') {

$quit =1;

(αλλάζει μόνο η τιμή της $quit, αλλά γράφω και την από πάνω γραμμή για να την βρείτε πιο εύκολα στον κώδικα).

Το πλήρες πρόγραμμα (με while αντί για until) έχει την ακόλουθη μορφή:

#!/usr/bin/perl -w

use strict;

print "Welcome to the Fahrenheit - Centigrade script\n";

my $quit = 1;

while ($quit) {

print "Please type the temperature you want to convert or q to quit:\n";

my $fah; # Oi bathmoi se Fahrenheit

my $cen; # Oi bathmoi se Kelsiou

my $temp; # Temperature

my $forc; # f if Fahrenheit or c if Centigrade

chomp ($temp = <STDIN>);

if ($temp eq 'q') {

$quit =0;

}

elsif ($temp =~ m/^\d+$/) {

print "Type f if what you typed is in Fahrenheit or c if it is in Centigrade:\n";

chomp ($forc = <STDIN>);

if ($forc eq 'f') {

$cen = int(($temp-32)*5/9);

print "The equivalent of $temp degrees Fahrenheit is $cen Centigrade\n";

}

elsif ($forc eq 'c') {

$fah = int($temp*9/5+32);

print "The equivalent of $temp degrees Centigrade is $fah Fahrenheit\n";

}

else {

print "You should have typed f or c. Restarting program\n";

}

}

else {

print "You should have typed q (for quit) or a valid equal or greater than zero integer. Restarting program\n";

}

}

print "Thank you for using this program\n";

Επιστροφή στην Κορυφή

Μια πιο κλασική εφαρμογή της while

Τις περισσότερες φορές προτιμούμε μέσα στην παρένθεση του while να υπάρχει ένας συγκεκριμένος όρος (condition) που όσο ισχύει η while συνεχίζει να λειτουργεί. Ας δούμε το ακόλουθο πρόγραμμα όπου ο χρήστης πρέπει να μας δώσει την ηλικία του και εμείς θα κάνουμε κάποια σχόλια πάνω σ’ αυτήν:

#!/usr/bin/perl -w

use strict;

Τα γνωστά

print "----------------------------------------------------------------------------------\n";

print "KALOSILΘES STHN EFARMOGH THS ALITHIAS GIA THN ILIKIA SOU\n";

print "----------------------------------------------------------------------------------\n";

Τα εισαγωγικά.

my $quit = 1;

Δίνουμε την αρχική τιμή της $quit.

while ($quit == 1)

Όσο η $quit είναι ίση με ένα, δηλαδή όσο διατηρεί την αρχική της τιμή το πρόγραμμα θα τρέχει (θα επαναλαμβάνεται) συνέχεια. Θα τερματίσει μόνο όταν αυτή αλλάξει. (Προσέξτε τα δύο = = που σημαίνουν σύγκριση. Αν λέγαμε $quit=1 αυτό θα σήμαινε ότι δίνουμε στην $quit την τιμή 1. Υπάρχει assignment τιμής και όχι condition. Συνεπώς το πρόγραμμα θα τρέχει για πάντα αφού δεν υπάρχει όρος που να του λέει πότε να σταματήσει..)

{

Αρχίζει το περιεχόμενο της while.

my $ilikia;

print " GRAPSE THN ILIKIA SOU:\n";

$ilikia = <STDIN>;

chomp $ilikia;

Ο χρήστης δίνει την ηλικία του.

if ( $ilikia eq '')

{

print "Prepei na grapshs kati\n";

}

Αν ο χρήστης πατήσει Enter χωρίς να γράψει τίποτε το πρόγραμμά μας θα λάβει ένα άδειο string και φυσικά θα διαμαρτυρηθεί γι’ αυτό. (eq σημαίνει equal. Δείτε σχετικά τα κείμενα για τους λογικούς τελεστές.)

elsif ( $ilikia =~ m/^-\d+$/)

{

print "EISE AGENITOS\n";

}

Αν πληκτρολογηθεί αρνητικός αριθμός (βλέπε σχετικά στις regular expressions). Σε αυτή την περίπτωση το πρόγραμμα (για την ακρίβεια η while) θα ξαναρχίσει από την αρχή.

elsif ($ilikia =~ m/\D/)

{

print "LES ANOHSIES\n";

}

Αν πληκτρολογηθεί μη αριθμητικός χαρακτήρας (βλέπε σχετικά στις regular expressions). Σε αυτή την περίπτωση το πρόγραμμα (για την ακρίβεια η while) θα ξαναρχίσει από την αρχή.

else

Αν δηλαδή η τιμή είναι σωστή (αποδεκτή).

{

if ( $ilikia >=1 and $ilikia <= 15 )

{

print "Eisai moro\n";

}

elsif ( $ilikia > 15 && $ilikia <= 19 )

{

print "Eisai gematos spirakia\n";

}

elsif ( $ilikia > 19 && $ilikia <= 35 )

{

print "Eisai ATOMO\n";

}

elsif ( $ilikia > 35 && $ilikia <= 999 )

{

print "Eisai apokliros kai ora na doseis topo stous neous\n";

}

else

{

print "LES ANOHSIES\n";

}

$quit = 2;

}

Μια σειρά από εναλλακτικές επιλογές που τελειώνουν με ορισμό της $quit σε κάτι διαφορετικό του 1. ’ρα η while σταματά να επαναλαμβάνεται.

}

Κλείσιμο της while.

Η γενική σύνταξη της while λοιπόν είναι:

While ( κάτι ) {σειρά εργασιών}

Δηλαδή για όσο χρονικό διάστημα ισχύει το «κάτι» να εκτελείς συνεχώς αυτές τις εργασίες.

Επιστροφή στην Κορυφή

Μια while με μενού επιλογών

Η προηγούμενη εφαρμογή λειτουργεί μόνο μια φορά και επαναλαμβάνει τον εαυτό της αποκλειστικά στην περίπτωση που ο χρήστης έδωσε λανθασμένα στοιχεία. Εδώ θα τοποθετήσουμε ένα μενού επιλογών στο τέλος της έτσι ώστε ο χρήστης να επιλέγει αν θέλει να δώσει άλλα στοιχεία ή να την εγκαταλείψει.

Μια καλογραμμένη εφαρμογή είναι απλή και διαθέτει μεγάλη ευελιξία. Ο κώδικας λοιπόν δεν θα είναι πολύ διαφορετικός:

#!/usr/bin/perl -w

use strict;

print "---------------------------------------------------------------\n";

print " KALOSILΘES STHN EFARMOGH THS ALITHIAS GIA THN ILIKIA SOU \n";

print "---------------------------------------------------------------\n";

my $quit = 1;

while ($quit == 1)

{

my $ilikia;

print " GRAPSE THN ILIKIA SOU:\n";

$ilikia = <STDIN>;

chomp ($ilikia);

if ( $ilikia eq '')

{

print "Prepei na grapshs kati\n";

}

elsif ( $ilikia =~ m/^-\d+$/)

{

print "EISE AGENITOS\n";

}

elsif ($ilikia =~ m/\D/)

{

print "LES ANOHSIES\n";

}

else

{

if ( $ilikia >=1 and $ilikia <= 15 )

{

print "Eisai moro\n";

}

elsif ( $ilikia > 15 && $ilikia <= 19 )

{

print "Eisai gematos spirakia\n";

}

elsif ( $ilikia > 19 && $ilikia <= 35 )

{

print "Eisai ATOMO\n";

}

elsif ( $ilikia > 35 && $ilikia <= 999 )

{

print "Eisai apokliros kai ora na doseis topo stous neous\n";

}

else

{

print "LES ANOHSIES\n";

}

Στην προηγούμενη εφαρμογή εδώ υπήρχε ένα $quit=2 ώστε να διακόπτεται το loop. Τώρα το αφαιρέσαμε και η Perl συνεχίζει να διαβάζει ό,τι υπάρχει παρακάτω.

}

Μόλις τελείωσε το μεγάλο else που περιέχει όλες τις αποδεκτές τιμές που μπορεί να μας δώσει ο χρήστης. Η Perl λοιπόν συνεχίζει εκτελώντας ό,τι υπάρχει παρακάτω.

my $grapse;

print "Gia na ksanagrapseis THN ILIKIA SOU type G \n";

print "Gia na bgeis type X \n";

$grapse = <STDIN>;

chomp ($grapse);

Ενημερώνουμε τον χρήστη για τις επιλογές του και του ζητάμε να εισάγει εκείνη που επιθυμεί:

if ( $grapse eq 'g')

{

}

Αν γράψει g, δηλαδή θέλει να συνεχίσει δεν δηλώνουμε τίποτε. Η $quit λοιπόν παραμένει 1 και όσα βρίσκονται μέσα στη while θα ξαναρχίσουν από την αρχή.

elsif ( $grapse eq 'x')

{

$quit = 3;

}

Ο χρήστης έδωσε x, άρα δίνουμε στην $quit τιμή διαφορετική του 1 για να διακοπεί η while (και μαζί της το πρόγραμμα αφού δεν υπάρχει κάτι άλλο παρακάτω).

else

{

print "Eprepe na grapseis g or x. H efarmogh ksanarxizei...\n";

print "\n";

}

Αν ο χρήστης μας έδωσε κάτι άλλο του διαμαρτυρόμαστε και ξαναρχίζουμε από την αρχή. (Χρησιμοποιήσαμε το print "\n" για να αφήσουμε μια κενή γραμμή μεταξύ της διαμαρτυρίας και της γραμμής εισαγωγής των νέων δεδομένων.)

}

Το άγκιστρο αυτό σηματοδοτεί το τέλος της while.

Επιστροφή στην Κορυφή

Η «μαγική» while

Χάρη στην Perl, συχνά με πολύ λίγο κώδικα μπορούμε να επιτύχουμε πάρα πολλά πράγματα. Ας δούμε το ακόλουθο παράδειγμα όπου ψάχνουμε για να βρούμε σε ποια σημεία (γραμμές) ενός αρχείου περιέχεται κάποια ακολουθία χαρακτήρων (η λέξη αποδοτικά):

#!/usr/bin/perl -w

use strict;

open (FILE, 'whatnew.htm') or die "can't open whatnew.htm $!";

Μέχρι τώρα διαχειριστήκαμε μόνο δεδομένα που λαμβάναμε από το πληκτρολόγιο (STDIN, δηλαδή standart input). Πιθανώς όμως να επιθυμούμε τη διαχείριση του περιεχομένου κάποιου αρχείου (στην περίπτωσή μας του αρχείου whatnew.htm). Τότε θα πρέπει να δηλώσουμε στην Perl ότι θέλουμε να ανοίξει κάποιο αρχείο και να της πούμε ποιο αρχείο είναι αυτό.

Με το open (FILE, 'whatnew.htm') επιτυγχάνουμε και τους δύο αυτούς στόχους. Το FILE δηλώνει στην Perl ότι πρέπει να διαχειριστεί ένα αρχείο (γι’ αυτό και στη βιβλιογραφία θα το δείτε να ονομάζεται filehandle), ενώ μέσα στις αποστρόφους υπάρχει το όνομα του αρχείου.

Όπως αναφέραμε και σε άλλο σημείο ο καλός προγραμματιστής πρέπει να προβλέπει όλες τις δυνατές πιθανότητες. Στην προκειμένη περίπτωση λοιπόν δεν αρκεί να δηλώσουμε στην Perl ποιο αρχείο να ανοίξει, αλλά και τι θα κάνει στην περίπτωση που δεν είναι δυνατόν να ανοιχθεί το αρχείο (π.χ. δεν υπάρχει). Γι’ αυτό και προσθέσαμε το

or die "can't open whatnew.htm $!"

Το or είναι ένας λογικός τελεστής (το ή δηλαδή το διαζευκτικό στα Ελληνικά) που δηλώνει στην Perl τι να πράξει αν τα πράγματα δεν πάνε καλά (δηλαδή αν δεν ανοίξει το αρχείο). Και αυτό που της λέμε είναι να διακόψει το πρόγραμμα (die), τυπώνοντας στην οθόνη τη φράση can't open whatnew.htm ακολουθούμενη από την τιμή της παραμέτρου $!

Η $! είναι μια ειδική παράμετρος η οποία περιέχει πάντοτε το τρέχον μήνυμα λάθους του συστήματος. Όταν λοιπόν προσπαθήσουμε να τρέξουμε το πρόγραμμα και αποτύχουμε το σύστημα θα δημιουργήσει ένα μήνυμα λάθους (π.χ. δεν έχετε δικαιώματα να τρέξετε το αρχείο, το αρχείο δεν υπάρχει κ.λπ.) το οποίο θα αποθηκευτεί στην $!. Προσθέτοντας λοιπόν την $! σε όσα μας δηλώνει η die, θα μπορέσουμε να δούμε αμέσως για ποιο λόγο τερμάτισε πρόωρα το πρόγραμμά μας και θα διορθώσουμε το πρόβλημα που έχει δημιουργηθεί με πολύ μεγαλύτερη ευκολία.

while (<FILE>) {

Όσο είναι ανοιχτό το αρχείο (ή όπως θα δούμε αλλού τα αρχεία) και το επεξεργαζόμαστε

if (m/αποδοτικά/) {

όπου βρεις τη λέξη αποδοτικά

print "$_";

Τύπωσε την $_. Η $_ είναι μια πολύτιμη «μαγική» παράμετρος η οποία αναφέρεται σε μια γραμμή κειμένου και περιέχει διαδοχικά όλες γραμμές του αρχείου μας (δηλαδή εδώ του whatnew.htm). Αντί λοιπόν να γράψουμε ειδικό κώδικα ο οποίος να λέει στην Perl «πέρασε μια μια όλες τις γραμμές του αρχείου και όπου βρεις τη λέξη αποδοτικά τύπωσέ μου όλη τη γραμμή», της λέμε «αν βρεις το αποδοτικά τύπωσε την τρέχουσα γραμμή» (δηλαδή την $_). Η Perl είναι αρκετά έξυπνη για να καταλάβει ότι αφού «εδώ» βρήκε τη λέξη αποδοτικά, άρα αυτή είναι η τρέχουσα γραμμή και έτσι την τυπώνει, συνεχίζοντας αμέσως μετά το ψάξιμο για την επόμενη εμφάνιση της λέξης αποδοτικά.

}

Κλείνει το άγκιστρο του block print "$_";

}

Κλείνει το άγκιστρο του block που ορίζει η While

close FILE;

Αφού τελείωσε η εργασία (η while έφθασε στο τέλος του αρχείου και τερμάτισε) κλείνουμε το αρχείο.

Ο πλήρης κώδικας που περιγράφηκε παραπάνω είναι ο:

#!/usr/bin/perl -w

use strict;

open (FILE, 'whatnew.htm') or die "can't open whatnew.htm $!";

while (<FILE>) {

if (m/αποδοτικά/) {

print "$_";

}

}

close FILE;

Επιστροφή στην Κορυφή

Λήψη δεδομένων από το prompt – η @ARGV

Πολλές φορές είναι πιο εύκολο να δηλώνουμε από τη γραμμή εντολών ποια είναι τα δεδομένα που θέλουμε να διαχειριστεί το πρόγραμμά μας. Στο ακόλουθο παράδειγμα δηλώνουμε τα αρχεία που θέλουμε να χρησιμοποιήσει η εφαρμογή μας με αυτό τον τρόπο:

perl test.pl test1.txt test2.txt test3.txt

Η Perl θα πάρει το περιεχόμενο των αρχείων test1.txt, test2.txt και test3.txt για να το χρησιμοποιήσει όπως ορίζει το πρόγραμμα test.pl. Ο σχετικός κώδικας είναι:

my $input_from_files;

while (defined($input_from_files = <>))

{

print "$input_from_files\n";

}

Το <> δηλώνει στην Perl ότι θα ασχοληθεί με τα περιεχόμενα των αρχείων που δηλώθηκαν στο prompt. Κάθε γραμμή κάθε αρχείου τοποθετείται στο $input_from_files και τα περιεχόμενά της υπόκεινται στις εντολές του block που ακολουθεί (εδώ μια απλή εκτύπωση). Η συνάρτηση defined υπάρχει απλώς για να σταματάει η Perl όταν δεν υπάρχουν άλλα περιεχόμενα στο αρχείο. Πιο σύντομα αυτό μπορεί να γραφτεί και:

while (<>)

{print;}

Αυτό που γίνεται print είναι φυσικά η $_.

Πώς όμως η Perl γνωρίζει τι γράψαμε εμείς στο prompt; Το καταλαβαίνει επειδή ό,τι γράψουμε μετά το perl program_name.pl αποθηκεύεται αυτόματα στη μεταβλητή @ARGV απ’ όπου και μπορούμε να το λάβουμε και χρησιμοποιήσουμε.

#!usr/bin/perl -w

use strict;

print "@ARGV\n";

print "$ARGV[0]\n";

Ο παραπάνω κώδικας εκτυπώνει ολόκληρο το περιεχόμενο της @ARGV, ενώ στην επόμενη γραμμή εκτυπώνεται το περιεχόμενο του πρώτου argument. Για παράδειγμα, εγώ έδωσα:

perl test2.pl 153 154 155

και η εκτύπωση ήταν:

153 154 155

153

Επιστροφή στην Κορυφή

while() do last

Μέχρι τώρα συναντήσαμε μόνο loops (επαναλαμβανόμενες διαδικασίες) όπου υπήρχε ένας έλεγχος τέλους, όπως while ($test ne 3) { κάνε κάτι}. Αν η $test έπαιρνε την τιμή 3 τότε όλα σταματούσαν. Αν στη while δεν δηλώσουμε κάποιον έλεγχο τότε αυτή θα τρέχει για πάντα (infinite loop). Για παράδειγμα:

while ()

{

print "Kolhsameeeeeeeeeeee\n";

}

Εδώ η εκτύπωση είναι:

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

K^C

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Kolhsameeeeeeeeeeee

Με Ctrl –C διακόψαμε το πρόγραμμα. Διαφορετικά θα έτρεχε για πάντα.

Επιστροφή στην Κορυφή

Do

Στη while και την until η σύνταξη είναι while ($test ne 3) { κάνε κάτι}. Πρώτα λοιπόν ελέγχουμε και μετά εκτελούμε. Η Do (που στην πραγματικότητα είναι συνάρτηση και γι’ αυτό χρειάζεται το ; στο τέλος της while) λειτουργεί ανάποδα. Πρώτα εκτελούμε και μετά ελέγχουμε. Για παράδειγμα:

my $sum = 0;

my $input;

do {

print "Type a number\n";

$input = <STDIN>;

chomp ($input);

$sum = $sum + $input;

} until ($sum > 300);

print "The sum is $sum\n";

Η εφαρμογή μας θα ζητάει την εισαγωγή νέων αριθμών όσο το until είναι αληθές. Θα σταματήσει λοιπόν μόνο όταν το $sum πάρει τιμή μεγαλύτερη από 300. Αν αντί για until ($sum > 300) δηλώσουμε while ($sum > 300) το loop θα τρέξει μόνο μια φορά αν η πρώτη τιμή που δηλώσουμε είναι 300 ή μικρότερη! Αυτό συμβαίνει επειδή το while loop εκτελείται μόνο αν ισχύει ό,τι έχει δηλωθεί σε αυτό. Αν λοιπόν το πρώτο $input είναι 300 ή μικρότερο, τότε δεν ισχύει το $sum > 300 και το loop διακόπτεται. Αν πάλι είναι μεγαλύτερο από 300 τότε θα κρατήσει για πάντα (infinite loop).

Επιστροφή στην Κορυφή

Arrays (σειρές δεδομένων) και Lists

Μέχρι τώρα χρησιμοποιήσαμε για τα προγράμματά μας μόνο μεμονωμένες τιμές (scalar variables). Για παράδειγμα, δηλώναμε $foo = 'Γιώργος ' και η Perl καταλάβαινε ότι η μεταβλητή $foo έχει την τιμή ‘Γιώργος’.

Πολλές φορές όμως θέλουμε να διαχειριστούμε σειρές από πολλές τιμές (arrays) και δεν θα ήταν καθόλου πρακτικό να θεωρήσουμε κάθε τιμή ως μια ξεχωριστή scalar variable.

Για παράδειγμα μπορεί να θέλουμε να διαχειριστούμε μια λίστα από θερμοκρασίες (25, 27, 26, 29, 27, 27, 28), έναν αριθμό από αρχεία (whatnew.htm, whatnew2.htm, what.htm) κ.λπ. Ολόκληρη αυτή τη σειρά δεδομένων τη συμβολίζουμε στην Perl ως μια array variable. Οι μεταβλητές αυτές μπορούν να έχουν όποιο όνομα επιθυμούμε και ξεχωρίζουν από τις scalar variables διότι ο πρώτος χαρακτήρας αντί για $ είναι @. Το όνομα της array πρέπει να είναι μικρότερο από 255 χαρακτήρες και να αποτελείται μόνο από γράμματα, αριθμούς και το χαρακτήρα underscore (_). Αντίθετα από τις scalars το όνομα μιας array μπορεί να αρχίζει από αριθμό, αλλά τότε θα περιέχει υποχρεωτικό μόνο αριθμούς.

Μια άδεια array ορίζεται ως: @empty =();

Ας δούμε ένα μικρό παράδειγμα γεμάτων arrays:

@only_a_list = (8, 9, 10);

Η λίστα αυτή αποτελείται από τις τιμές 8, 9 και 10. Όπως όμως αναφέραμε παραπάνω λίστα για την Perl σημαίνει σειρά δεδομένων. Τα περιεχόμενά της λοιπόν δεν είναι περιορίζονται μόνο σε αριθμούς:

my $test= 7;

@only_a_list = (8, 9, 10);

@just_a_list = (7, 'giorgos', 7+4, @only_a_list, 'hellow world', "this is a

test", $test);

print "@just_a_list\n";

Η εκτύπωση της @just_a_list θα μας δώσει:

7 giorgos 11 8 9 10 hellow world this is a test 7

Παρατηρούμε ότι:

  • Το giorgos όπως και το hellow world ή το this is a test τυπώθηκαν χωρίς τα εισαγωγικά ή τις αποστρόφους τους (“” ή ‘’).
  • To 7+4 έγινε 11 (η Perl έκανε αυτόματα την πρόσθεση)
  • Μέσα στην @just_a_list περιλάβαμε τη σειρά @only_a_list. Η Perl μπορεί να δεχθεί σειρές μέσα σε άλλες σειρές και διαχειρίζεται το τελικό αποτέλεσμα ως μια μεγάλη σειρά. Έτσι, το @only_a_list αντικαταστάθηκε από τα περιεχόμενά του: 8 9 10
  • Μια σειρά μπορεί να περιέχει και scalar variables (στο παράδειγμά μας την $test) και «καταλαβαίνει» αυτόματα ποιο είναι το περιεχόμενό τους (στην προκειμένη περίπτωση το 7)

Αν και οι αριθμοί δηλώνονται στην Perl χωρισμένοι απλώς με κόμματα (π.χ. 8, 9, 10) οι λέξεις δηλώθηκαν στη λίστα χωρισμένες με εισαγωγικά ή αποστρόφους (“” ή ‘’). Αν δοκιμάσουμε να βάλουμε το giorgos χωρίς αποστρόφους ή εισαγωγικά:

@just_a_list = (7, giorgos, 7+4, @only_a_list, 'hellow world', "this is a

test", $test);

τότε αν η Perl που λειτουργεί υπό συνθήκες «αυστηρής» σύνταξης (use strict) δεν θα εκτελέσει το πρόγραμμά μας διαμαρτυρόμενη επειδή χρησιμοποιήσαμε μια “bareword” (μια λέξη χωρίς δηλωτικά σύμβολα που να ορίζουν πού αρχίζει και πού τελειώνει). Επειδή λοιπόν εμείς είμαστε οπαδοί της αυστηρής σύνταξης (για να αποφεύγουμε τα λάθη) φροντίζουμε να μην υπάρχουν ποτέ bareworlds στις λίστες μας.

Αυτό δυστυχώς είναι πρόβλημα όταν θέλουμε να δηλώσουμε πολλές από αυτές. Για να γλιτώσουμε λοιπόν τον μπελά της πληκτρολόγησης εισαγωγικών ή αποστρόφων (και να αποφύγουμε τον κίνδυνο να ξεχάσουμε κάτι) χρησιμοποιούμε τη συνάρτηση qw. Το:

my @word_test = qw(Ιανουάριος Φεβρουάριος Μάρτιος Απρίλιος);

είναι ισοδύναμο ως έκφραση με το:

my @word_test = (‘Ιανουάριος’, ‘Φεβρουάριος’, ‘Μάρτιος’, ‘Απρίλιος’);

Σημειώστε επίσης ότι εκτός από παρενθέσεις η qw λειτουργεί και με // ή ||.

Όλες οι παραπάνω εκφράσεις δοκιμάστηκαν με το ακόλουθο παράδειγμα:

#!/usr/bin/perl -w

use strict;

my @only_a_list;

my @just_a_list;

my $test= 7;

my @word_test = qw/Ιανουάριος Φεβρουάριος Μάρτιος Απρίλιος/;

@only_a_list = (8, 9, 10);

@just_a_list = (7, 'giorgos', 7+4, @only_a_list, 'hellow world', "this is a

test", $test);

print "@just_a_list\n";

print "@word_test\n";

Επιστροφή στην Κορυφή

Range - assignment

Η Perl μας προσφέρει έναν εύκολο τρόπο δημιουργίας arrays με σειρές γραμμάτων ή αριθμών:

my @number_array = (25 .. 48);

Θα δημιουργήσει μια array με περιεχόμενα τις τιμές από το 25 έως και το 48.

my @letter_array = ('a' .. 'y');

Θα δημιουργήσει μια array με περιεχόμενα τα γράμματα από το a έως και το y (με αλφαβητική σειρά φυσικά).

Για όσους τώρα αναρωτιούνται ποια η διαφορά ανάμεσα σε λίστα και array, ας δούμε το ακόλουθο παράδειγμα:

($foo, $bar, $oof, $rab) = ( 1, 2, 3, 4 );

Εδώ έχουμε δύο λίστες και η δεύτερη δίνει τιμές στις μεταβλητές της πρώτης (το $foo γίνεται 1, το $bar γίνεται 2 κ.ο.κ.).

($foo , $bar) = ($bar, $foo);

Εδώ οι τιμές των μεταβλητών της αριστερής λίστας ανταλλάσσονται (η $foo παίρνει την τιμή της $bar και αντιστρόφως). Η σύνταξη αυτή μπορεί να λειτουργήσει και για περισσότερες από δύο μεταβλητές και η Perl καταφέρνει να κάνει τις αντιμεταθέσεις τιμών χωρίς να χαθεί ή να μπερδευτεί τίποτε.

Επιστροφή στην Κορυφή

Η θέση των περιεχομένων μιας array και οι λειτουργίες shift, unshift, push και pop

my @content = (12, 8, 15);

Με τον τρόπο αυτό δημιουργήσαμε μια λίστα με τις τιμές 12, 8 και 15

my $deytero = $content[1];

Τώρα δώσαμε στη (scalar) μεταβλητή $deytero την τιμή της δεύτερης εγγραφής της λίστας (δηλαδή 8). Το $content[1] σημαίνει πάρε τη δεύτερη εγγραφή και όχι την πρώτη επειδή η Perl ξεκινάει το μέτρημα από το 0. Έτσι, $content[0] είναι η τιμή 12 και $content[2] η τιμή 15.

my $trito = $content[-1];

Αν θέλουμε μπορούμε να μετρήσουμε και από το τέλος της array. Τότε όμως η αρίθμηση ξεκινάει από το –1 (αφού δεν υπάρχει –0) οπότε το $content[-1] θα έχει την τιμή 15, το $content[-2] θα έχει την τιμή 8 και το $content[-3] θα έχει την τιμή 12.

$content[0] = 7;

Με τον τρόπο αυτό αλλάζουμε μια τιμή της λίστας (array). Στην προκειμένη περίπτωση το 8 γίνεται 7.

my @new_content = @content[2,0];

Έτσι δημιουργούμε μια καινούρια array η οποία θα περιέχει τιμές από την ήδη υπάρχουσα. Το 15 που θα είναι πρώτο και το 7 που θα είναι δεύτερο (θυμηθείτε ότι το 12 άλλαξε παραπάνω και έγινε 7).

unshift (@content, 22);

Έτσι προσθέτουμε μια τιμή στην αρχή κάποιας array. Τώρα η @content περιέχει τα:

22, 7, 8, 15

unshift (@content, ('giorgos',25,36));

Μπορούμε να προσθέσουμε πολλά τιμές ταυτόχρονα και όλες θα τοποθετηθούν στην αρχή της array. Τώρα η @content περιέχει τα:

giorgos, 25, 36, 22, 7, 8, 15

push (@content, 1453);

Νέες τιμές μπορούν να προστεθούν και στο τέλος της array, χάρη στην push. Τώρα η @content περιέχει τα:

giorgos, 25, 36, 22, 7, 8, 15, 1453

Φυσικά και εδώ μπορούν να προστεθούν πολλές τιμές μαζί:

push (@content, (330, 1204));

Τώρα η @content περιέχει τα:

giorgos, 25, 36, 22, 7, 8, 15, 1453, 330, 1204

Προσέξτε ότι τα περιεχόμενα της array @content εμφανίζονται με αυτόν τον τρόπο (χωρισμένα με κενά και κόμματα) μόνο αν η εντολή εκτύπωσης περιέχει την @content μέσα σε εισαγωγικά. (π.χ. print "@content\n";). Αν εμείς δηλώσουμε:

print @content;

Τότε το αποτέλεσμα θα είναι:

giorgos253622781514533301204

Απλός είναι και ο τρόπος με τον οποίο μπορούμε να αφαιρέσουμε μια τιμή από την αρχή ή το τέλος μιας array.

Με το:

my $arxi = shift(@content);

Η scalar μεταβλητή $arxi αποκτά την τιμή giorgos και τα περιεχόμενα της array (@content είναι πλέον:

25, 36, 22, 7, 8, 15, 1453, 330, 1204

Αντίστοιχα, αν θέλουμε να πάρουμε την πρώτη τιμή από το τέλος δηλώνουμε:

my $telos = pop(@content);

Όπου $telos = 1204 και τα περιεχόμενα της array (@content είναι πλέον:

25, 36, 22, 7, 8, 15, 1453, 330, 1204

Θα τελειώσουμε με μερικά μικροπράγματα:

my @a = (12, 24, 48);

my $b = @a;

$b = 3 (η scalar μεταβλητή μας δίνει τον αριθμό των περιεχομένων της λίστας)

Το ίδιο περίπου αποτέλεσμα επιτυγχάνουμε και με τον τελεστή $# που μας δίνει τη θέση του τελευταίου στοιχείου της λίστας (στο παράδειγμα που ακολουθεί του three).

my @test = ('one', 'two', 'three');

my $last_element_number = $#test;

Εδώ το $last_element_number θα έχει την τιμή 2.

my @c = (153);

Λίστα με μια τιμή (153)

my @d = 12;

Λίστα με μια τιμή (12)

my @e = (128, 256, @a);

Λίστα με τις τιμές 128, 256, 12, 24, 48

my @f = (12 .. 25);

Λίστα με τις τιμές 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25

Όλες οι παραπάνω λειτουργίες δοκιμάστηκαν στο ακόλουθο παράδειγμα:

#!/usr/bin/perl -w

use strict;

my @content = (12, 8, 15);

my $deytero = $content[1];

print "$deytero\n";

my $trito = $content[-1];

print "$trito\n";

$content[0] = 7;

print "@content\n";

my @new_content = @content[2,0];

print "@new_content\n";

unshift (@content, 22);

print "@content\n";

unshift (@content, ('giorgos',25,36));

print "@content\n";

push (@content, 1453);

print "@content\n";

push (@content, (330, 1204));

print "@content\n";

print @content;

my $arxi = shift(@content);

print "$arxi\n";

print "@content\n";

my $telos = pop(@content);

print "$telos\n";

print "@content\n";

my @a = (12, 24, 48);

my $b = @a;

print "$b\n";

my @c = (153);

print "@c\n";

my @d = 12;

print "@d\n";

my @e = (128, 256, @a);

print "@e\n";

my @f = (12 .. 25);

print "@f\n";

Επιστροφή στην Κορυφή

Defined – Undefined scalars και arrays

Μια μεταβλητή μπορεί να είναι defined (ορισμένη) ή undefined (αόριστη). Όταν δηλώνουμε my $input αλλά δεν έχουμε δώσει καμία τιμή στην $input τότε αυτή είναι undefined. Αν λοιπόν δώσουμε print "$input\n" θα τυπωθεί απλώς ένα κενό.

Μερικές φορές είναι χρήσιμο να γνωρίζουμε αν η μεταβλητή μας είναι defined ή undefined πράγμα που επιτυγχάνεται με τη συνάρτηση defined.

my $input;

if (defined $input)

{

κάνε κάτι (π.χ.. print "\$input is defined\n")

}

Αν η $input είναι defined (δηλαδή έχει κάποια τιμή), τότε θα εκτελεστεί το περιεχόμενο των αγκίστρων.

if (!defined $input)

{

print "\$input is undefined\n";

}

Αν η $input δεν είναι defined (είναι δηλαδή undefined και δεν έχει κάποια τιμή), τότε θα εκτελεστεί το περιεχόμενο των αγκίστρων. (Υπενθυμίζουμε ότι το ! είναι λογικός τελεστής και σημαίνει not)

Ας δούμε τώρα την ακόλουθη περίπτωση:

my @my_array = ('a', 'b', 'c');

$my_array[9] = 'd';

print "@my_array\n";

Το αποτέλεσμα που θα λάβουμε από την εκτύπωση είναι:

Use of uninitialized value in join or string at now.pl line 5.

Use of uninitialized value in join or string at now.pl line 5.

Use of uninitialized value in join or string at now.pl line 5.

Use of uninitialized value in join or string at now.pl line 5.

Use of uninitialized value in join or string at now.pl line 5.

Use of uninitialized value in join or string at now.pl line 5.

a b c d

Η Perl μεγάλωσε την @my_array και τοποθέτησε την τιμή d στη δέκατη θέση (μην ξεχνάτε ότι στις arrays αρχίζουμε να μετράμε από το 0). Μεταξύ όμως του c (της τρίτης τιμής) και του d δεν υπήρχε τίποτε. Έτσι η Perl τοποθέτησε 6 undefined τιμές που όμως δεν κατάφερε να τυπώσει όταν χρειάστηκε να ενώσει ολόκληρη την @my_array σε ένα string για να την τυπώσει. (Γενικά, όταν συναντάτε το μήνυμα λάθους uninitialized value σημαίνει ότι ζητήσατε από την Perl να διαχειριστεί κάτι που δεν υπάρχει π.χ. της λέτε να βρει κάτι άλλο από αυτό που υπάρχει.)

Ένα μικρό πρόγραμμα θα μας δείξει ποιες τιμές της array που μας ενδιαφέρει είναι defined και ποιες όχι:

my @my_array = ('a', 'b', 'c');

$my_array[9] = 'd';

my $count = 0;

Φτιάχνουμε έναν counter και τον ξεκινούμε από την τιμή 0.

my $quit = 5;

Θα χρησιμοποιήσουμε μια while και δίνουμε την τιμή για την οποία αυτή θα πρέπει να εκτελείται (θα μπορούσα να βάλω $quit = 1 όπως και στα προηγούμενα παραδείγματα, αλλά δεν θέλω να συνηθίσετε και να νομίζετε ότι αυτό είναι υποχρεωτικό).

my $length = @my_array;

Ο αριθμός των περιεχομένων της @my_array αποθηκεύεται στη $length. (Εδώ ο αριθμός αυτός είναι 10, δηλαδή υπάρχουν 10 τιμές μέσα στην @my_array.)

while ($quit == 5)

{

if (defined $my_array[$count])

{

print "Position \[$count\] is defined\n";

}

Φανταστείτε ότι είστε η Perl και τρέχετε για πρώτη φορά αυτή τη while. Θα ελέγξετε το περιεχόμενο της $my_array[0] και θα τυπώσετε την πληροφορία ότι είναι defined αν αυτό όντως ισχύει.

else

{

print "Position \[$count\] is undefined\n";

}

Αν η $my_array[0] είναι undefined θα τυπώσετε αυτή την πληροφορία.

$count++;

Αυξάνουμε την $count κατά 1. (Αν βρισκόμαστε στο πρώτο πέρασμα τότε η τιμή της από 0 γίνεται 1.)

if ($count == $length)

{

$quit = 4;

}

Αν η $count είναι 1 τότε το if δεν ισχύει και ξαναρχίζουμε από την αρχή ελέγχοντας αν είναι defined η $my_array[1].

Όταν η $count γίνει 10 (το μέγεθος της $length) αυτό σημαίνει ότι έχουμε ήδη ελέγξει τα [0], [1] ,[2] ,[3] ,[4] ,[5] ,[6] ,[7] ,[8] , [9], δηλαδή ολόκληρο το περιεχόμενο της @my_array. Το loop λοιπόν μπορεί να τερματιστεί.

}

Η εκτύπωση του προγράμματος αυτού θα μας δώσει:

Position [0] is defined

Position [1] is defined

Position [2] is defined

Position [3] is undefined

Position [4] is undefined

Position [5] is undefined

Position [6] is undefined

Position [7] is undefined

Position [8] is undefined

Position [9] is defined

Επιστροφή στην Κορυφή

Η λειτουργία foreach

Στον κώδικα που ακολουθεί επεκτείνουμε ένα παλαιότερο παράδειγμά μας έτσι ώστε το πρόγραμμα να ψάχνει για γραμμές που περιέχουν τη λέξη «αποδοτικά» σε περισσότερα από ένα αρχεία:

#!/usr/bin/perl -w

use strict;

my $file_directory = '/home/gepiti/temp';

Ξεκινούμε δηλώνοντας στην Perl το directory μέσα στο οποίο βρίσκονται τα αρχεία μας.

my @list_of_files = ('whatnew.htm', 'whatnew2.htm');

Στην συνέχεια δηλώνουμε την array μεταβλητή (τη @list_of_files) και τα αρχεία που περιέχει. (Στην περίπτωσή μας αυτά είναι μόνο δύο.)

foreach my $filename (@list_of_files) {

Στα αγγλικά for each σημαίνει «για καθένα». Για την Perl η παραπάνω γραμμή κώδικα ότι κάποια εργασία (αυτή που περιέχεται μέσα στην αγκύλη που ακολουθεί) πρέπει να εκτελεστεί για κάθε ένα από τα αρχεία που περιέχει η @list_of_files.

Προσέξτε τη σύνταξη η οποία είναι:

(foreach) (δήλωση scalar variable) (array variable) {κάνε αυτό}

Αυτό που λέμε στην Perl είναι ότι τις τιμές της σειράς @list_of_files πρέπει να τις διαχειριστεί μια μια. Έτσι, το πρώτο πράγμα δηλώνουμε μετά την foreach είναι τη μεταβλητή (εδώ την $filename) η οποία θα πάρει μια μια όλες τις τιμές της σειράς (@list_of_files) και θα κάνει κάτι (ό,τι ορίσουμε στα άγκιστρα).

open (FILE, "$file_directory/$filename") or die "can't open $filename $!";

Στο προηγούμενο παράδειγμα είχαμε δώσει απλώς το όνομα του αρχείου μια και αυτό βρισκόταν στον ίδιο φάκελο με το πρόγραμμά μας. Αυτό όμως σπάνια συμβαίνει στην πράξη. Εδώ δηλώνουμε το πλήρες path "$file_directory/$filename" και φυσικά στην πρόβλεψη για debugging (εύρεση και διόρθωση σφαλμάτων) δηλώνουμε “can't open $filename $!"; Έτσι ώστε μάθουμε αμέσως ποιο είναι το αρχείο που παρουσιάζει πρόβλημα.

while (<FILE>) {

if (m/αποδοτικά/) {

κατά τα γνωστά

print "$filename:\n$_";

Η μικρή προσθήκη που κάναμε εδώ σε σχέση με το προηγούμενο παράδειγμα είναι ότι το πρόγραμμα θα μας εμφανίσει όχι μόνο τη γραμμή στην οποία βρέθηκε η λέξη αποδοτικά, αλλά και το όνομα του αρχείου, πράγμα απαραίτητο αφού εδώ έχουμε περισσότερα από ένα αρχεία.

}

}

close FILE;

}

κατά τα γνωστά.

Ο πλήρης κώδικας του προγράμματος είναι:

#!/usr/bin/perl -w

use strict;

my $file_directory = '/home/gepiti/temp';

my @list_of_files = ('whatnew.htm', 'whatnew2.htm');

foreach my $filename (@list_of_files) {

open (FILE, "$file_directory/$filename") or die "can't open $filename $!";

while (<FILE>) {

if (m/αποδοτικά/) {

print "$filename:\n$_";

}

}

close FILE;

}

Επιστροφή στην Κορυφή

Ένα προσωπικό σχόλιο πριν προχωρήσετε παρακάτω

Η Perl είναι μια εξαιρετικά πλούσια και ισχυρή γλώσσα. Μέχρι εδώ σας παρουσίασα τα πιο βασικά χαρακτηριστικά της τα οποία (με μικρές διαφορές, ανάλογα με τις επιλογές του συγγραφέα) θα βρείτε σε οποιοδήποτε καλό βιβλίο Perl. Από δω και στο εξής όμως τα πράγματα δυσκολεύουν.

Τα κείμενα που ακολουθούν περιέχουν πολλά χρήσιμα παραδείγματα και γνώσεις, αλλά δεν αποτελούν μια πλήρη ή ισόρροπη παρουσίαση της Perl. Μεγάλα κεφάλαια της γλώσσας απουσιάζουν τελείως ή καλύπτονται πολύ λίγο μια και εμένα προσωπικά δεν με ενδιέφεραν και ασχολήθηκα ελάχιστα μαζί τους.

Παρόλο λοιπόν, που όσο περνά ο καιρός εμπλουτίζω το περιεχόμενο αυτών των μαθημάτων και πιστεύω ότι θα μάθετε πολλά χρήσιμα πράγματα μελετώντας τα, σάς συνιστώ ανεπιφύλακτα να προμηθευτείτε κάποιο βιβλίο για Perl και να επεκτείνετε τις γνώσεις σας, διαβάζοντάς το. Διαφορετικά είστε καταδικασμένοι να μην γίνετε ποτέ σοφότεροι από μένα και επειδή θεωρώ τον εαυτό μου πολύ μέτριο προγραμματιστή είμαι βέβαιος ότι αξίζετε κάτι καλύτερο.

Φιλικά

Γιώργος Επιτήδειος

Προχωρημένα χαρακτηριστικά της Perl

Επιστροφή στην Κορυφή

Εύρεση και διαχείριση αρχείων

Στο παράδειγμα αναζήτησης σε πολλαπλά αρχεία υποχρεωθήκαμε να δηλώσουμε στην Perl τα ονόματα των αρχείων στα οποία επιθυμούσαμε να γίνει η αναζήτηση. Τι συμβαίνει όμως αν δεν γνωρίζαμε τα ονόματά τους (ή αν αλλάζουν συχνά και δεν θέλουμε να μπαίνουμε στον κώδικα και να πληκτρολογούμε τα ονόματα από την αρχή); Σε αυτή την περίπτωση χρειαζόμαστε μια διαδικασία για να βρίσκουμε αυτά τα ονόματα.

Η απλούστερη περίπτωση θα ήταν να θέλουμε η Perl να εκτελέσει αναζητήσεις σε όλα τα αρχεία. Θέλουμε λοιπόν να μάθουμε ποια είναι τα ονόματα των αρχείων που περιέχονται σε directory (φάκελο) Χ. Στο UNIX αυτό γίνεται με την εντολή ls και για να πούμε στην Perl να κάνει το ίδιο πρέπει να τοποθετήσουμε την ls μέσα στα λεγόμενα backtics (``). Έτσι ο κώδικάς μας από

my @list_of_files = ('whatnew.htm', 'whatnew2.htm');

γίνεται

my @list_of_files = `ls /home/gepiti/temp`;

ή αν θέλουμε να χρησιμοποιήσουμε μια μεταβλητή αντί για το όνομα του directory

my @list_of_files = `ls /$file_directory`;

(Υπενθυμίζεται ότι έχουμε ήδη ορίσει πως my $file_directory = '/home/gepiti/temp')

Τώρα τα ονόματα των αρχείων μπαίνουν αυτόματα στο array και το μόνο που πρέπει να φροντίζουμε είναι τα αρχεία μας (όσα κι αν είναι) να βρίσκονται στο σωστό directory.

Στην περίπτωση όμως που δεν θέλουμε να διαχειριστούμε όλα τα αρχεία ενός directory, αλλά μόνο ορισμένα από αυτά τα πράγματα γίνονται πιο περίπλοκα. Στο παραπάνω παράδειγμα μας ενδιαφέρουν μόνο τα αρχεία htm και για να το πετύχουμε αυτό θα χρησιμοποιήσουμε τις opendir, readdir grep closedir. Σε μια τέτοια περίπτωση ο κώδικάς μας γίνεται:

opendir (DIR, "$file_directory") || die "can't : $!";

my @list_of_files = grep /\.htm$/, readdir DIR;

closedir DIR;

Η opendir λειτουργεί όπως και η open με τη διαφορά ότι αντί για αρχείο (FILE) εδώ ανοίγουμε directory(DIR). Προσέξτε ότι στην πρόβλεψή μας για debugging (το die "can't : $!) δεν χρησιμοποιήσαμε τον τελεστή or αλλά το ισοδύναμό του ||. Αυτό έγινε για λόγους παρουσίασης του || και μόνο. Με το or θα λειτουργούσε το ίδιο καλά. (Τα and, or, not μπορούν να αντικατασταθούν από τα &&, || και ! αντίστοιχα.)

Στη δεύτερη γραμμή δηλώνουμε στην Perl ότι επιθυμούμε να κάνουμε match (να ταιριάξουμε) μόνο όσα αρχεία τελειώνουν σε .htm. Η grep () είναι μια συνάρτηση που μας επιτρέπει να φιλτράρουμε δεδομένα. Η σύνταξη που χρησιμοποιούμε εδώ είναι grep EXPR, LIST δηλαδή grep έκφραση (EXPR), περιοχή δεδομένων (LIST).

Όπως αναφέραμε αλλού η σύνταξη για ταίριασμα στοιχείων είναι /pattern/ δηλαδή /ό,τι_θέλουμε_να_ταιριάξει/ (το m από το m// που αναφέραμε σε άλλο σημείο εννοείται και μπορεί να απαλειφθεί) και το $ στο τέλος σημαίνει ότι θέλουμε να ταιριάξει το τέλος κάθε ακολουθίας χαρακτήρων (string). Η τελεία όμως είναι ειδικός χαρακτήρας στις εργασίες ταιριάσματος (matching) γι’ αυτό και για να μην μπερδεύεται το πρόγραμμα όταν θέλουμε να του δηλώσουμε ότι αυτή η . πρέπει να την διαχειριστεί ως τελεία και όχι κάτι άλλο βάζουμε μπροστά της την backslash. Έτσι, αντί να γράψουμε /.htm/ όπως θα ήταν και το φυσιολογικό γράφουμε /\.htm$/.

Για να γίνει καλύτερα κατανοητό αυτό ας φανταστούμε ότι θέλουμε να βρούμε σε κάποιο κείμενο την ακολουθία home/gepiti. Αυτό θα γραφτεί /home\/gepiti/ διαφορετικά η Perl θα νομίσει ότι ψάχνουμε για το /home/ και θα μας βγάλει μήνυμα λάθους γιατί δεν καταλαβαίνει τι θέλουμε να της πούμε με το gepiti.

Τέλος με την τρίτη γραμμή κλείνουμε το directory.

Επιστροφή στην Κορυφή

Regular Expressions

Οι δυνατότητες αναγνώρισης και διαχείρισης χαρακτήρων της Perl είναι ανώτερες, και ευκολότερες στη χρήση, από κάθε άλλης γλώσσας προγραμματισμού. Ωστόσο, οι πολλές επιλογές πάντα περιπλέκουν την εργασία του προγραμματιστή και γι’ αυτό οι ερωτήσεις σχετικά με pattern matching και substitution είναι πάρα πολύ συχνές στο comp.lang.perl usenet newsgroup.

Η συνηθέστερη μορφή pattern matching εμφανίζεται σε if statements όπως:

if ($something =~ m/giorgos/) { κάνε κάτι }

Το =~ σημαίνει «περιέχει ή υπάρχει» και αναφέρεται σε ό,τι βρίσκεται ανάμεσα στις //. Δηλαδή λέμε στην Perl πως αν η μεταβλητή ($something περιέχει την ακολουθία χαρακτήρων giorgos τότε θα πρέπει να εκτελεστεί το περιεχόμενο των {}.

Αν θέλαμε η $something να μην περιέχει την ακολουθία giorgos τότε αντί για =~ θα γράφαμε !~.

Όπως γνωρίζουμε και από την print υπάρχουν μερικοί χαρακτήρες (metacharacters) που η Perl τους χρησιμοποιεί για άλλες εργασίες και γι’ αυτό πρέπει να τους δηλώσουμε με ειδικό τρόπο αν τους επιθυμούμε αυτούσιους. Για παράδειγμα για να τυπώσουμε το ποσό «$35» θα γράψουμε print “\$35\n”; διότι διαφορετικά η Perl θα θεωρήσει πως το $35 αποτελεί όνομα μεταβλητής, ενώ χάρη στη \ θα καταλάβει ότι πρέπει να τυπώσει απλώς το σήμα του δολαρίου.

Ειδικοί χαρακτήρες υπάρχουν και στις regular expressions. Όποτε πρέπει να αναζητήσετε με ένα από τα:

^ . ? { ( ) / [ $ + * \ |

θα πρέπει να χρησιμοποιήσετε πριν από αυτά τη γνωστή μας backslash (\).

Αν για παράδειγμα επιθυμούμε να βρούμε το (35$) αυτό θα γραφτεί:

m/\(35\$\)/

Σημειώστε ότι το m δεν είναι απαραίτητο και οι // μπορούν να πάρουν και άλλη μορφή. Όταν η ακολουθία χαρακτήρων που δηλώνουμε βρίσκεται μέσα σε // τότε το m μπορεί να παραληφθεί. Είναι όμως υποχρεωτικό όταν αντί για // χρησιμοποιούμε κάποιον άλλο ειδικό χαρακτήρα. Οι παρακάτω εκφράσεις λοιπόν είναι ισοδύναμες:

m/giorgos/ /giorgos/ m|giorgos| m!giorgos! m%giorgos% κ.λπ.

Φυσικά στην περίπτωση που χρησιμοποιούμε κάποιον άλλο χαρακτήρα θα πρέπει να τον δηλώνουμε με ειδικό τρόπο αν αποτελεί και μέρος της αναζητούμενης ακολουθίας. Για παράδειγμα, αν αναζητούμε το «Η αύξηση των πωλήσεων έφτασε το 35% τον Ιανουάριο», αυτό θα γραφτεί:

m%Η αύξηση των πωλήσεων έφτασε το 35\% τον Ιανουάριο%

Πάντως καλύτερα να προτιμήσετε τις // εκτός αν έχετε κάποιο ειδικό λόγο (π.χ. υπάρχουν πολλές / μέσα στο pattern και βαριέστε να τις δηλώνετε με ειδικό τρόπο δηλαδή με \/).

Εκτός από απλές ακολουθίες χαρακτήρων (strings) η Perl μπορεί να διαχειριστεί και πιο περίπλοκες εκφράσεις όπως:

Χαρακτήρας

Ιδιότητα

\w

Οποιοδήποτε γράμμα, αριθμός ή ο χαρακτήρας underscore

(το _), δηλαδή οτιδήποτε μπορεί να χρησιμοποιηθεί ως μέρος του ονόματος μιας μεταβλητής στην Perl.

\W

Οτιδήποτε εκτός από γράμμα, αριθμό ή underscore (ο χαρακτήρας αυτός αποτελεί το αντίθετο του \w)

\d

Οποιοδήποτε ψηφίο (0,1,2,3,4,5,6,7,8,9)

\D

Οτιδήποτε εκτός από ψηφίο (ο χαρακτήρας αυτός αποτελεί το αντίθετο του \d)

\s

Οποιοδήποτε σύμβολο κενού και συναφών στοιχείων (\n\t\f\r), δηλαδή κενό διάστημα, αλλαγή γραμμής (\n), tab (\t), formfeed (\f), carriage return (\r).

\S

Οτιδήποτε εκτός από σύμβολο κενού (ο χαρακτήρας αυτός αποτελεί το αντίθετο του \s)

\b

Λεκτικό σύνορο (word boundary)

\B

Οτιδήποτε εκτός από λεκτικό σύνορο

^

Αρχή string

$

Τέλος string

|

ή διαζευκτικό (ο λογικός χαρακτήρας εναλλαγής)

[]

Εναλλακτική χρήση του περιεχομένου (character class)

.

Οποιοσδήποτε χαρακτήρας

?

Μια ή καμία εμφάνιση του προηγούμενου χαρακτήρα

*

Καμία, μια ή περισσότερες εμφανίσεις του προηγούμενου χαρακτήρα

+

Μια ή περισσότερες εμφανίσεις του προηγούμενου χαρακτήρα

{}

Αριθμός επαναλήψεων ενός χαρακτήρα

Οι κυριότεροι τρόποι σύναξης pattern matching της Perl είναι οι ακόλουθοι:

Παράδειγμα

Αναζήτηση για

Σχόλια

/gepiti/

gepiti

Ό,τι ζητήθηκε.

/gepiti/i

Gepiti, gepiti, GEPITI, gePIti κ.λπ.

Το i σημαίνει case insensitive. Θα βρεθούν λοιπόν τόσο κεφαλαία όσο και πεζά.

/gepiti/g

gepiti παντού

Η αναζήτηση συνεχίζεται και μετά την εύρεση του πρώτου αποτελέσματος (θα εξηγηθεί παρακάτω με την while).

/\bδημο/

δημοκρατία, δημογέροντας κ.λπ.

Λόγω το \b θα αναζητηθούν μόνο strings που ξεκινούν μια λέξη

/\bτρία\b/

Η λέξη τρία

Δεν θα βρεθούν τα αλλοτρία, τρίαινα, πατρίας μια και βρίσκονται (ολόκληρα ή κατά ένα μέρος) μέσα σε λέξεις

/\Bτρία\B/

Λέξεις όπως το πατρίας

Δεν πρέπει να υπάρχει λεκτικό σύνορο πριν ή μετά το τρία. Θα βρεθούν λοιπόν μόνο strings όπου το τρία δεν αποτελεί αρχή ή τέλος λέξης.

/^Αλ/

Αλλά, Αλλιώς κ.λπ. στην αρχή του προς αναζήτηση περιεχομένου

Αν $something =~ /^Αλ/ τότε η αναζήτηση θα είναι επιτυχής μόνο αν το $something ξεκινάει από Αλ.

$something = ‘Αλλιώς τα περιμέναμε’ (επιτυχής αναζήτηση) $something = ‘Τίποτα. Αλλιώς πρέπει να γίνει’ (αποτυχία αφού το $something ξεκινάει με το «Τίποτα»).

/ος$/

Γιώργος. Νίκος κ.λπ. στο τέλος του προς αναζήτηση περιεχομένου

Αν $something =~ /ος$/ τότε η αναζήτηση θα είναι επιτυχής μόνο αν το $something τελειώνει σε ος (ή αν αναζητούμε στο $_ τότε θα πρέπει να τελειώνει σε ος ακολουθούμενο αμέσως μετά από αλλαγή γραμμής).

$something = ‘/Το όνομά μου είναι Νίκος’ (επιτυχής αναζήτηση) $something = ‘Το όνομά μου είναι Γιώργος.’ (αποτυχία αφού το $something τελειώνει με τελεία).

/^/

Κάθε γραμμή που έχει αρχή

Όλες οι γραμμές έχουν αρχή, συνεπώς κάθε γραμμή (π.χ. αν διαβάσουμε γραμμή γραμμή ένα αρχείο με τη while και την $_).

/^5$/

5

Μια γραμμή που περιέχει μόνο τον χαρακτήρα 5.

/^$/

’δεια γραμμή

Μια γραμμή που περιέχει μόνο την αρχή και το τέλος της χωρίς τίποτε ενδιάμεσα.

/Νίκος| Γιώργος/

Νίκος ή Γιώργος

Π.χ. $something =~ /Νίκος|Γιώργος/

/(Νίκ|Γιώργ)ος/

Νίκος ή Γιώργος

Εδώ εναλλάσσεται το περιεχόμενο των παρενθέσεων αντί για ολόκληρες τις λέξεις.

/τ[οαη]/

το, τα, τη, τον, ταραμάς, αυτοκίνητο κ.λπ.

Οτιδήποτε περιέχει τ ακολουθούμενο από ο ή α ή η.

/58[$?]/

58$ ή 58? ή 58$$$ κ.λπ.

Μέσα στις character classes οι ειδικοί χαρακτήρες δεν χρειάζονται escaping (προσθήκη του \ για την αποφυγή «κακής κατανόησης» από την Perl) με εξαίρεση τα ], - και το ^ αν βρίσκεται πρώτο.

/[^78]/

Οτιδήποτε εκτός από 7 ή 8.

Η ^ στην αρχή της character class δηλώνει άρνηση. H $something =~ /[^78]/ είναι αληθής όταν δεν περιέχεται πουθενά το 7 ή το 8. (Πρέπει όμως να υπάρχει κάτι άλλο. Αν η $something είναι κενή δεν έχουμε ταίριασμα.)

/[a-z]1/

a1, b1, c12 κ.λπ.

Η – δηλώνει εύρος τιμών. Εδώ από το a έως και το z.

/[0-9]a/

0a, 5ab κ.λπ.

Εδώ το εύρος τιμών είναι από 0 έως και 9.

/[0-9a-z]/

0, 1, 2, c, d, κ.λπ.

Εδώ το εύρος τιμών είναι από 0 έως και 9 και από a έως και z.

/25.00/

25100, 25a00, 25*00, 25,00 κ.λπ.

Η τελεία αντιπροσωπεύει οποιονδήποτε χαρακτήρα. Αν θέλαμε να βρούμε το 25.00 θα έπρεπε να το γράψουμε /25\.00/

/^.$/

Οτιδήποτε αλλά μόνο ένα

Μια γραμμή που μπορεί να περιέχει οποιονδήποτε χαρακτήρα, αλλά μόνον έναν.

/σ?ταύρος/

σταύρος, ταύρος, παπασταύρος, μικροταύρος κ.λπ.

Το σ μπορεί να υπάρχει μια φορά ή καθόλου. Αν είχαμε /στ?αύρος/ θα ταίριαζαν τα σαύρος, σταύρος, αλλά όχι το στταύρος γιατί πρέπει να έχουμε μόνο ένα ή κανένα τα μετά το σ.

/ύαινα(κι)?/

ύαινα, υαινάκι κ.λπ.

Το κείμενο στο οποίο αναφέρεται το ? μπορεί να μπει και μέσα σε παρένθεση.

/αβ*γ/

αγ, αβγ, αββγ, αβββγ, αββββββγ, κ.λπ

Το β μπορεί να επαναληφθεί άπειρες φορές. Μπορεί επίσης να μην υπάρχει καθόλου.

/αβ+γ/

αβγ, αββγ, αβββγ, αββββββγ, κ.λπ

Το β μπορεί να επαναληφθεί άπειρες φορές. Θα πρέπει όμως να υπάρχει τουλάχιστον μια (δεν γίνεται δεκτό το αγ).

/\d{1,5}\$/

1$, 15$, 333$, 4896$, 15987$ κ.λπ.

Ένα έως πέντε ψηφία (δηλαδή 0,1,2,3,4,5,6,7,8,9) ακολουθούμενα από το $.

/\d{5}\$/

12345$, 98765$, 01234$ κ.λπ.

Πέντε ψηφία (όχι περισσότερα ή λιγότερα) ακολουθούμενα από το $.

/μαμα{3,} μου/

μαμααα μου, μαμαααααα μου κ.λπ.

Ο προηγούμενος χαρακτήρας μπορεί να επαναλαμβάνεται 3 ή περισσότερες (οσεσδήποτε) φορές. Το «μαμαα μου» όμως δεν θα ταιριάξει (έχει μόνο δύο α μετά το μαμ).

 

 

 

Επιστροφή στην Κορυφή

Χρήση Regular Expressions αντί κλασικών προγραμματιστικών εργασιών

Οι Regualr Expressions είναι πολύ χρήσιμες όταν θέλετε να ελέγξετε τα δεδομένα που πληκτρολογεί ο χρήστης ή να επιθεωρήσετε τα περιεχόμενα ενός αρχείου κειμένου. Μπορούν όμως να αντικαταστήσουν ακόμη και «παραδοσιακές» προγραμματιστικές ενέργειες, διευκολύνοντας το έργο σας. Στο ακόλουθο παράδειγμα ο χρήστης μας δίνει έναν αριθμό και εμείς του απαντούμε αν ήταν θετικός ή αρνητικός.

#!/usr/bin/perl -w

use strict;

print " Kalosirthate \n";

Καλωσορίζουμε τον χρήστη.

my $quit = 1;

while ($quit == 1)

Όσο η $quit έχει την τιμή 1 θα επαναλαμβάνεται συνεχώς ο κώδικας που ακολουθεί.

{

print "Dose to numero \n";

my $number = <STDIN> ;

chomp ($number);

Ο χρήστης πληκτρολογεί ένα νούμερο.

if ($number =~ /\D/)

{

print "Thelo thetiko arithmo\n";

}

Αν πληκτρολογήσει οτιδήποτε εκτός από 0123456789 του ζητούμε ένα θετικό αριθμό. (Αν η εφαρμογή μας δεχθεί αρνητικό νούμερο, π.χ. –78 τότε το \D θα αναγνωρίσει το πρόσημο – και θα τυπώσει το Thelo thetiko arithmo).

elsif ($number =~ /(0*)0$/ )

{

print " --------- THELO THETIKO ARITHMO ------------\n";

}

Αν πληκτρολογήσει ένα ή περισσότερα μηδενικά του ζητούμε ένα θετικό αριθμό. Το 0$ σημαίνει ότι το τελευταίο ψηφίο είναι 0, ενώ το 0* σημαίνει ότι μπροστά του μπορεί να υπάρχει κανένα, ένα ή περισσότερα μηδενικά. Η εφαρμογή λοιπόν θα διαμαρτυρηθεί αν δει 0, 00, 000000 κ.λπ.

Με τους παραπάνω ελέγχους έχουμε εξασφαλίσει ότι ο χρήστης θα πληκτρολογήσει ένα θετικό αριθμό (διαφορετικά η εφαρμογή θα ξαναρχίσει από την αρχή). Το μόνο που μας μένει τώρα είναι να βρούμε αν ο αριθμός είναι ζυγός (άρτιος) ή μονός (περιττός).

elsif ($number =~ /[02468]$/)

’ρτιοι είναι μόνο οι αριθμοί που τελειώνουν σε 02468. ’ρα ένα pattern matching από μια απλή regular expression αρκεί για να ανακαλύψουμε τη φύση των δεδομένων που πληκτρολόγησε ο χρήστης.

elsif ($number %2 == 0)

Εναλλακτικά (με πιο προγραμματιστικά κλασικό δηλαδή με μαθηματικό τρόπο) μπορούμε να χρησιμοποιήσουμε το modulus (%) που μας δίνει το υπόλοιπο μιας διαίρεσης (προσοχή, όχι το αποτέλεσμα αλλά το υπόλοιπο, αν θέλαμε το αποτέλεσμα θα γράφαμε $number / 2).

Γνωρίζουμε ότι όλοι οι άρτιοι αριθμοί διαιρούμενοι με το 2 μας δίνουν μηδέν. ’ρα το $number %2 θα πρέπει να είναι 0 (υπενθυμίζουμε ότι το ίσον στην Perl γράφεται με διπλό ίσον, καθώς το μονό χρησιμοποιείται για να δώσει τιμή σε μια μεταβλητή).

{

print " --------- ZIGOS ------------\n";

$quit =2;

}

Εκτυπώνουμε το αποτέλεσμα στον χρήστη.

else

{

print " --------- MONOS ------------\n";

$quit=2;

}

Αν ο αριθμός δεν είναι ζυγός τότε υποχρεωτικά θα είναι μονό. Εκτυπώνουμε λοιπόν το αποτέλεσμα στον χρήστη.

}

Κλείνουμε τη while και έχουμε ολοκληρώσει την εφαρμογή μας. Αν θέλετε μπορούμε να συμπληρώσουμε ακόμη και έναν αποχαιρετισμό.

print “Ευχαριστούμε που χρησιμοποιήσατε το πρόγραμμά μας\n”;

Επιστροφή στην Κορυφή

Split και Join (και λίγο substitution)

Θα πειραματιστούμε με τα δεδομένα του αρχείου data2.txt:

This is the first line of text.

And a second line of text.

---------

A third line of text.

Now a fourth line of text.

---------

The fifth line of text.

Finally, the sixth line of text.

Το πρόγραμμα που ακολουθεί κάνει διάφορες μετατροπές στο αρχείο χρησιμοποιώντας τις πολύ χρήσιμες εντολές Split και Join που μας βοηθούν να διαχειριστούμε τα περιεχόμενα μιας λίστας (array).

#!/usr/bin/perl -w

use strict;

Κατά τα γνωστά.

my $file ='data2.txt';

open (INFO, "<$file");

my @lines = <INFO>;

Ορίζουμε μια scalar μεταβλητή με τα στοιχεία του αρχείου, το ανοίγουμε δίνοντάς του το filehandle INFO και μετά μεταφέρουμε τα περιεχόμενα του INFO σε μια λίστα (array).

close (INFO);

Αφού κάναμε τη δουλειά που θέλαμε και πήραμε τα δεδομένα του data2.txt δεν υπάρχει λόγος να κρατάμε αυτή τη διαδικασία ανοικτή. Κλείνουμε λοιπόν το filehandle.

print "@lines\n";

Αυτό το κάναμε για να δούμε σε τι κατάσταση είναι τα δεδομένα μας τώρα που τα πήραμε από το αρχείο και τα βάλαμε μέσα σε μια array. Η εκτύπωση θα μας δώσει το ακόλουθο αποτέλεσμα:

This is the first line of text.

And a second line of text.

---------

A third line of text.

Now a fourth line of text.

---------

The fifth line of text.

Finally, the sixth line of text.

Πήραμε δηλαδή το ίδιο αποτέλεσμα με πριν. Η πηγή όμως είναι διαφορετική. Αντί για το περιεχόμενο του αρχείου, εδώ βλέπουμε μία μία όλες τις τιμές της array @lines.

my $long_line = join ("\n", @lines);

Η εντολή join παίρνει τα περιεχόμενα μιας array (εδώ της @lines) και τα ενώνει μεταξύ τους δημιουργούνται μια ενιαία σειρά χαρακτήρων (string) που αποθηκεύεται σε μια scalar (εδώ στην $long_line).

Για παράδειγμα αν δίναμε my $long_line = join ("*", @lines) αυτό θα σήμαινε:

Πάρε το περιεχόμενο της @lines και κόλλησε όλες τις τιμές μεταξύ τους χρησιμοποιώντας ως διαχωριστικό τον χαρακτήρα *. Έτσι το τελικό αποτέλεσμα θα ήταν:

This is the first line of text.*And a second line of text.*---------*A third line of text.*Now a fourth line of text.*---------*The fifth line of text.*Finally, the sixth line of text.

Στο τρέχον πρόγραμμα όμως κάναμε κάτι ιδιόμορφο. Είπαμε στην Perl ότι ο χαρακτήρας (ή ακολουθία χαρακτήρων) με την οποία θα «κολλήσει» τις τιμές είναι το \n δηλαδή η αλλαγή γραμμής. Έτσι, παρά το γεγονός ότι όλη η $long_line αποτελεί μια ακολουθία «κολλημένων» μεταξύ τους χαρακτήρων, η εμφάνισή της θα είναι:

print "$long_line\n";

This is the first line of text.

And a second line of text.

---------

A third line of text.

Now a fourth line of text.

---------

The fifth line of text.

Finally, the sixth line of text.

Παρατηρούμε λοιπόν ότι μπήκε μια καινούρια κενή γραμμή μεταξύ των γραμμών που περιείχαν κείμενο. Η εμφάνιση λοιπόν μπορεί να εξαπατά. Παρά τα κενά που υπάρχουν το παραπάνω υλικό αποτελεί μια και μοναδική ενιαία ακολουθία χαρακτήρων (απλώς ένας από αυτούς είναι ο \n).

my @dash_sep = split (/---------/, $long_line);

Αφού μάθαμε την join τώρα ήρθε η σειρά της split που κάνει ακριβώς το αντίθετο. Δηλαδή, παίρνει τα δεδομένα μιας scalar και τα χωρίζει στο προκαθορισμένο σημείο. Εδώ δηλαδή λέμε στην Perl να πάρει τη scalar $long_line και από αυτή να δημιουργήσει μια array (την @dash_sep). Το σημείο στο οποίο θα κοπεί η $long_line είναι οι 9 παύλες. Έτσι, η νέα array θα αποτελείται από τρεις τιμές.

print "@dash_sep\n";

This is the first line of text. And a second line of text.

(η πρώτη τιμή, δηλαδή η $dash_sep[0])

A third line of text. Now a fourth line of text.

(η δεύτερη τιμή, δηλαδή η $dash_sep[1])

The fifth line of text. Finally, the sixth line of text.

(η τρίτη τιμή, δηλαδή η $dash_sep[2])

Τώρα γνωρίζεται τις βασικές λειτουργίες των split και join. Επειδή όμως υποσχέθηκα στον Barry B. Floyd του οποίου το παράδειγμα χρησιμοποιώ εδώ, να δημοσιεύσω όλο τον κώδικά του θα συνεχίσουμε με μια μικρή εφαρμογή αντικατάστασης.

print "<HTML><HEAD><TITLE>Perl output</TITLE></HEAD>\n";

print "<BODY>\n";

Θέλουμε να δημιουργήσουμε μια Web σελίδα με περιεχόμενο κάπως διαφορετικό από εκείνο του data2.txt. Εδώ τυπώνουμε απλώς τον αρχικό κώδικα.

<HTML><HEAD><TITLE>Perl output</TITLE></HEAD>

<BODY>

foreach my $line (@dash_sep)

Για κάθε μεμονωμένη τιμή ($line) της @dash_sep

{

$line =~ s/line of text./text fragment. /g;

Δηλαδή, αντικατέστησε (s) το line of text (το περιεχόμενο της πρώτης //). με to text fragment. (το περιεχόμενο της δεύτερης //). Αυτή η αντικατάσταση πρέπει να γίνεται κάθε φορά που η Perl συναντά το περιεχόμενο της πρώτης // (g).

$line =~ s/\n//g;

Επειδή δεν μας αρέσουν τα πολλά κενά, λέμε την Perl να διαγράψει τις αλλαγές γραμμών, δηλαδή αντικαταστήσει την αλλαγή γραμμής (το \n) με το τίποτε (οι δύο // δεν περιέχουν τίποτε)

Το πρόγραμμα θα μπορούσε να τελειώσει εδώ. Εμείς όμως θέλουμε να κάνουμε κάτι ακόμη. Να τυπωθούν οι χαρακτήρες <HR><BR> πριν από τη δεύτερη τιμή ($dash_sep[1]) της @dash_sep.

if ($line =~ /^A third text/)

Έτσι δηλώνουμε ότι αν η αρχή (το σύμβολο ^ που στα αγγλικά ονομάζεται caret) της τιμής αποτελείται από την ακολουθία χαρακτήρων A third text

{

print " <HR><BR> \n";

}

τότε να τυπωθούν οι χαρακτήρες <HR><BR>

print " <P> $line </P> \n";

}

Να τυπωθεί το περιεχόμενο της $line μέσα στους χαρακτήρες <P> και </P>

print " </BODY>\n</HTML>\n";

Να τυπωθούν τα </BODY> και </HTML> με αλλαγή γραμμών μετά από αυτά.

Η τελική εκτύπωση λοιπόν θα είναι:

<HTML><HEAD><TITLE>Perl output</TITLE></HEAD>

<BODY>

<P>This is the first line of text. And a second line of text. </P>

<HR><BR>

<P>A third line of text. Now a fourth line of text. </P>

<P>The fifth line of text. Finally, the sixth line of text. </P>

</BODY>

</HTML>

Ολόκληρος ο κώδικας του παραπάνω προγράμματος είναι ο:

#!/usr/bin/perl -w

use strict;

my $file ='data2.txt';

open (INFO, "<$file");

my @lines = <INFO>;

close (INFO);

print "@lines\n";

my $long_line = join ("\n", @lines);

print "$long_line\n";

my @dash_sep = split (/---------/, $long_line);

print "@dash_sep\n";

print "<HTML><HEAD><TITLE>Perl output</TITLE></HEAD>\n";

print "<BODY>\n";

foreach my $line (@dash_sep)

{

$line =~ s/line of text./text fragment. /g;

print "@dash_sep\n";

$line =~ s/\n//g;

print "@dash_sep\n";

if ($line =~ /^A third text/)

{

print " <HR><BR> \n";

}

print " <P> $line </P> \n";

}

print " </BODY>\n</HTML>\n";

Επιστροφή στην Κορυφή

Greed (Τραβώντας URLs από Web σελίδες)

Αν και υπάρχει ειδικό module για τη λήψη όλων των URLs μιας σελίδας (το HTML::LinkExtor), μερικές φορές προτιμούμε απλούστερες λύσεις είτε διότι τα modules δεν είναι πάντα διαθέσιμα στο σύστημα όπου εργαζόμαστε, είτε επειδή αυτό που θέλουμε να επιτύχουμε είναι αρκετά απλό και δεν υπάρχει λόγος να σκοτώνουμε τα ποντίκια με το κανόνι (small and simple is beautiful).

Στον κώδικα που ακολουθεί όλη η Web σελίδα βρίσκεται στην $content και επιθυμούμε να πάρουμε όλα τα URLs που ξεκινούν από ό,τι περιέχει η παράμετρος $start_url (π.χ. http://www.eeei.gr/interbiz).

while ($content =~ m/['"]($start_url(.*?))['"]/sig)

Η Perl θα βρει, δηλαδή θα κάνει match (m) ό,τι περικλείεται από τον χαρακτήρα “ ή ‘ ['"], αρχίζει από $start_url και ακολουθείται από οτιδήποτε άλλο (.*?). Η αναζήτηση γίνεται γραμμή γραμμή (s) αν και αυτό δεν έχει σημασία αφού όλη η $content αποτελεί μια γραμμή, είναι case insensitive (i) και ισχύει για όλο το περιεχόμενο της $content, δηλαδή είναι global (g).

Για όσους έχουν απορία για το οτιδήποτε άλλο (.*?), αυτό μεταφράζεται ως ακολούθως:

. = οτιδήποτε (εκτός από αλλαγή γραμμής)

* = οσαδήποτε (από 0 έως όσο τύχει)

? = Σταμάτα στο πρώτο που θα βρεις

Για να καταλάβετε τη σημασία του ερωτηματικού δείτε την ακόλουθη περίπτωση:

<a href=http://www.eeei.gr/interbiz/articles/article1.htm”> ’ρθρο 1</a> μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα <a href=http://www.eeei.gr/interbiz/articles/article2.htm”> ’ρθρο 2</a>

Στο (.*)['"] δηλαδή ταίριαξε ό,τι μπορείς μέχρι το “ ή ‘ η Perl θα μας φέρει το:

http://www.eeei.gr/interbiz/articles/article1.htm”> ’ρθρο 1</a> μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα μπλα <a href=http://www.eeei.gr/interbiz/articles/article2.htm

Δηλαδή δεν θα σταματήσει στο πρώτο “ που θα συναντήσει, αλλά στο τελευταίο. Τότε λέμε ότι η αναζήτηση είναι greedy (αχόρταγη). Το ? της ζητάει να κάνει δίαιτα και να ικανοποιηθεί με το πρώτο “ που θα συναντήσει, οπότε το αποτέλεσμα θα είναι:

http://www.eeei.gr/interbiz/articles/article1.htm

{

my $basic_url_to_get = $1;

Παίρνουμε το περιεχόμενο του (.*?)

my @url_to_get = ($site_begin, $basic_url_to_get);

Στις περισσότερες web σελίδες τα URLs δεν είναι πλήρη. Για παράδειγμα, αν η σελίδα:

http://www.eeei.gr/interbiz/index.htm

παραπέμπει στο

http://www.eeei.gr/interbiz/articles/article1.htm

τότε ο κώδικας συνήθως θα είναι απλώς:

<a href=”articles/article1.htm”>’ρθρο 1</a>

Aν λοιπόν η $start_url ήταν articles/ τότε το $basic_url_to_get θα έχει τις τιμές:

articles/article1.htm

articles/article2.htm

articles/article7.htm

articles/newarticle.htm

και ό,τι άλλο υπάρχει στη σελίδα. Αν εμείς θέλουμε το πλήρες URL τότε θα πρέπει αυτό να υπάρχει ήδη καταχωρημένο κάπου στο πρόγραμμά μας. Για παράδειγμα:

my $site_begin = http://www.eeei.gr/interbiz/

Δημιουργούμε λοιπόν την array @url_to_get που περιέχει τα $site_begin και $basic_url_to_get.

my $complete_url_to_get = join ('', @url_to_get);

Με την join ενώνουμε αυτά τα δύο σε μια scalar με διαχωριστικό τίποτε. Έτσι τώρα η $complete_url_to_get θα έχει τιμές όπως:

http://www.eeei.gr/interbiz/ articles/article2.htm

Επιστροφή στην Κορυφή

Συγκρίσεις τιμών και ακολουθιών χαρακτήρων (strings)

Ο πίνακας που ακολουθεί περιέχει όλα όσα χρειάζεται να γνωρίζετε για τις συγκρίσεις μέσω της Perl:

Εργασία

Αριθμητική σύγκριση

Σύγκριση strings

equal

= =

eq

non equal

!=

ne

less than

<

lt

greater than

>

gt

less than or equal

< =

le

greater than or equal

> =

ge

Προσέξτε ότι:

  1. Η while ($foo = 5) {κώδικα που κάνει κάτι} θα τρέχει για πάντα. Η $foo θα είναι πάντα ίση με 5 επειδή η έκφραση $foo = 5 σημαίνει: «Η $foo να πάρει την τιμή 5». Αυτό που θα έπρεπε να γράψουμε είναι while ($foo = = 5), δηλαδή «όσο η $foo έχει τιμή 5».
  2. Η έκφραση $foo != $bar είναι σωστή αν οι $foo και $bar περιέχουν αριθμητικές τιμές. Αν όμως περιέχουν κείμενο (κάποια ακολουθία χαρακτήρων), θα πρέπει να δηλώσουμε $foo ne $bar.

Η Perl διαθέτει επίσης λογικούς τελεστές:

Σύνταξη C

Σύνταξη Perl

Ερμηνεία

&&

and

AND

||

or

OR

!

not

NOT

Η σύνταξη C είναι πιο ισχυρή από τη σύνταξη Perl. Έτσι η Perl θα αξιολογήσει πρώτα το && και μετά το and. Με τον τρόπο αυτό μπορείτε να γλιτώσετε μερικές παρενθέσεις αν χρειάζεστε μια πολύ περίπλοκη λογική διαδικασία. (Υπενθυμίζω ότι οι εκφράσεις μέσα σε παρένθεση αξιολογούνται πριν από της εκτός παρενθέσεων.)

Προσέξτε ότι αν και μπορούμε να δημιουργήσουμε πολύ μεγάλες και πολύπλοκες λογικές φράσεις, η Perl λειτουργεί πάντοτε όσο λιγότερο χρειάζεται για να τις ικανοποιήσει. Για παράδειγμα, αν δηλώσουμε:

$a || $b || $c || $d ||e και ισχύει το $a η Perl δεν θα ασχοληθεί καθόλου με τα υπόλοιπα και θα προχωρήσει παρακάτω.

Τέλος, να θυμάστε πάντοτε την επανάληψη των όρων της σύγκρισης όταν χρησιμοποιείτε λογικούς τελεστές. Το:

( $ilikia > 19 && <= 35 )

είναι λάθος. Σωστό είναι το:

( $ilikia > 19 && $ilikia <= 35 )

Επιστροφή στην Κορυφή

Τι είναι τα modules

Ένα από τα πλεονεκτήματα του open source software είναι η ελεύθερη διακίνηση και επαναχρησιμοποίηση κώδικα. Ο καλός προγραμματιστής είναι ο τεμπέλης προγραμματιστής (!) γιατί θα χρησιμοποιήσει τον πιο απλό και εύχρηστο τρόπο για να φθάσει στο επιθυμητό αποτέλεσμα.

Πολλοί προγραμματιστές χρειάστηκε συχνά να δημιουργήσουν ειδικές εφαρμογές και να επεκτείνουν τις δυνατότητες της Perl έτσι ώστε να καλύψουν κάποιες δικές τους ανάγκες. Ο κώδικας αυτός (συνήθως γραμμένος σε Perl, αλλά καμιά φορά και σε C) διατίθεται ως αυτόνομη προσθήκη στην Perl και είναι γνωστός με το όνομα module.

Υπάρχουν εκατοντάδες modules και πολλά από όσα επιθυμείτε να πραγματοποιήσετε καλύπτονται ήδη από αυτά. Στο παράδειγμα αυτής της σελίδας θα δούμε μια απλή εφαρμογή του module LWP (LWP σημαίνει Library for WWW Programming in Perl και στην πραγματικότητα πρόκειται για μια σειρά από modules και όχι για ένα απλό και μεμονωμένο module).

Κάθε module έχει τη δική του σύνταξη και τρόπο λειτουργίας και θα βρείτε όλα τα modules και οδηγίες για τη χρήση τους (αρκετά στρυφνές για να λέμε την αλήθεια) στο CPAN (http://www.cpan.org). Τέλος σημειώστε ότι τα modules δεν αποτελούν μέρος της τυπικής εγκατάστασης της Perl. Συνήθως, οι Linux distributions μαζί με την εγκατάσταση της Perl προσθέτουν και κάποια από τα πιο δημοφιλή modules. Αν εκείνα που σας ενδιαφέρουν δεν είναι εγκατεστημένα στο μηχάνημά σας, τότε θα πρέπει να φροντίσετε εσείς γι’ αυτό.

Πληροφορίες για το ποια modules είναι ήδη εγκατεστημένα και μπορούν να χρησιμοποιηθούν θα πάρετε από τον administrator του συστήματος που χρησιμοποιείτε. Αν είστε εσείς ο administrator το σύστημά σας πρέπει από κάπου να σας παρέχει αυτή την πληροφορία. Για παράδειγμα, στο Mandrake Linux μπορείτε να το δείτε από το rmpdrake.

Επιστροφή στην Κορυφή

Παράδειγμα module (Φέρνοντας web σελίδες από το Internet)

#!/usr/bin/perl -w

use strict;

use LWP::Simple;

my $page = get ('http://www.flash.gr');

print "$page";

Όπως βλέπετε τα πράγματα είναι πολύ απλά.

use LWP::Simple;

Με τον τρόπο αυτό δηλώνουμε στην Perl ότι πρέπει να χρησιμοποιήσει το module LWP::Simple

my $page = get ('http://www.flash.gr');

Θεωρήσαμε πως όλη η σελίδα είναι μια scalar variable την οποία ονομάσαμε $page και ζητήσαμε το URL 'http://www.flash.gr'

print "$page";

Η print δεν έχει σχέση με το module, αλλά μια και φέραμε τη σελίδα την τυπώνουμε (στην οθόνη) για να βεβαιωθούμε ότι πράγματι έφτασε κανονικά. Εννοείται ότι αυτό που ήρθε είναι το html αρχείο και όχι η πλήρης σελίδα με τα γραφικά της ή με όποια άλλα objects (αντικείμενα) περιέχει.

Αν τώρα θέλουμε να αποθηκεύσουμε αυτή τη σελίδα σε ένα αρχείο τότε ο κώδικάς μας γίνεται:

use LWP::Simple;

getstore ('http://www.in.gr', 'test.htm');

Η σελίδα του IN.gr θα αποθηκευτεί στο αρχείο test.htm.

Επιστροφή στην Κορυφή

Μια πιο ευέλικτη εφαρμογή λήψης web σελίδων

Όπως θα παρατηρήσετε, όταν αρχίσετε να ασχολείστε με modules, οι καλοί άνθρωποι που τα δημιουργούν δυστυχώς δεν δίνουν την ανάλογη σημασία στο documentation. Οι οδηγίες χρήσης είναι συνήθως σύντομες και δυσανάγνωστες ειδικά για τον αρχάριο προγραμματιστή.

Γι’ αυτό και θα επιμείνουμε περισσότερο στα πιο συνηθισμένα modules δίνοντας πολλά διαφορετικά παραδείγματα. Ο ακόλουθος κώδικας καλεί μια σελίδα και την αποθηκεύει σε μια scalar μεταβλητή:

#!/usr/bin/perl -w

use strict;

use LWP::Simple;

my $URL = 'http://www.flash.gr';

my $content;

$content = get($URL);

print "$content\n";

Η χρησιμότητά του βρίσκεται στο ότι:

  1. Αντί να δηλώσουμε στο module το URL ως μια διεύθυνση το δηλώνουμε ως μεταβλητή π.χ. αντί για my $page = get ('http://www.flash.gr') λέμε $content = get($URL). Έτσι, τώρα μπορούμε να χρησιμοποιήσουμε το ίδιο πρόγραμμα για να λάβουμε αυτόματα πολλά URLs.
  2. Το περιεχόμενο αποθηκεύεται στη μεταβλητή $content και μπορούμε να το διαχειριστούμε όπως θέλουμε.

Η εξήγηση του κώδικας είναι:

#!/usr/bin/perl -w

use strict;

use LWP::Simple;

Κατά τα γνωστά.

my $URL = 'http://www.flash.gr';

Ορίζουμε ποιο URL θέλουμε να καλέσουμε.

my $content;

Δηλώνουμε τη μεταβλητή $content

$content = get($URL);

Ορίζουμε το περιεχόμενο της $content και η Perl (για την ακρίβεια το module) φροντίζει να το λάβει.

print "$content\n";

Παρουσιάζουμε στην οθόνη το περιεχόμενο της $content. Αυτό φυσικά δεν είναι απαραίτητο, αλλά είναι ένας εύκολος τρόπος για να δούμε να η σελίδα ελήφθη σωστά.

Επιστροφή στην Κορυφή

Προσθήκη (concatenation), μαθηματικές πράξεις, συντομογραφίες

Εδώ τα παραδείγματα θα είναι πολύ πιο παραστατικά από τις εξηγήσεις:

$foo = 'Καλημέρα';

$bar = $foo . 'Μαρία';

print "$bar\n";

Το αποτέλεσμα της εκτύπωσης είναι:

ΚαλημέραΜαρία

Αυτή η διαδικασία λέγεται προσθήκη (concatenation) και προσέθεσε στο $foo που είχε την τιμή Καλημέρα την ακολουθία χαρακτήρων Μαρία. Αν το $foo αντί για ‘Καλημέρα’ είχε την τιμή ‘Καλημέρα ’ (υπήρχε δηλαδή και διάστημα) τότε το αποτέλεσμα της εκτύπωσης θα ήταν Καλημέρα Μαρία.

$foo = 'Καλημέρα';

$bar = $foo x3;

print "$bar\n";

Το αποτέλεσμα της εκτύπωσης είναι:

ΚαλημέραΚαλημέραΚαλημέρα

Δηλαδή πολλαπλασιάσαμε το $foo επί 3.

$foo = 'Γιώργος ';

$foo .= 'Επιτήδειος';

print "$foo\n";

Αυτή είναι η συντομογραφία της προσθήκης και λειτουργεί όπως το ΚαλημέραΜαρία. Εδώ όμως γράψαμε ‘Γιώργος ’ αντί για ‘Γιώργος’ (δηλαδή μετά το Γιώργος αφήσαμε ένα κενό) και η εκτύπωση δεν θα κολλήσει τις λέξεις, αλλά θα είναι:

Γιώργος Επιτήδειος

Αν τώρα θέλουμε να εκτυπώσουμε το:

This is the 12th day of the month, το πρόβλημα που έχουμε είναι ότι το th κανονικά δεν μπορεί να κολλήσει με τη μεταβλητή. Αν δώσουμε:

Print “This is the $dayth day of the month\n”;

η Perl θα ψάξει να βρει τη μεταβλητή $dayth και φυσικά θα αποτύχει. Για να της εξηγήσουμε πού τελειώνει η μεταβλητή η σύνταξη θα πρέπει να είναι:

Print “This is the ${day}th day of the month\n”;

Οι αγκύλες της εξηγούν ποια είναι τα όρια του ονόματος της μεταβλητής.

Ας δούμε τώρα μερικές άλλες ιδιομορφίες:

$foo = 1_500;

$bar = $foo + 1;

print "$bar\n";

Το αποτέλεσμα της εκτύπωσης είναι:

1501

Δηλαδή το 1_500 αναγνωρίζεται από την Perl ως 1.500

$foo = 1.500;

$bar = $foo + 1;

print "$bar\n";

Το αποτέλεσμα της εκτύπωσης είναι:

2,5

Δηλαδή το 1.500 αναγνωρίζεται από την Perl ως 1,5

$foo = 1,500;

$bar = $foo +1;

print "$bar\n";

Στο δικό μου σύστημα το αποτέλεσμα της εκτύπωσης είναι:

2

Δεν καταλαβαίνω γιατί συμβαίνει αυτό (αν κανείς γνωρίζει ας μου γράψει). Εγώ θα φανταζόμουν ότι αφού αναγνωρίζει το 1.500 ως 1,5 (κατά το αγγλοσαξονικό σύστημα γραφής) θα αναγνώριζε και το 1,500 ως 1.500. Σας το αναφέρω εδώ ως προειδοποίηση ότι η Perl μπορεί να αναγνωρίζει πολλά πράγματα αυτόματα (π.χ. αν μια μεταβλητή περιέχει αριθμούς ή χαρακτήρες), αλλά καμιά φορά συμβαίνουν και απρόοπτα.

$foo = 2*7;

$bar = ($foo-4)/2+7;

print "$bar\n";

Η Perl ακολουθεί την ίδια μαθηματική ακολουθία που μάθαμε και στο σχολείο (πρώτα οι πράξεις μέσα στις παρενθέσεις, μετά οι πολλαπλασιασμοί και οι διαιρέσεις και μετά οι προσθέσεις και αφαιρέσεις κινούμενοι από αριστερά προς τα δεξιά.

Το αποτέλεσμα της εκτύπωσης είναι:

12

Δυνάμεις:

$foo = 2 ** 3;

$bar = $foo ** 5;

print "$foo\n";

print "$bar\n";

Εδώ το $foo είναι 8 (2 εις την 3η), ενώ το $bar 32.768 (8 εις την 5η)

$foo = 5;

$bar = 5x3;

print "$bar\n";

Όπως και παραπάνω (ΚαλημέραΚαλημέραΚαλημέρα), εδώ έχουμε;

555

Αυτό θα συμβεί αν ξεχαστείτε και γράψετε x αντί για * (το σύμβολο του πολλαπλασιασμού).

$foo = 5;

$foo = $foo * 3;

print "$foo\n";

Προσέξτε ότι μια μεταβλητή μπορεί να μεταβάλει τον εαυτό της. Η παραπάνω σύνταξη είναι σωστή και μας δίνει ως εκτύπωση το 15.

Σωστή είναι επίσης και η φράση

$foo = $bar = 5;

όπου και οι δύο μεταβλητές αποκτούν την τιμή 5.

$foo = 5;

$foo *=3;

print "$foo\n";

Επειδή πολύ συχνά επιθυμούμε μια μεταβλητή να τροποποιήσει τον εαυτό της υπάρχει και σχετική συντομογραφία. Η $foo *=3 λειτουργεί ακριβώς όπως και η $foo = $foo * 3

$foo = 0;

$foo = ++$foo;

$bar = 9;

$lala = $bar--;

print "\$foo=$foo και \$bar=$bar και \$lala=$lala\n";

Εδώ έχουμε πιο πονηρά πράγματα. Η εκτύπωση μας δίνει:

$foo 1 και $bar 8 και $lala 9

Το $foo = ++$foo δηλώνει autoincrement, δηλαδή πάρε το $foo και πρόσθεσέ του μια μονάδα (γράφεται και $foo++ ή ++$foo). Έτσι, το $foo από 0 γίνεται 1.

Το $lala = $bar-- δηλώνει autodecrement (και μπορεί να γραφτεί επίσης ως --$bar)

Προσοχή όμως! Η σύνταξη αυτή δηλώνει: «Δώσε στο $lala την τιμή του $bar και μετά αφαίρεσε από το $bar μια μονάδα».

Τέλος, σας υπενθυμίζω ότι για να εκτυπώσουμε το «λέξη» $foo χρειάστηκε να γράψουμε \$foo διαφορετικά η Perl θα εκτύπωνε την τιμή της $foo, δηλαδή το 1.

my $a = 5;

my $b = 5;

my $c = $a++;

my $d = ++$b;

print "a = $a b = $b c = $c d = $d\n";

Το αποτέλεσμα της εκτύπωσης θα είναι:

a = 6 b = 6 c = 5 d = 6

Αυτό συμβαίνει διότι όταν τα ++ βρίσκονται μετά τη μεταβλητή ($a++) τότε η Perl πρώτα θα δώσει την τρέχουσα τιμή της μεταβλητής (εδώ η $c θα αποκτήσει την τιμή 5) και μετά θα την αυξήσει. Αντίθετα όταν τα ++ βρίσκονται πριν τη μεταβλητή (++$b) τότε η Perl πρώτα θα αυξήσει την τιμή της μεταβλητής (το $b) και μετά θα δώσει την νέα τρέχουσα τιμή κάπου αλλού (εδώ η $d λαμβάνει την τιμή 6).

$foo = 15;

$foo += 5;

print "$foo\n";

Το αποτέλεσμα της εκτύπωσης είναι:

20

Δηλαδή, με αυτή τη συντομογραφία προσθέσαμε το 5 στην ήδη υπάρχουσα τιμή του $foo και δεν χρειάστηκε να γράψουμε $foo = $foo + 5. Έχουμε δηλαδή:

$foo = $foo + 5;

Ανάλογα φυσικά λειτουργούν και τα -=, *=, /=, **= και %= (** είναι η δύναμη, ενώ % το υπόλοιπο, modulus, μιας διαίρεσης, π.χ. 15 % 4 = 3).

Επιστροφή στην Κορυφή

printf και sprintf (στρογγυλοποιήσεις)

Οι printf και sprintf αποτελούν «αδελφές» συναρτήσεις. Δέχονται τις ίδιες ακριβώς παραμέτρους και η διαφορά τους βρίσκεται στο γεγονός ότι η printf τυπώνει αμέσως, ενώ η sprintf χρησιμοποιείται για να τροποποιήσει μια τιμή (που φυσικά μετά μπορεί να τυπωθεί αν το επιθυμούμε).

Ας δούμε ένα παράδειγμα:

my $number = 8/3;

Το 8 δια του 3 μας δίνει: 2,66666666666666666666 κ.λπ. Εμείς όμως θέλουμε να στρογγυλέψουμε αυτό το νούμερο.

my $simpler = sprintf ("%.0f", $number);

Το % είναι υποχρεωτικό και το .0f σημαίνει ότι δεν θέλουμε καθόλου δεκαδικά ψηφία (floating point).

print "$simpler\n";

Η εκτύπωση εδώ θα είναι: 3 (2,7 που στρογγυλοποιείται σε 3).

Με ένα δεκαδικό ψηφίο έχουμε:

my $simpler = sprintf ("%.1f", $number);

Εδώ η $simpler γίνεται 2,7.

Αν θέλουμε να υπάρχουν 3 δεκαδικά ψηφία θα δηλώσουμε:

printf ("%.3f", $number);

Εδώ η εκτύπωση γίνεται απευθείας (χρησιμοποιήσαμε την printf αντί για τη sprintf) και το αποτέλεσμα θα είναι 2,667 (στρογγυλοποίηση του τελευταίου ψηφίου). Προσέξτε ότι εδώ ξεχάσαμε να βάλουμε το \n οπότε μετά την εκτύπωση δεν θα αλλάξει γραμμή.

Ας δούμε μερικά ακόμη παραδείγματα:

my $value = 4567899.789456123;

printf ("To akeraio meros ths timhs einai: %d\n" , $value);

Μας δίνει:

To akeraio meros ths timhs einai: 4567899

Το d σημαίνει ότι ενδιαφερόμαστε μόνο για το ακέραιο μέρος της τιμής και αγνοούμε τα δεκαδικά.

printf ("%.5f edo exoyme 5 dekadika\n", $value);

Μας δίνει:

4567899.78946 edo exoyme 5 dekadika

Επιστροφή στην Κορυφή

Μεταβλητές Hash

Όταν μιλήσαμε για arrays (λίστες) είδαμε ότι οι τιμές κάθε λίστας είναι δομημένες με μια συγκεκριμένη σειρά, π.χ. (75, ‘gepiti’, 356/2). Οι hashes (γνωστές και ως associative arrays) θα μπορούσαν να θεωρηθούν και αυτές ως λίστας, αλλά χωρίς συγκεκριμένη θέση κάθε τιμής. Στην hash δεν υπάρχει πρώτος, δεύτερος, τελευταίος κ.λπ. Αντί γι’ αυτό κάθε τιμή (value) έχει το δικό της κλειδί (key), δηλαδή τη δική της ετικέτα που τη χαρακτηρίζει.

Για παράδειγμα, η ακόλουθη hash χρησιμοποιείται για να καταχωρηθεί το ύψος και το βάρος μου:

my %info = (height => 185, weight => 87);

Το ίδιο αποτέλεσμα θα μας δώσει και η:

my %info = (‘height’ , 185 , ‘weight’ , 87);

(Οι τιμές πρέπει να είναι ζυγές διαφορετικά η τελευταία αγνοείται.)

Κάθε όνομα hash ξεκινάει με τον χαρακτήρα % και αφού δεν υπάρχει αρχή και τέλος δεν έχουμε λειτουργίες όπως οι shift, unshift, pop, push που μάθαμε στις arrays.

Για να προσθέσουμε μια τιμή δηλώνουμε απλώς:

$info{hair} = 'dark';

Για να πάρουμε μόνο τα ονόματα όλων των ετικετών μιας hash δηλώνουμε:

my @keys = keys(%info);

Ενώ για να έχουμε όλες τις τιμές δηλώνουμε:

my @values = values(%info);

Προσέξτε ότι οι τιμές καταχωρούνται στη hash με τυχαία σειρά και δεν υπάρχει κανενός είδους ταξινόμηση στα αποτελέσματα που θα λάβετε από αυτές τις λειτουργίες.

Για να δούμε τον αριθμό των στοιχείων μιας hash θα χρησιμοποιήσουμε την keys:

my %info = (height => 185, weight => 87, age => 36);

my $number_of_elements = keys %info;

print "Arithmos stoixeion = $number_of_elements\n";

Η εκτύπωση μας δίνει:

Arithmos stoixeion = 3

Για να λάβουμε τμήματα μιας hash χρησιμοποιούμε την ακόλουθη σύνταξη:

my @a_piece_of_info = @info{'height', 'hair'};

Ενώ για να διαγράψουμε ένα στοιχείο της hash:

delete $info{weight};

Με τον ίδιο τρόπο γίνεται και η αλλαγή τιμής:

$info{height} = 187;

και η τιμή από 185 γίνεται 187.

Αν θέλουμε να μεταφέρουμε ένα στοιχείο από μια hash σε μια άλλη (διαγράφεται από εκεί που βρίσκεται και προστίθεται στην άλλη) θα δηλώσουμε:

$new_hash{key} = delete $old_hash{key};

Για παράδειγμα:

my %info = ('height' , 185 , 'weight' , 87);

my %info2 = ('age' , 36 , 'income' , 87000);

$info2{height} = delete $info{height};

print %info;

print "\n";

print %info2;

Η εκτύπωση μας δίνει:

weight87

height185income87000age36

Για να διαχειριστούμε ένα ένα όλα τα στοιχεία μιας hash θα χρησιμοποιήσουμε τη λειτουργία foreach. Για παράδειγμα:

my %info = (height => 185, weight => 87, age => 36);

foreach my $element (keys %info)

{

print "The $element is $info{$element}\n";

}

Εδώ παίρνουμε ένα ένα τα keys, δίνουμε την τιμή τους σε μια προσωρινή μεταβλητή που την ονομάσαμε $element (φυσικά θα μπορούσε να έχει οποιοδήποτε όνομα) και εκτελούμε κάποιες εργασίες (εδώ μια απλή εξίσωση) για κάθε στοιχείο της hash.

Η εκτύπωση θα δώσει:

The height is 185

The weight is 87

The age is 36

Με τη foreach (for) μπορούμε και να διαγράψουμε όλα τα περιεχόμενα μιας hash. Απλούστερος και ταχύτερος τρόπος όμως είναι να δηλώσουμε:

%hash_name = ();

Επιστροφή στην Κορυφή

Συναρτήσεις της Perl

Η Perl διαθέτει έναν μεγάλο αριθμό από συναρτήσεις. Συναντήσαμε ήδη τις print, chomp, printf, sprintf, keys και values. Εδώ θα αναφέρουμε μερικές ακόμη.

Δυστυχώς όλες είναι πάρα πολλές για να τις παρουσιάσω όλες εδώ, έτσι περιορίζομαι στις σημαντικότερες λειτουργίες όσων συναρτήσεων θεωρώ εγώ πιο χρήσιμες. Για περισσότερες πληροφορίες διαβάστε ένα καλό βιβλίο για Perl.

Μαθηματικές Συναρτήσεις:

Abs

Μας δίνει την απόλυτη τιμή.

my $a = -5;

my $b = abs ($a);

Έτσι $b = 5

Rand και Int

Η Rand μας δίνει έναν ψευδοτυχαίο δεκαδικό αριθμό μεταξύ του 0 και του 1 (ο αριθμός μπορεί να πάρει την τιμή 0 αλλά όχι την τιμή 1).

my $c = rand;

print "c = $c\n";

Η τιμή που έλαβα εγώ ήταν 0,668212890625

my $d = rand 5;

print "d = $d\n";

Αν συνοδεύσουμε την rand με μια τιμή (π.χ. εδώ το 5) τότε ο τυχαίος αριθμός θα είναι μεγαλύτερος ή ίσος με το μηδέν, αλλά μικρότερος από την τιμή που δώσαμε (δηλαδή εδώ από το 5). Πράγματι η τιμή που έλαβα εγώ τρέχοντας αυτό το πρόγραμμα ήταν 4,65286254882813

Αν επιθυμούμε ο τυχαίος αριθμός μας να είναι ακέραιος, θα συνδυάσουμε τη rand με την int (integer) που μετατρέπει τους αριθμούς σε ακέραιους.

my $e = int(rand 5) +1;

print "e = $e\n";

Αυτό θα μας δώσει μια τιμή μεταξύ του 1 και του 5, ενώ το $e = int(rand 5) θα μας έδινε τιμή μεταξύ του 0 και του 4.

Η int μετατρέπει έναν αριθμό σε ακέραιο απαλείφοντας τα δεκαδικά ψηφία. Για παράδειγμα:

my $a = 5,99999999;

my $b = int ($a);

print "b = $b\n";

Το $b θα είναι ίσο με 5 και όχι με 6 όπως θα περίμενε κανείς βασισμένος στους κανόνες στρογγυλοποιήσεως. Για στρογγυλοποιήσεις χρησιμοποιήστε τη sprintf.

sqrt (τετραγωνική ρίζα)

my $a = '16';

my $b = sqrt($a);

print "b = $b\n";

Μας δίνει b = 4.

Συναρτήσεις διαχείρισης strings:

lc

Μετατροπή κεφαλαίων σε πεζά (μικρά).

my $a = 'ABCDEF’;

my $b = lc($a);

print "b = $b\n";

Μας δίνει: b = abcdef

lcfirst

my $a = 'ABCDEFG HIJKL';

my $b = lcfirst($a);

print "b = $b\n";

Μας δίνει: b = aBCDEFG HIJKL

uc - ucfirst

Μετατροπή όλων (ή μόνο του πρώτου χαρακτήρα) από πεζά σε κεφαλαία.

my $a = 'abcde fgh';

my $b = uc ($a);

my $c = ucfirst($a);

print "b = $b, c = $c\n";

Μας δίνει: b = ABCDE FGH και c = Abcde fgh

Reverse

Με αυτή τη συνάρτηση μπορούμε να αντιστρέψουμε τα περιεχόμενα μιας scalar ή μιας array:

my @test_array = (0, 12, 5, 'a', 9, 'z');

my @antistrofi = reverse @test_array;

print "@antistrofi \n";

Το αποτέλεσμα είναι:

z 9 a 5 12 0

my $test = "abcd123456789";

my $anapoda = reverse $test;

print "$anapoda \n";

Το αποτέλεσμα είναι:

987654321dcba

Map

Η map παίρνει τα περιεχόμενα μιας λίστας, τα τροποποιεί και τοποθετεί σε μια νέα λίστα το αποτέλεσμα της τροποποίησης κάθε ενός από αυτά. Για παράδειγμα:

my @test_array = (0, 12, 5, 18 , 9, 1966);

my @diplasia = map {$_*2} @test_array;

print "@diplasia \n";

Μας δίνει:

0 24 10 36 18 3932

Η map μπορεί να δεχθεί μέσα της ολόκληρα blocks εντολών. Για παράδειγμα:

my @test_array = (0, 12, 5, 18 , 9, 1966);

my @diaforetiki = map

{

if ($_<6) {}

elsif ($_ > 8 and $_ <16) {$_}

else {$_*3}

} @test_array;

print "@diaforetiki \n";

Το αποτέλεσμα είναι:

12 54 9 5898

Προσέξτε ότι οι τιμές που ήταν μικρότερες από 6 δεν συμπεριελήφθησαν στη νέα array (@diaforetiki) αφού είπαμε στη map να μην κάνει τίποτε ({}). Η map λοιπόν μπορεί να χρησιμοποιηθεί και για φιλτράρισμα των περιεχομένων μιας array και δημιουργία καινούριας με βάση κάποια κριτήρια.

Σημειώνουμε ότι το {$_} σημαίνει πως η τιμή πρέπει να παραμείνει αμετάβλητη, ενώ το {$_*3} πολλαπλασιάζει την τιμή του στοιχείου της λίστας με το 3.

Lenth

Με τη συνάρτηση αυτή βρίσκουμε τον αριθμό των χαρακτήρων μιας scalar. Για παράδειγμα:

my $test = 'abcdefghig';

my $how_many = length ($test);

print "Number of characters : $how_many\n";

Η εκτύπωση θα μας δώσει:

Number of characters : 10

Μέσα στους χαρακτήρες περιλαμβάνονται και τα κενά. Το my $test = 'abcde fghig' λοιπόν έχει μήκος 11 χαρακτήρες.

Time

Η συνάρτηση αυτή μας δίνει τον αριθμό των δευτερολέπτων που έχουν περάσει από την “epoch” (συνήθως την 1/1/1970 UTC). Επειδή μπορεί να διαφέρει από ένα λειτουργικό σύστημα σε ένα άλλο, χρησιμοποιείται συνήθως για να μετρήσουμε τον χρόνο που διήρκεσε μια εργασία. Για παράδειγμα:

my $start_time = time();

… κάποιες εργασίες

my $end_time = time();

my $duration = $end_time - $start_time;

Αφαιρόντας τη $start_time από την $end_time έχουμε τον χρόνο που διήρκεσαν οι εργασίες μας (σε δευτερόλεπτα).

localtime

Η συνάρτηση αυτή μας δίνει την τρέχουσα χρονική στιγμή (διορθωμένη κατά την τοπική ώρα). Για παράδειγμα το:

my $test = localtime (time());

μου έδωσε:

Wed Jun 19 11:01:01 2002

Δηλαδή το παράδειγμα αυτό έτρεξε την Τετάρτη 19 Ιουνίου 2002 στις 11:01:01.

Συνήθως τη localtime τη χρησιμοποιούμε σε list και όχι σε scalar μορφή όπως κάναμε παραπάνω. Στο παράδειγμα που ακολουθεί όλες οι πληροφορίες αποτελούν στοιχεία μιας array.

my @test = localtime (time());

Εδώ το αποτέλεσμα μιας εκτύπωσης είναι:

25 3 11 19 5 102 3 169 1

Στην παραπάνω τη λίστα:

  • Το [0] είναι τα δευτερόλεπτα (εδώ 25)
  • Το [1] είναι τα λεπτά (εδώ 3)
  • Το [2] είναι οι ώρες (εδώ 11)
  • Το [3] είναι οι ημέρες (εδώ η 19η του μήνα)
  • Το [4] είναι οι μήνες (εδώ ο 5). Οι μήνες μετρούν από το 0 έως το 11. Ο 5 λοιπόν είναι ο έκτος μήνας, δηλαδή ο Ιούνιος.
  • Το [5] είναι ο χρόνος (εδώ το 102). Τα χρόνια αρχίζουν να μετρούν από το 1900. Το 2002 λοιπόν είναι το 102.
  • Το [6] είναι η ημέρα της εβδομάδας. Οι ημέρες μετρούν από το 0 έως το 6. Η Τετάρτη λοιπόν είναι 3. (Η Κυριακή είναι 0 και το Σάββατο 6.)
  • Το [7] είναι ο αύξων αριθός της ημέρας από την αρχή του χρόνου. Η 19/6/2002 (σήμερα που γράφω αυτό το κείμενο) είναι η 170η ημέρα του χρόνου. Έχουν περάσει δηλαδή 169 ολόκληρες ημέρες.
  • Το [8] είναι το daylight savings time.

scalar

Υποχρεώνει μια array να συμπεριφερθεί ως scalar. Για παράδειγμα:

my @test = (15, 25, 3);

print scalar (@test);

Η print θα διαχειριστεί την @test ως μια scalar και θα μας δώσει τον αριθμό των στοιχείων της. Η λειτουργία εδώ είναι αντίστοιχη του

my @test = (15, 25, 3);

my $test1 = @test;

print "$test1";

Στην περίπτωση της localtime που αναφέρθηκε παραπάνω θα έχουμε:

my $current_time = scalar localtime;

print "$current_time\n";

Εδώ η $current_time λαμβάνει τη scalar μορφή της localtime δηλαδή Wed Jun 19 14:29:32 2002

Index

Μας δίνει τη θέση ενός string μέσα σε ένα άλλο. Για παράδειγμα:

my $big_string = ‘"After more than two long years of hard work, the perl5-porters is proud to announce Perl 5.8.0. The most important new features are enhanced Unicode and threads support, and the new I/O subsystem called PerlIO, but there are lots of new goodies, not to mention bazillion bug fixes. The full announcement is available, and you can read what is new in 5.8.0, and if you like what you see, start installing. Since this release has extensive support for non-Latin scripts, we also translated the announcement to Traditional Chinese (Big5-ETEN), Simplified Chinese (EUC-CN or GB2312), Japanese (EUC-JP), and Korean (EUC-KR) (by Autrijus Tang, Dan Kogai, and Jungshik Shin).';

my $search_string = 'Latin';

my $position = index($big_string, $search_string);

print "$position\n";

Εδώ ψάχνουμε για το string Latin μέσα στη $big_string. Η τιμή που θα μας δώσει η εκτύπωση είναι 458. Η index είναι case sensitive. Αν ψάξουμε για το latin η τιμή θα είναι –1 (δεν το βρήκε και επειδή ξεκίνησε από αρχική τιμή θέσης 0 αφαίρεσε μια μονάδα και μας έδωσε –1).

Substr

Η συνάρτηση αυτή χρησιμοποιείται για να λάβουμε το τμήμα ενός string που βρίσκεται μεταξύ συγκεκριμένων θέσεων. Η πιο συνηθισμένη σύνταξή της είναι:

Substr ( το string από το οποίο θα λάβουμε ένα τμήμα, θέση αρχής, μέγεθος τμήματος)

Για παράδειγμα:

my $big_string = 'Προχωράς προς το σπίτι του αγοριού/κοπελιάς σου. Υπάρχουν δυο δρόμοι για να πας εκεί. Ο ένας είναι ένας ίσιος δρόμος που σε πάει εκεί γρήγορα,

αλλα είναι συνηθισμένος και βαρετός. Ο άλλος είναι ένας με αρκετές στροφές

και γεμάτος με αξιοθέατα στη διαδρομή, αλλα αργείς λίγο για να φτάσεις στο

σπίτι του/της αγαπημένου/αγαπημένης σου';

my $new_string= substr ($big_string, 171, 7);

print "$new_string\n";

Εδώ λαμβάνουμε τους χαρακτήρες που βρίσκονται στις θέσεις 171 –177, δηλαδή τη λέξη βαρετός (όπως και στην index η θέση δεν συμπίπτει ακριβώς με τον αριθμό των χαρακτήρων + τα κενά γιατί πιθανώς να υπάρχουν και άλλοι «αφανείς» χαρακτήρες όπως οι αλλαγές γραμμών). Η πρώτη θέση έχει την τιμή 0 άρα αν θέλουμε την αρχή του string θα δηλώσουμε ως θέση εκκίνησης το 0. Αν δηλώσουμε αρνητική θέση εκκίνησης τότε η substr θα λειτουργήσει ανάποδα. Θα αφήσει τόσους χαρακτήρες από το τέλος και μετά θα λάβει τον αριθμό των χαρακτήρων που θέλουμε. Για παράδειγμα, στο παραπάνω string:

my $new_string= substr ($big_string, -20, 7);

Μας δίνει: μένου/α

Επιστροφή στην Κορυφή

Η συνάρτηση exists (διαγραφή διπλοτύπων)

Η συνάρτηση αυτή χρησιμοποιείται για να μας δείξει αν υπάρχει ή όχι ένα key μιας hash. Για παράδειγμα:

my %info = (height => 185, weight => 87);

my @info2 = (85,25,33);

if (exists $info{height})

{

print "\$info\{height\} exists\n";

}

else

{

print "\$info\{height\} does not exist\n";

}

if (exists $info2[3])

{

print "\$info[3] exists\n";

}

else

{

print "\$info[3] does not exist\n";

}

Εδώ η εκτύπωση θα δώσει:

$info{height} exists

$info[3] does not exist

Το $info{height} υπάρχει ενώ το $info[3] όχι.

Προσοχή! Η exists ελέγχει μόνο αν υπάρχει το στοιχείο, όχι αν η τιμή του είναι αληθής ή αν είναι defined (ένα στοιχείο μπορεί να υπάρχει και με ψευδή τιμή ή να βρίσκεται σε undefined κατάσταση).

Η συνάρτηση exists χρησιμοποιήται συχνά για τη διαγραφή διπλοτύπων ή την καταμέτρηση τιμών. Για παράδειγμα:

my %repetitions;

Δημιουργούμε μια hash για να τοποθετήσουμε το τελικό αποτέλεσμα των ενεργειών μας.

my @test = qw(Giorgos Nikos Matina Giorgos Giorgos Nikos Mixalis);

Αυτή είναι η λίστα με τα δεδομένα μας (χρησιμοποίησα το qw για να γλιτώσω την πληκτρολόγηση κομάτων και αποστρόφων). Θέλουμε να βρούμε πόσες φορές υπάρχει το καθένα και να καθαρίσουμε τη λίστα από τα διπλότυπα.

foreach my $element (@test)

Για κάθε στοιχείο της @test.

{

if (exists $repetitions{$element})

Αν υπάρχει ως key μέσα στην hash

{

$repetitions{$element}++;

}

αύξησε την τιμή του κατά μια μονάδα.

else

{

$repetitions {$element} = 1;

}

}

Διαφορετικά, βάλε το μέσα στην hash και δώσε του ως τιμή το 1.

Το

print %repetitions;

θα μας δώσει:

Mixalis1Nikos2Matina1Giorgos3

Οι τιμές (values) της hash μας δίνουν τον αριθμό των επαναλήψεων, ενώ τα keys της hash μας δίνουν τις μοναδικές τιμές της @test.

Επιστροφή στην Κορυφή

Η μεταβλητή $_

Μια πολύ βολική scalar μεταβλητή που θα συναντήσετε πολύ συχνά σε διάφορα προγράμματα είναι η $_ που χρησιμοποιείται για να υποδηλώσει την τρέχουσα τιμή. Φανταστείτε την σαν το clipboard (πρόχειρο) των Windows. Αν κάνουμε κάτι copy τότε αυτό κάθεται στο clipboard και περιμένει πότε θα γίνει paste.

Μπορούμε να το κάνουμε paste όσες φορές θέλουμε, λέγοντας απλώς στα Windows: «Κάνε εδώ paste αυτό που έχεις ήδη στο clipbard». Αν όμως κάνουμε copy κάτι άλλο, τότε αυτό αντικαθιστά το προηγούμενο και πλέον το «Κάνε εδώ paste αυτό που έχεις ήδη στο clipbard» αναφέρεται στο νέο περιεχόμενό του, ενώ το παλαιό έχει χαθεί (εκτός αν το έχουμε φυλάξει αλλού).

Αντίστοιχα λοιπόν λειτουργεί και η $_ που σημαίνει: «Χρησιμοποίησε την τιμή που έχεις ήδη». Θα το κατανοήσουμε όμως καλύτερα με ένα απλό παράδειγμα:

#!/usr/bin/perl -w

use strict;

my $line = 1;

Δίνουμε την τιμή 1 στη μεταβλητή $line.

while (<>) {

print $line, " ", $_;

$line = $line + 1;}

Επιστροφή στην Κορυφή

Slice – «Κόβοντας» arrays και hashes

Σε πολλές περιπτώσεις είναι χρήσιμο να λαμβάνουμε τμήματα μιας array ή κάποιας hash. Αυτό γίνεται με τον ακόλουθο τρόπο:

my @array_test = qw/one two three four five six/;

my @array_slice = @array_test[0,1,5];

Η @array_slice περιέχει τα one two six (τα 0, 1, 5 είναι οι αντίστοιχες θέσεις τους).

my %hash_test = ( 'giorgos' => 'brother', 'sophia' => 'sister', 'stefanos' => 'father', 'ioanna' => 'mother');

my @hash_slice = @hash_test {'giorgos','sophia'};

Η @hash_slice περιέχει τα brother sister. Προσέξτε ότι στην περίπτωση της hash δηλώσαμε σε μια array τα keys (giorgos ,sophia) και λάβαμε τις values σε μια άλλη array και όχι σε κάποια καινούρια hash.

Επιστροφή στην Κορυφή

File Handles - Λήψη δεδομένων από αρχεία κειμένου (text files) και επεξεργασία τους χωρίς όμως να πειράξουμε το αρχικό αρχείο.

Πριν ξεκινήσουμε δημιουργούμε φυσικά ένα αρχείο κειμένου. Στο παράδειγμά μας το αρχείο αυτό είναι το data.txt και το περιεχόμενό του είναι το ακόλουθο:

This is a

Basic PERL Framework

------------------

Example 1

by

Barry B. Floyd

------------------

Translated in the

Greek language

by

Giorgos Epitidios

Στο παρόν παράδειγμα θέλουμε να μετατρέψουμε αυτό το αρχείο από απλό κείμενο σε HTML:

#!/usr/bin/perl -w

use strict;

Κατά τα γνωστά

my $file ='data.txt';

Αν και θα μπορούσαμε να κάνουμε την Perl να ανοίγει απευθείας το αρχείο εμείς προτιμούμε να βάλουμε το όνομα του αρχείου μέσα σε μια scalar variable (μεταβλητή). Έτσι θα μας είναι ευκολότερο να κάνουμε μελλοντικά επεκτάσεις της εφαρμογής μας (π.χ. να ανοίγουμε πολλά αρχεία το ένα μετά το άλλο).

open (INFO, "<$file");

Εδώ λέμε στην Perl να ανοίξει αυτό το αρχείο. Η ακριβής μετάφραση της παραπάνω δήλωσης είναι:

  1. ’νοιξε (open) μέσα σου έναν ανεξάρτητο χώρο προσωρινής αποθήκευσης και διαχείρισης αρχείου (filehandle)
  2. Δώσε του το όνομα INFO. Το όνομα είναι απαραίτητο γιατί μας επιτρέπει να έχουμε ανοικτά περισσότερα από ένα filehandles το καθένα από τα οποία θα έχει το δικό του όνομα. Φυσικά μπορούμε να επιλέξουμε όποιο όνομα προτιμούμε εμείς. (Σε αρκετά βιβλία θα συναντήσετε παραδείγματα όπου το filehandle βαφτίζεται απλώς FILE. Αυτό όμως αποτελεί κακή πρακτική γιατί έτσι πολλοί αναγνώστες νομίζουν ότι πάντοτε έτσι πρέπει να ονομάζονται τα filehandles, κάτι που δεν είναι αλήθεια)
  3. Μέσα στο filehandle βάλε τα περιεχόμενα του αρχείου $file. Το αρχείο ανοίγει μόνο για ανάγνωση και τα περιεχόμενά του δεν μπορούν να τροποποιηθούν (αυτό δηλώνεται με το σύμβολο < μπροστά από το $file). Το άνοιγμα μόνο για ανάγνωση αποτελεί την default επιλογή της Perl. Το ίδιο αποτέλεσμα λοιπόν θα είχαμε και αν η σύνταξή μας ήταν: open (INFO, "$file");

my @lines = <INFO>;

Τοποθετούμε τα περιεχόμενα του αρχείου μέσα σε μια array (την @lines). Κάθε σειρά του αρχείου θα αποτελεί και ένα διαφορετικό στοιχείο της array.

print "<HTML><HEAD><TITLE>Text to HTML example</TITLE></HEAD>\n";

print "<BODY>\n";

Προσθέτουμε κώδικα HTML στην αρχή του αρχείου.

foreach my $line (@lines)

{

print "\n <P> $line </P>";

}

Το $line συμβολίζει ένα στοιχείο της @lines. Εδώ λοιπόν λέμε στην Perl ότι για κάθε στοιχείο ($line) της @lines θα πρέπει να το τυπώσει στην οθόνη προσθέτοντάς του ένα σημάδι παραγράφου στην αρχή και ένα σημάδι τέλους παραγράφου στο τέλος.

print "\n </BODY></HTML>\n";

Η Perl τυπώνει τον κώδικα τέλους της σελίδας HTML.

Το συνολικό τυπωμένο αποτέλεσμα που μας παράγει η Perl από το παραπάνω πρόγραμμα είναι το ακόλουθο:

<HTML><HEAD><TITLE>Text to HTML example</TITLE></HEAD>

<BODY>

<P> This is a

</P>

<P> Basic PERL Framework

</P>

<P> ------------------

</P>

<P> Example 1

</P>

<P> by

</P>

<P> Barry B. Floyd

</P>

<P> ------------------

</P>

<P> Translated in the

</P>

<P> Greek language

</P>

<P> by

</P>

<P> Giorgos Epitidios

</P>

<P>

</P>

</BODY></HTML>

(Το ξέρω ότι το </P> δεν χρειάζεται για να οριστεί μια παράγραφος και το παράδειγμα είναι πράγματι απλοϊκό. Πιο σύνθετα παραδείγματα θα διαβάσετε στη συνέχεια.)

Ολόκληρος ο κώδικας του παραπάνω προγράμματος είναι ο:

#!/usr/bin/perl -w

use strict;

my $file ='data.txt';

open (INFO, "<$file");

my @lines = <INFO>;

print "<HTML><HEAD><TITLE>Text to HTML example</TITLE></HEAD>\n";

print "<BODY>\n";

foreach my $line (@lines)

{

print "\n <P> $line </P>";

}

print "\n </BODY></HTML>\n";

Επιστροφή στην Κορυφή

Αποθήκευση των αποτελεσμάτων ενός προγράμματος σε ένα αρχείο (print redirection)

Μέχρι τώρα, κάθε φορά που δημιουργούσαμε ένα πρόγραμμα τα αποτελέσματα των εργασιών του εμφανίζονταν στην οθόνη μέσω της εντολής print. Η Perl διαθέτει ένα default filehandle με το όνομα STDOUT (standard output). Όταν λοιπόν λέμε στην Perl να τυπώσει, εννοούμε να τυπώσει σε αυτό που γνωρίζει (δηλαδή στην οθόνη η οποία αποτελεί το STDOUT).

Στην πλειοψηφία των περιπτώσεων όμως τα αποτελέσματα των εργασιών μας θέλουμε να τα αποθηκεύσουμε σε ένα αρχείο. Θα δούμε πώς με το ακόλουθο πρόγραμμα:

#!/usr/bin/perl -w

use strict;

Τα γνωστά (θα μπορούσα να μην τα βάζω πλέον, αλλά επιμένω γιατί πολλοί προγραμματιστές αδιαφορούν για τέτοιες «ενοχλητικές» τυπικότητες που τους υποχρεώνουν να συντάσσουν καλογραμμένο κώδικα και δεν θέλω να τους μοιάσετε).

my $file = '/mnt/win_e/Ex0204.log';

Δηλώνω το αρχείο που θέλω να διαχειριστώ. Στην προκειμένη περίπτωση πρόκειται για το log file ενός web server. Το αρχείο αυτό είναι σε text μορφή (ASCII) και η Perl μπορεί να το ανοίξει και να το διαχειριστεί χωρίς πρόβλημα. Το αρχείο αυτό αποτελείται από γραμμές με περιεχόμενο όπως το ακόλουθο:

2002-04-01 00:00:58 151.204.127.149 - 62.1.204.6 80 GET /news/newszoom.asp?id=4689 200 Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.1;+Q312461) ClientId=1082294 http://www.aw.gr/news/Paper.asp?in_PaperID=84

ή

2002-04-01 00:00:58 151.204.127.149 - 62.1.204.6 80 GET /news/default.htm - 200 Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.1;+Q312461) ClientId=1082294 http://www.aw.gr/news.asp

Αυτό που εμείς επιθυμούμε είναι να βρούμε όλες τις γραμμές που αναφέρονται σε σελίδες με δυναμικό περιεχόμενο και να τις αποθηκεύσουμε σε ένα ξεχωριστό αρχείο.

my $statfile = 'data.txt';

Δηλώνω το αρχείο στο οποίο θέλω να αποθηκευτούν τα αποτελέσματα της εργασίας του προγράμματος.

my @page_stats;

Το αρχείο Ex0204.log το άνοιξα προηγουμένως με έναν editor και παρατήρησα ότι αποτελείται από πολλές γραμμές κειμένου, κάθε μια από τις οποίες αντιστοιχεί σε μια κλήση προς τον web server. Αποφάσισα λοιπόν ότι αυτό που θα κάνω είναι να δημιουργήσω μια array όπου κάθε στοιχείο της θα είναι και μια γραμμή από το αρχείο Ex0204.log. Αντί όμως για όλο το περιεχόμενο του αρχείου η @page_stats θα περιέχει μόνο τα στοιχεία που με ενδιαφέρουν, δηλαδή κλήσεις σε συγκεκριμένες web σελίδες.

open (LOGFILE, "$file") || die "can't open file: $!";

Ανοίγω το Ex0204.log χρησιμοποιώντας ένα file handle που το ονόμασα LOGFILE.

while (<LOGFILE>) {

if ($_ =~ /id=/)

{

push (@page_stats, $_);

}

}

Όλα τα παραπάνω τα έχετε συναντήσει ήδη σε προηγούμενα μαθήματα. Λέω στην Perl ότι όσο διαχειρίζεται το LOGFILE (που με τη «μαγική» While το κοιτάει γραμμή γραμμή), αν η τρέχουσα γραμμή (δηλαδή η γραμμή που κοιτάει εκείνη τη στιγμή, δηλαδή η $_) περιέχει την ακολουθία χαρακτήρων (string) id= τότε αυτή η γραμμή να πηγαίνει ολόκληρη και να αποθηκεύεται ως μια ξεχωριστή τιμή της array @page_stats.

(Αναζητούμε την ακολουθία id= επειδή γνωρίζουμε ότι μόνο όσες γραμμές περιέχουν αυτούς τους χαρακτήρες αναφέρονται σε δυναμικές σελίδες.)

close (LOGFILE);

Αφού δεν μας χρειάζεται πλέον το file handle LOGFILE το κλείνουμε.

open (WRITEFILE, "+>$statfile") or die "can't open file: $!";

Ανοίγουμε το αρχείο στο οποίο θα τοποθετήσουμε τα περιεχόμενα της μεταβλητής @page_stats και δίνουμε στο file handle το όνομα WRITEFILE. Προσέξτε ότι:

  1. Το όνομα του αρχείου βρίσκεται μέσα σε εισαγωγικά («») επειδή δεν δηλώνουμε το ίδιο το όνομα του αρχείου, αλλά μια μεταβλητή η οποία το περιέχει. Αν δηλώναμε απευθείας το όνομα του αρχείου, τότε αντί για εισαγωγικά θα βάζαμε αποστρόφους (‘’).
  2. Του ονόματος του αρχείου προηγούνται οι χαρακτήρες +>. Η σημασία όλων αυτών των προσδιοριστικών είναι:

<

Ανοίγουμε το αρχείο μόνο για να διαβάσουμε το περιεχόμενό του (reading). Το < αποτελεί την default τιμή. Αν δηλαδή δεν γράψουμε τίποτε εννοείται ότι το αρχείο θα ανοίξει μόνο για ανάγνωση.

>

Ανοίγουμε το αρχείο μόνο για καταγραφή (writing).

>>

Ανοίγουμε το αρχείο μόνο για προσθήκη (appending).

+<

Ανοίγουμε το αρχείο για καταγραφή ή ανάγνωση (reading or writing).

+>

Η Perl διαγράφει όλα τα περιεχόμενα του αρχείου και το ανοίγει για καταγραφή ή ανάγνωση (reading or writing).

my $old_output = select;

select WRITEFILE;

Η εντολή select καθορίζει πού θα οδηγηθούν τα αποτελέσματα της εντολής print. ’ρα εμείς θα έπρεπε να δηλώσουμε απλώς select WRITEFILE και αυτομάτως έχει γίνει redirection της εκτύπωσης στο αρχείο που θέλουμε (εδώ στο data.txt που δηλώσαμε με τη μεταβλητή $statfile).

Ως προνοητικοί άνθρωποι που είμαστε όμως, πριν το κάνουμε αυτό αποθηκεύσαμε το προηγούμενο περιεχόμενο της select στη μεταβλητή $old_output. Το γιατί θα το δείτε παρακάτω.

print "@page_stats;\n";

Τυπώνουμε το περιεχόμενο της @page_stats που σημαίνει ότι η Perl το αποθηκεύει στο αρχείο data.txt.

select $old_output;

Η εργασία μας τελείωσε, αλλά όχι απαραίτητα και το πρόγραμμα. Πιθανώς να πρέπει να γίνουν και άλλες δουλειές «παρακάτω», δηλαδή να προσθέσουμε και άλλο κώδικα τώρα ή αργότερα. Με το select μας όμως εμείς «χαλάσαμε» την εντολή print και τώρα οδηγεί τα αποτελέσματα σε διαφορετικό «σημείο» απ’ ό,τι προηγουμένως.

Εδώ το πρόγραμμα είναι δικό μας και αρκετά μικρό ώστε θυμόμαστε πολύ καλά τι συνέβαινε προηγουμένως (η print χρησιμοποιούσε το file handle STDOUT). Υπάρχει όμως η περίπτωση να χρειάστηκε να μπούμε «στη μέση» ενός μεγάλου προγράμματος που έχει φτιάξει κάποιος άλλος (ή εμείς αλλά πριν από τόσο καιρό που δεν θυμόμαστε πλέον ακριβώς τι κάναμε σε κάθε σημείο του).

Έτσι, ως καλοί και ευγενικοί μουσαφίρηδες θα αφήσουμε τα πράγματα όπως τα βρήκαμε. Με την παραπάνω εντολή λοιπόν ξαναδίνουμε στην print την προηγούμενη λειτουργία της, όποια κι αν ήταν αυτή.

print "edo eimaste pali\n";

Ελέγχουμε ότι πράγματι η print επέστρεψε στις παλαιές της συνήθειες (εμφάνιση του αποτελέσματος, δηλαδή του «edo eimaste pali» στην οθόνη).

Ολόκληρος ο κώδικας του παραπάνω προγράμματος είναι ο:

#!/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 ($_ =~ /id=/)

{

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";

Επιστροφή στην Κορυφή

Print to file – Append to file

Στο παρακάτω παράδειγμα θα δούμε έναν απλό τρόπο για να τοποθετούμε δεδομένα μέσα σε ένα αρχείο.

#!/usr/bin/perl -w

use strict;

Τα γνωστά

my $file = 'test.txt'; # Το αρχείο στο οποίο θα αποθηκευτούν τα δεδομένα

open (APPENDFILE, ">>$file") || die "can't open file: $!";

Ανοίγουμε κατά τα γνωστά το αρχείο. Το >> σημαίνει ότι ανοίγει για προσθήκη. Ό,τι καινούριο δημιουργηθεί θα προστεθεί στο ήδη υπάρχον περιεχόμενο του αρχείου. Αν δηλώναμε >$file τότε το νέο περιεχόμενο θα αντικαθιστούσε (overwrite) το παλιό.

my $input = 6;

Δηλώνουμε (declare) τη μεταβλητή $input όπως απαιτεί το –w. Κανονικά εδώ δεν χρειάζεται να τις δώσουμε τιμή. Το γιατί γίνεται αυτό θα φανεί αμέσως παρακάτω.

while ($input ne '5')

Εδώ λέμε στην Perl ότι όσο η $input δεν είναι ίση με 5 πρέπει να κάνει κάποια πράγματα (το περιεχόμενο των {}). Επειδή η Perl ελέγχει την τιμή της $input κάθε φορά που τρέχει το περιεχόμενο της while, την πρώτη φορά τη συγκρίνει με την αρχική declared τιμή (το 6). Αν δεν είχαμε δηλώσει τιμή εκεί το πρόγραμμα πάλι θα έτρεχε, αλλά θα μας διαμαρτυρόταν πρώτα για την απουσία τιμής για να κάνει τη σύγκριση.

{

print "Γράψε κάτι: ";

$input = <STDIN>;

chomp ($input);

Στην οθόνη εμφανίζεται το Γράψε κάτι και ο χρήστης πληκτρολογεί ό,τι επιθυμεί. Αν πληκτρολογήσει 5 το πρόγραμμα θα σταματήσει.

print APPENDFILE "$input\n";

}

Το περιεχόμενο της $input (αυτό που πληκτρολόγησε ο χρήστης) «τυπώνεται» στο filehandle APPENDFILE δηλαδή στο αρχείο test.txt

Επιστροφή στην Κορυφή

Τοποθετώντας δεδομένα σε καινούρια αρχεία

Πολλές φορές αντί να αποθηκεύσουμε κάτι σε ένα ήδη υπάρχον αρχείο προτιμούμε να δημιουργήσουμε ένα νέο. Ας δούμε πώς θα γίνει αυτό στην Perl.

my $content = ‘κάποια δεδομένα’;

my $sample_time = time();

open (NEWFILE, ">$sample_time.txt") or die "can't create file $!";

print NEWFILE "$content";

close (NEWFILE);

Επιστροφή στην Κορυφή

Αντικαταστάσεις (substitution operations)

Στο ακόλουθο παράδειγμα θα προσπαθήσουμε να κάνουμε κάτι αρκετά πιο περίπλοκο. Θα ανοίξουμε το αρχείο 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";

Επιστροφή στην Κορυφή

Αντικαταστάσεις μέσα σε λίστες

Στο προηγούμενο παράδειγμα θα παρατηρήσατε ότι μια μια γραμμή ($_) του αρχείου ελεγχόταν από την Perl και γίνονταν οι απαραίτητες αντικαταστάσεις. Η $_ είναι μια scalar μεταβλητή. Υπάρχουν όμως περιπτώσεις που εμείς θέλουμε να κάνουμε αλλαγές σε όλες τις τιμές μιας λίστας. Αυτό μπορεί να γίνει με την εντολή for που λειτουργεί με τρόπο ανάλογο της foreach. Αντί λοιπόν να πραγματοποιούμε τις αλλαγές μας σε μια μια τιμή, παίρνουμε ολόκληρη τη λίστα και δηλώνουμε:

for (@page_stats)

{

s/.asp (\w)/.asp?$1/;

s/asp - /asp /;

s/.htm -/.htm/;

}

Σε κάθε τιμή της λίστας λοιπόν εκτελούνται όλες οι εντός των αγκίστρων εργασίες.

Ολόκληρος ο κώδικας του τροποποιημένου πλέον προγράμματος είναι ο:

#!/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

{

push (@page_stats, $_);

}

}

for (@page_stats)

{

s/.asp (\w)/.asp?$1/;

s/asp - /asp /;

s/.htm -/.htm/;

}

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";

Επιστροφή στην Κορυφή

Grep και sort (και ένα παράδειγμα κατάργησης διπλοτύπων)

Μια από τις συνηθέστερες εργασίες μας είναι η διαγραφή όλων των διπλοτύπων από μια λίστα. Για παράδειγμα, έχω μια λίστα με URLs πολλά από τα οποία είναι καταγεγραμμένα εκεί δύο ή περισσότερες φορές και θέλω να την «καθαρίσω» έτσι ώστε να περιέχει κάθε URL μόνο μια φορά. Για να γίνει αυτό θα χρησιμοποιήσω την grep:

my %exist;

Πρώτα δημιουργώ μια hash μέσω της οποία θα «καθαρίσω» όλες τις διπλότυπες τιμές.

my @uniqueURL = grep {! $exist{$_}++} @allURLs;

Αυτή η μαγική γραμμή κώδικα θα κάνει όλη τη δουλειά. Όπως συμβαίνει πολλές φορές στην Perl για να την καταλάβουμε πρέπει να διαβάσουμε από δεξιά προς τα αριστερά. Ο κώδικάς μας λέει: «Πάρε την array @allURLs (όλες οι τιμές της είναι URLs). Η $exist αποτελεί το key της %exist. Κάθε φορά που συναντάς μια τιμή της @allURLs (που η Perl τις περνάει όλες μία μία από την $_) πρόσθεσε μια μονάδα στην value που έχει για key αυτό το URL.

Εξυπακούεται (η Perl μερικές φορές είναι πολύ έξυπνη) ότι αν δεν υπάρχει αυτό το key στην %exist, τότε θα δημιουργηθεί με τιμή 1. Με τον τρόπο αυτό λοιπόν η %exist περιέχει πλέον ως keys όλα τα URLs της @allURLs, ενώ το value κάθε URL είναι ο αριθμός των επαναλήψεών του.

Πηγαίνοντας πιο αριστερά βλέπουμε και την grep. Αυτό που λέμε στην Perl είναι πως πρέπει να παρακολουθεί όλα όσα συμβαίνουν μέσα στα άγκιστρα που ακολουθούν και αν δεν υπάρχει (αυτό υποδηλώνει το θαυμαστικό) η $exist ($_), δηλαδή αν δεν υπάρχει κάποιο URL που να βρίσκεται ήδη μέσα στην %exist, τότε αυτό το URL να καταχωρείται στην @uniqueURL.

Γενικά η grep αποτελεί ένα φίλτρο που ξεχωρίζει με βάση κάποιους κανόνες τα περιεχόμενα μιας λίστας. (Στην προκειμένη περίπτωση της @allURLs.)

Στο παραπάνω παράδειγμα είχαμε την ακόλουθη γενική σύνταξη:

grep - {μια σειρά από εργασίες} - λίστα (η @allURLs)

Μια πιο απλή σύνταξη θα ήταν η:

grep – μια έκφραση - λίστα

Για παράδειγμα:

my @beginxlist = grep m/^x/, @biglist;

Εδώ η @beginxlist αποτελείται από όσες τιμές της @biglist ξεκινούν από x. (Προσέξτε το κόμμα πριν την @biglist που δεν υπήρχε στο πιο περίπλοκο παράδειγμα με τις αγκύλες).

Sort

Η Perl διαθέτει μερικές βασικές διαδικασίες ταξινόμησης. Για παράδειγμα, αν:

@numberlist = (15, 7, 28, 6)

τότε το:

@numberlist = sort { $a <=> $b } @numberlist;

μας δίνει: @numberlist = (6, 7, 15, 28)

Αν δηλώναμε { $b <=> $a } τότε θα είχαμε:

@numberlist = (28, 15, 7, 6), δηλαδή αντίστροφη ταξινόμηση.

Ο comparison operator (<=>) δεν χρησιμοποιείται κατά την ταξινόμηση strings. Για παράδειγμα:

my @my_array = ('a', 'd', 'y', 'b', 'c');

my @ordered_array = sort @my_array;

print "\@my_array \= @my_array\n";

Μας δίνει: @my_array = a d y b c

print "\@ordered_array \= @ordered_array\n";

Μας δίνει: @ordered_array = a b c d y

Όπως παρατηρείτε η @my_array δεν μεταβλήθηκε καθόλου.

Πιο περίπλοκο είναι το ακόλουθο παράδειγμα:

foreach my $key (sort{$exist{$b} <=> $exist{$a}} keys %exist)

{

print "$key $exist{$key}\n";

}

Όπως ίσως θα θυμάστε από το παράδειγμα της grep, η %exist περιέχει μια λίστα με μοναδικά URLs, καθώς και τις φορές που υπήρχε το καθένα σε μια άλλη λίστα (που την πήραμε από ένα log file). Στις hash όμως τα δεδομένα τοποθετούνται σε τυχαία σειρά και εμείς θέλουμε να ξέρουμε πόσα URLs επαναλήφθηκαν τις περισσότερες φορές. Γι’ αυτό, θα τα ταξινομήσουμε ανάλογα με την value τους (κάθε URL αποτελεί και ένα ξεχωριστό Key).

Για να μην ξεχνιόμαστε υπενθυμίζω ότι η value ενός key μιας hash απεικονίζεται ως:

$hashname{key}

Έτσι λοιπόν, παραπάνω κώδικας λέει στην Perl:

«Σορτάρησε» ένα ένα (foreach) τα keys της %exist με βάση τις values αντίστροφα (από τη μεγαλύτερη προς τη μικρότερη) και τύπωσε ένα ένα τα keys μαζί με την τιμή τους.

Πιο απλό φυσικά θα ήταν να «σορτάρουμε» μόνο τα keys.

my %hash_test = ( 'giorgos' => 'brother', 'sophia' => 'sister', 'stefanos' => 'father', 'ioanna' => 'mother');

my @sorted_keys = sort {$b <=> $a} keys %hash_test;

print "@sorted_keys\n";

Εδώ η εκτύπωση θα μας δώσει: giorgos ioanna sophia stefanos Δημιουργήσαμε δηλαδή μια νέα array (την @sorted_keys) που περιέχει όλα τα keys σε αλφαβητική σειρά.

Αν πάντως τρέξατε τον παραπάνω κώδικα θα παρατηρήσατε το μήνυμα λάθους:

Argument "sophia" isn't numeric in sort at test2.pl line 4.

Argument "stefanos" isn't numeric in sort at test2.pl line 4

Argument "ioanna" isn't numeric in sort at test2.pl line 4.

Argument "giorgos" isn't numeric in sort at test2.pl line 4.

Αυτό συμβαίνει διότι στη sort χρησιμοποιήθηκε ο τελεστής <=> αντί για τον cmp. Ο πρώτος χρησιμοποιείται για συγκρίς εις αριθμών, ενώ ο δεύτερος για strings (ακολουθίες χαρακτήρων). Ο σωστός κώδικας λοιπόν θα είναι:

my %hash_test = ( 'giorgos' => 'brother', 'sophia' => 'sister', 'stefanos' => 'father', 'ioanna' => 'mother');

my @sorted_keys = sort {$b cmp $a} keys %hash_test;

print "@sorted_keys\n";

Επιστροφή στην Κορυφή

Ένα απλό configuration file

Πολλές φορές χρειάζεται να διαβάσουμε και να χρησιμοποιήσουμε δεδομένα που υπάρχουν σε κάποιο configuration file. Για παράδειγμα, μπορεί έχουμε έναν πίνακα αποδεκτών και να θέλουμε να στείλουμε σε αυτούς κάποιο email. Τα στοιχεία των πελατών μπορούν να βρίσκονται σε ένα απλό text file που θα μοιάζει με το ακόλουθο:

Γιώργος---#---Επιτήδειος---#---gepiti@gepiti.com---#---Ιστιοπλόος

Σοφία---#---Επιτηδείου---#---sophia@gepiti.com---#---Αδερφή του Γιώργου

Ματίνα Ειρήνη---#---Παπαδοπούλου---#---matina@kati.com---#---Φίλη του Γιώργου

Κάθε γραμμή αποτελείται από ένα άτομο και τα μεταξύ τους στοιχεία χωρίζονται με το ---#--- δηλαδή το ---#--- είναι ο delimiter μας. Θα μπορούσαμε να επιλέξουμε όποιο delimiter θέλουμε (ακόμη και το κενό διάστημα). Εγώ όμως προτίμησα κάτι που να μην υπάρχει πουθενά ώστε να μην μπερδέψω την Perl και κάνει λάθη στο «κόψιμο» (για παράδειγμα αν επέλεγα το κενό θα είχε πρόβλημα στα σχόλια, π.χ. «Αδερφή του Γιώργου» ή στα διπλά ονόματα π.χ. «Ματίνα Ειρήνη»

Αφού έχω έτοιμο το αρχείο, δημιουργώ τώρα την εφαρμογή που θα το διαβάσει:

open (MAILDATA, "$mail_file") || die "can't open file:$!";

Ανοίγουμε το αρχείο που βρίσκεται στη θέση την οποία έχουμε υποδηλώσει ήδη στη μεταβλητή $mail_file (π.χ.

my $mail_file = '/mnt/win_d/my_personal_documents/linuxbak/mail_data.txt')

while (<MAILDATA>)

{

Δηλαδή για κάθε γραμμή του αρχείου

my @mail_config = split (/---#---/,$_);

δημιούργησε μια λίστα που θα έχει ως τιμές τα περιεχόμενα της γραμμής. Θα καταλάβεις πότε αλλάζει η τιμή από την ακολουθία ---#---

my $mail_to_get = $mail_config[2];

push (@mails_to_get, $mail_to_get);

Πάρε την τρίτη τιμή (στις arrays αρχίζουμε να μετράμε από το 0) και βάλε την σε μια νέα array (την (@mails_to_get).

}

close (MAILDATA);

Με τον τρόπο αυτό έχουμε πλέον μια νέα array που περιέχει τις τιμές:

gepiti@gepiti.com

sophia@gepiti.com

matina@kati.com

Φυσικά για να λειτουργήσει αυτό το σύστημα όλες οι τιμές πρέπει να είναι συμπληρωμένες και να μην υπάρχουν κενά. (Κάθε γραμμή θα είναι πλήρης.)

Επίσης, το τέλος κάθε γραμμής δεν πρέπει να περιέχει carriage return (Enter) ή αλλαγή γραμμής γιατί τότε η τιμή που θα λάβει η Perl θα είναι:

Αδερφή του Γιώργου\n ή (\r) αντί για Αδερφή του Γιώργου με αποτέλεσμα να λειτουργούμε με διαφορετικές τιμές από εκείνες που θέλουμε. Γι’ αυτό καλύτερα το configuration file να έχει τη μορφή:

Ματίνα Ειρήνη---#---Παπαδοπούλου---#---matina@kati.com---#---Φίλη του Γιώργου---#---

Επιστροφή στην Κορυφή

Ένα απλό CGI script

Θα δημιουργήσουμε μια web σελίδα με φόρμα όπου ο επισκέπτης θα δίνει το όνομά του και θα λαμβάνει απάντηση όπου θα αναφέρεται αυτό το όνομα. Ο HTML κώδικας της σελίδα θα είναι:

<HTML><head><TITLE>Φόρμα επικοινωνίας</TITLE></head><BODY bgcolor=white>

<form action="/cgi-local/test.pl">

<p>Γράψτε μας το όνομά σας: <INPUT NAME="visitor_name">

<p><INPUT TYPE="submit" VALUE="Υποβολή ονόματος">

</form>

</body>

</html>

(Δείτε τις σχετικές σελίδες του site http://www.eeei.gr για οδηγίες κατασκευής web σελίδων και τοποθέτηση φορμών μέσα σε αυτές.)

Από πλευράς CGI scripting ο παραπάνω κώδικας HTML περιέχει δύο σημαντικές πληροφορίες για μας:

<form action="/cgi-local/test.pl">

Δηλώνουμε δηλαδή πως το script μας ονομάζεται test.pl και είναι τοποθετημένο στον υποφάκελο cgi-local. Το πού πρέπει να τοποθετήσετε τα scripts σας και τι θα κάνετε για να λειτουργήσουν (π.χ. ρύθμιση των ανάλογων permissions) θα το συζητήσετε με τον system administrator του web server και γενικότερα του μηχανήματος που φιλοξενεί τις σελίδες και τα scripts σας.

<INPUT NAME=" visitor_name">

Ο χρήστης δηλώνει μόνο ένα στοιχείο σε εμάς. Αυτό έχει τον τίτλο visitor_name και η μορφή του είναι κείμενο (text) που μας δακτυλογραφεί ο ίδιος ο χρήστης. (Ο πλήρης κώδικας είναι <INPUT TYPE=”text” NAME="visitor_name"> και το TYPE=”text” δηλώνει ότι στον χρήστη εμφανίζεται ένα παράθυρο κειμένου για να πληκτρολογήσει. Επειδή όμως το TYPE=”text” αποτελεί την default τιμή του INPUT εννοείται όταν δεν έχει οριστεί κάτι άλλο και γι’ αυτό μπορούμε να το παραλείψουμε.)

Το CGI script μας θα είναι το ακόλουθο:

#!/usr/local/bin/perl -w

Ο administrator του μηχανήματος θα μας πει ποιο ακριβώς path πρέπει να δηλώσουμε.

use strict;

Κατά τα γνωστά

use CGI qw(:standard);

Αν και θα μπορούσαμε να δημιουργήσουμε ένα CGI script από το μηδέν αυτό είναι πολύ δύσκολο και δεν υπάρχει λόγος να εφευρίσκουμε από την αρχή τον τροχό. Θα δουλέψουμε λοιπόν βασισμένοι στο πολύ δημοφιλές module CGI.pm

print header();

Μέσω του CGI.pm δηλώνουμε στον web server τι είδους περιεχόμενο θα σταλεί πίσω στον χρήστη. Το print header() σημαίνει πως το περιεχόμενο θα είναι text/html. (Αυτό που θα λάβει ως απάντηση ο χρήστης θα έχει τη μορφή web σελίδας.)

print start_html ('Καλημέρα');

Το CGI.pm φροντίζει για τον αρχικό HTML κώδικα της web σελίδας (HTML, HEAD, TITLE κ.λπ.) που θα λάβει ο χρήστης. Ο τίτλος της σελίδας είναι Καλημέρα.

print "<h3>Το όνομά σου είναι: ", param('name'), "</h3>\n";

Το ", param('name'), " περιέχει τα δεδομένα που συμπλήρωσε ο χρήστης (προσοχή στα μονά και διπλά εισαγωγικά, αλλά και στα κόμματα).

print "Ευχαριστώ <b>πολύ</b> που συμπλήρωσες τη φόρμα.\n";

Δικά μας σχόλια ή όποιος κώδικας HTML επιθυμούμε.

print end_html;

Τα καταληκτικά τμήματα της web σελίδας (</body></html>)

Πριν ανεβάσουμε το script μας online ένας απλός για να το ελέγξουμε είναι να τρέψει απευθείας από το PC μας. Για να γίνει αυτό αρκεί να γράψουμε στο UNIX prompt:

perl test.pl name=Γιώργος (ή κάποιο άλλο όνομα που μπορεί να έδινε ο επισκέπτης της σελίδας)

Αν όλα πάνε καλά, τότε θα πρέπει να εμφανιστεί στην οθόνη μας ο HTML κώδικας της σελίδας που θα αποστέλλονταν στον επισκέπτη της μετά την υποβολή της σχετικής φόρμας.

Επιστροφή στην Κορυφή

System commands (system function)

Η Perl μας δίνει τη δυνατότητα να εκτελέσουμε εντολές του command prompt μέσα από τα scripts μας. Ο συνηθέστερος τρόπος να το κάνουμε αυτό είναι με την εντολή system. Για παράδειγμα:

system ('ls');

Το script μας θα ανοίξει ένα UNIX shell και θα τρέξει την εντολή ls που έχουμε τοποθετήσει μέσα σε αποστρόφους. Μετά την εκτέλεση της system θα εκτελεστούν κανονικά οι υπόλοιπες γραμμές κώδικα της εφαρμογής μας (σε αντίθεση με την εντολή exec που διακόπτει τη λειτουργία του προγράμματός μας και ξεκινά κάτι άλλο).

Η system μπορεί να λειτουργήσει παραμετρικά:

my $file = 'log.txt';

system ("cat $file");

Προσέξτε ότι εδώ χρησιμοποιήσαμε διπλές αποστρόφους επειδή μέσα στη system περιέχεται μια μεταβλητή και θέλουμε η Perl να χρησιμοποιήσει την τιμή αυτής της μεταβλητής. Αντίθετα στο system ('ls') το ls τοποθετήθηκε σε μονές αποστρόφους διότι θέλαμε η Perl να εκτελέσει ακριβώς ό,τι της δώσαμε.

Στη δεύτερη περίπτωση (όπως και στην πρώτη) το αποτέλεσμα της εργασίας (η εμφάνιση των περιεχομένων του αρχείου log.txt) θα παρουσιαστεί στο STDOUT (standard output), δηλαδή στην οθόνη. Αν εμείς επιθυμούμε να τοποθετηθεί σε μια μεταβλητή, τότε αντί για εισαγωγικά θα χρησιμοποιήσουμε backquotes (``). Τα backquotes πληκτρολογούνται από το πλήκτρο που βρίσκεται αριστερά του 1 και πάνω από το tab.

Στο παρακάτω παράδειγμα τρέχουμε μια εντολή με backquotes και αποθηκεύουμε το περιεχόμενό της σε μια μεταβλητή.

my $myself = `whoami`;

Καλό είναι να είμαστε προσεκτικοί στη χρήση της system, καθώς υποχρεώνει την Perl να ανοίξει ένα ξεχωριστό shell για να τρέξει αυτή την εργασία. Καταναλώνονται λοιπόν αρκετοί πόροι του συστήματος.

Επιστροφή στην Κορυφή

Λαμβάνοντας μέρη μιας web σελίδας

$content =~ m/$url_data_array[1](.*?)$url_data_array[2]/ig;

my $title_data = $1;

$title_data =~ s/<(.*?)>//g;

my $title = $title_data;

$content =~ m/$url_data_array[3](.*?)$url_data_array[4]/ig;

my $body_data = $1;

$body_data =~ s/<(.*?)>//g;

my $body = $body_data;

print "$title\n";

print "$body\n";

}

}

}

close (PREVIOUSURLS);

Επιστροφή στην Κορυφή

Δημιουργία και κλήση υποπρογραμμάτων (subroutines)

Αν και τα προγράμματα των παραδειγμάτων μας είναι μικρά θα βρείτε πολλές φορές χρήσιμη την κατάτμηση ενός μεγάλου προγράμματος σε μικρότερα. Με τον τρόπο αυτό είναι απλούστερο να διορθωθούν λάθη ή να γίνουν επεκτάσεις, ενώ είναι πολύ ευκολότερο να «ανακυκλώσετε» τον κώδικά σας χρησιμοποιώντας τον σε άλλες εφαρμογές:

Ας δούμε ένα σύντομο παράδειγμα:

#!usr/bin/perl -w

use strict;

print "Grapse 1\n";

my $input;

chomp ($input = <STDIN>);

Το πρόγραμμά μας είναι πολύ απλό. Ζητούμε από τον χρήστη να πληκτρολογήσει τον αριθμό 1.

if ($input eq 1)

{

&subroutine1;

}

Αν ο χρήστης πράγματι πληκτρολογήσει 1, τότε θα εκτελεστεί το περιεχόμενο των {}. Μέσα εκεί όμως δεν υπάρχει κώδικας, αλλά μόνο μια κλήση προς το υποπρόγραμμα subroutine1. Το & δηλώνει στην Perl ότι πρέπει να εκτελέσει ό,τι περιέχει αυτό το υποπρόγραμμα. (Η Perl θα καταλάβει τι πρέπει να κάνει και χωρίς αυτό, αλλά για λόγους σωστής δομής καλό είναι να το χρησιμοποιείτε πάντοτε.)

else

{

&subroutine2;

}

Αν ο χρήστης δεν πληκτρολογήσει 1, τότε θα εκτελεστεί το υποπρόγραμμα που ονομάσαμε subroutine2. Το όνομά κάθε subroutine πρέπει να είναι μικρότερο από 255 χαρακτήρες και να περιέχει μόνο γράμματα, αριθμούς και την underscore. Επίσης είναι case sensitive, δηλαδή το $subroutine1 είναι διαφορετικό από το $Subroutine1

Η Perl γνωρίζει πού να βρει αυτά τα υποπρογράμματα. Συνήθως, τα τοποθετούμε στο τέλος του script μετά από όλες τις άλλες γραμμές κώδικα. Το ίδιο κάναμε και εδώ:

sub subroutine1

{

print "Egrapses 1\n";

}

Το sub εξηγεί στην Perl ότι αναφερόμαστε σε ένα υποπρόγραμμα και ακολουθεί ο τίτλος του, ενώ μέσα στα άγκιστρα υπάρχει το περιεχόμενο αυτής της υπορουτίνας (που εδώ φυσικά είναι πάρα πολύ απλό).

sub subroutine2

{

print "Den egrapses 1\n";

}

Η υπορουτίνα 2.

Η γενική σύνταξη ενός υποπρογράμματος είναι:

sub - όνομα προγράμματος - {εργασίες}

Μια από τις σημαντικότερες εφαρμογές των υποπρογραμμάτων αυτών είναι η χρήση τους ως εξειδικευμένες συναρτήσεις. Αν η Perl δεν διαθέτει συνάρτηση για να κάνει κάτι που εσείς χρειάζεστε συχνά, μπορείτε να δημιουργήσετε τη δική σας υπορουτίνα και να την προσθέτετε σε όλα τα προγράμματά σας.

Sub fahr_to_cel

{

$celsius = ($fahrenheight – 32) *5 / 9;

}

Η Perl δεν διαθέτει συνάρτηση για να μετατρέπει τους βαθμούς Fahrenheight σε Κέλσιου. Έτσι εμείς δημιουργήσαμε την παραπάνω subroutine και τη χρησιμοποιούμε σε όποιο πρόγραμμά μας τη χρειάζεται. (Αν φυσικά απαιτείται συχνά από τα προγράμματά μας κάτι τέτοιο.)

Με την τεχνική αυτή «ανακυκλώνουμε» τον κώδικά μας και τον επαναλαμβάνουμε όσες φορές χρειαστεί. Για κώδικα που θα χρησιμοποιηθεί σε ένα μόνο πρόγραμμα η δημιουργία subroutines δεν είναι απαραίτητη. Είναι όμως χρήσιμη γιατί τακτοποιεί τον κώδικά μας και μας είναι ευκολότερο:

  1. Να τον τροποποιήσουμε αργότερα
  2. Να ανακαλύψουμε λάθη σε αυτόν.
Επιστροφή στην Κορυφή

Εισαγωγή στο scope (εφαρμογές σε subroutines)

Το παρακάτω πολύ απλό πρόγραμμα καλεί μια υπορουτίνα (ένα υποπρόγραμμα) και το εκτελεί:

&justaroutine;

sub justaroutine

{

my $test = 'Dokimi';

print "$test\n";

}

Αν όμως το πρόγραμμα ήταν:

&justaroutine;

sub justaroutine {

my $test = 'Dokimi';

}

print "$test\n";

Θα λαμβάναμε (αν χρησιμοποιούσαμε τη use strict) το μήνυμα:

Global symbol "$test" requires explicit package name at now.pl line 7.

Execution of now.pl aborted due to compilation errors.

Αυτό που συνέβη είναι ότι προσπαθήσαμε να τυπώσουμε από το κυρίως πρόγραμμα μια μεταβλητή την $test η οποία ορίστηκε μέσα στο υποπρόγραμμα. Για την Perl κάθε μεταβλητή υπάρχει και έχει ισχύ μόνο για το δικό της block ({}) και όσα βρίσκονται μέσα σε αυτό, αλλά όχι για τα ανώτερα. Λέμε λοιπόν ότι το πρόγραμμα δεν μπορεί να λειτουργήσει γιατί η εκτύπωση κάλεσε μια μεταβλητή έξω από το scope (το εύρος ισχύος) της.

Το ακόλουθο πρόγραμμα όμως θα λειτουργήσει σωστά:

my $test = "Dokimi";

&justaroutine;

sub justaroutine {

print "$test\n";

}

Εδώ η $test βρίσκεται έξω από την υπορουτίνα, άρα μπορεί να κληθεί και να χρησιμοποιηθεί κανονικά.

Σημειώστε πάντως ότι το θέμα του scope έχει εφαρμογή σε κάθε block και όχι μόνο σε υπορουτίνες. Για παράδειγμα, μια μεταβλητή που ορίστηκε μέσα σε ένα while, for, foreach κ.λπ. (οτιδήποτε περικλείεται από άγκιστρα) δεν είναι αναγνωρίσιμη έξω από αυτό. Επίσης, αν μια μεταβλητή έχει οριστεί ως global (είναι εκτός κάποιου block και ισχύει για όλο το πρόγραμμα), αλλά ορίσουμε μέσα σε κάποιο block μια άλλη μεταβλητή με το ίδιο όνομα, τότε η τελευταία θα έχει ισχύ μέσα σε αυτό το block και η άλλη οπουδήποτε αλλού. Τέτοιες πρακτικές όμως καλό είναι να αποφεύγονται, γιατί είναι εύκολο να μπερδευτούμε και να κάνουμε λάθη.

Επιστροφή στην Κορυφή

Λαμβάνοντας τιμές από subroutines

Οι μεταβλητές που ορίζονται και χρησιμοποιούνται μέσα σε μια subroutine δεν μπορούν να χρησιμοποιηθούν «ως έχουν» από το υπόλοιπο πρόγραμμα, αφού αυτό υπερβαίνει το scope τους.

Το:

my $test = 8;

&justaroutine;

sub justaroutine {

$test++;

print "$test\n";

}

θα μας δώσει στην εκτύπωση 9, όπως ακριβώς και το

my $test = 8;

&justaroutine;

sub justaroutine {

$test++;

}

print "$test\n";

Για να το επιτύχουμε αυτό όμως χρειάστηκε να δηλώσουμε την $test έξω από τη subroutine ως global variable (δηλαδή είναι διαθέσιμη από οποιοδήποτε σημείο του προγράμματός μας). Αν έχουμε ένα μεγάλο πρόγραμμα με πολλές μεταβλητές αυτό γίνεται περίπλοκο, δύσχρηστο και αργό. Χρειαζόμαστε λοιπόν έναν τρόπο για να λαμβάνουμε τιμές από μια ή περισσότερες μεταβλητές που βρίσκονται μέσα σε κάποια subroutine. Το δοκιμάσαμε αυτό με το:

&justaroutine;

sub justaroutine {

my $test = 'Dokimi';

}

print "$test\n";

αλλά αποτύχαμε. Θα δουλέψει όμως το:

&justaroutine;

my $subtest = &justaroutine;

sub justaroutine {

my $test = 'Dokimi';

}

print "$subtest\n";

Η εκτύπωση εδώ μας δίνει: Dokimh. Η σημαντική γραμμή είναι η my $subtest = &justaroutine. Αυτό που κάναμε ήταν να πάρουμε την τελευταία τιμή της justaroutine και να τη δώσουμε σε μια εξωτερική (εκτός της υπορουτίνας) μεταβλητή.

Προσοχή όμως:

&justaroutine;

my $subtest = &justaroutine;

sub justaroutine {

my $test = 'Dokimi';

my $test2 = 'Dokimi2';

}

print "$subtest\n";

Η εκτύπωση εδώ μας δίνει Dokimi2. Με άλλα λόγια η υπορουτίνα μας θυμάται και μπορεί να μεταδώσει μόνο την τελευταία τιμή που διαχειρίστηκε.

Αυτή η πρακτική όμως παρουσιάζει προβλήματα: Για παράδειγμα, μπορείτε να μαντέψετε ποιο θα είναι το αποτέλεσμα του παρακάτω προγράμματος;

&justaroutine;

my $subtest = &justaroutine;

sub justaroutine {

my $test = 'Dokimi';

my $test2 = 'Dokimi2';

print "$test\n";

}

print "$subtest\n";

Η εκτύπωση θα δώσει:

Dokimi

Dokimi

1

Τυπώθηκαν δηλαδή 3 πράγματα παρόλο που ζητήθηκαν 2 (Το justaroutine τρέχει μια φορά γιατί το ζητάμε στη δεύτερη γραμμή και μια για να δώσει κάποιο αποτέλεσμα στο my $subtest = &justaroutine). Σε πολλές καταστάσεις (και ειδικά σε loops όπως τα while και for) το τι διαχειρίστηκε τελευταίο η υπορουτίνα είναι σχετικό (π.χ. μπορεί να είναι το condition του loop). Γι’ αυτό και είναι καλύτερα να δηλώνουμε οι ίδιοι στην υπορουτίνα τι θέλουμε να μας παραδώσει. Αυτό γίνεται τοποθετώντας στο τέλος της υπορουτίνας την εντολή return.

&justaroutine;

my $subtest = &justaroutine;

sub justaroutine {

my $test = 'Dokimi';

my $test2 = 'Dokimi2';

print "$test\n";

return $test2;

}

print "$subtest\n";

Εδώ η εκτύπωση θα είναι:

Dokimi

Dokimi

Dokimi2

Περισσότερες τιμές μπορούν να μεταφερθούν μέσω μιας λίστας. Για παράδειγμα:

&justaroutine;

my ($subtest, $subtest2) = &justaroutine;

sub justaroutine {

my $test = 'Dokimi';

my $test2 = 'Dokimi2';

return ( $test, $test2);

}

print "$subtest $subtest2\n";

Εδώ η εκτύπωση θα είναι:

Dokimi Dokimi2

Δυστυχώς από την υπορουτίνα δεν μπορούμε να πάρουμε ολόκληρες arrays, αλλά μόνο scalar μεταβλητές. Τα στοιχεία κάθε λίστας μας λοιπόν θα πρέπει να μετατραπούν σε μεμονωμένες scalar μεταβλητές για να «βγουν» από την υπορουτίνα και μετά να ξαναγίνουν λίστες.

Επιστροφή στην Κορυφή

Δίνοντας τιμές σε μια subroutine – Η @_ array

Μέχρι τώρα καλούσαμε μια υπορουτίνα δίνοντας απλώς το όνομά της μετά το σύμβολο & που είπαμε ότι είναι προαιρετικό. Προαιρετικές επίσης είναι και οι παρενθέσεις που την ακολουθούν. Η υπορουτίνα justaroutine μπορεί να κληθεί ως justaroutine ή &justaroutine ή justaroutine() ή &justaroutine(). Προτιμούμε πάντως το τελευταίο.

Οι παρενθέσεις χρησιμοποιούνται στην περίπτωση που θέλουμε να δώσουμε κάποιες τιμές στην υπορουτίνα μας για να τις χρησιμοποιήσει. Όπως και στην περίπτωση της εξαγωγής τιμών (return) αυτό γίνεται με τη μορφή λίστας και οι τιμές αποθηκεύονται στην default array που είναι η @_. Έτσι έχουμε:

&justaroutine (85,99,138);

my $subtest = &justaroutine(85,99,138);

print "$subtest\n";

sub justaroutine

{

my $sum = $_[0] + $_[1] + $_[2] ;

return $sum;

}

Η justaroutine πήρε τις τιμές 85,99,138 και τις αποθήκευσε στην @_. Εμείς τις αφαιρέσαμε μια μια (μέσω της $_) και τις χρησιμοποιήσαμε. (Η εκτύπωση εδώ μας δίνει 322.)

Το ίδιο αποτέλεσμα έχουμε και στην περίπτωση που το script μας γίνει πιο ευέλικτο, δηλαδή:

my $a = 85;

my $b = 99;

my $c = 138;

&justaroutine ($a,$b,$c);

my $subtest = &justaroutine($a,$b,$c);

κ.λπ.

Ένας γρήγορος τρόπος για να δώσουμε τις εξωτερικές τιμές σε μεταβλητές της υπορουτίνας μας είναι και ο:

sub justaroutine

{

my ($value1, $value2, $value3) = @_;

my $sum = $value1 + $value2 + $value3 ;

return $sum;

}

Αν έχετε δει ποτέ κώδικα άλλων προγραμμάτων Perl (και αν έχετε φτάσει μέχρι εδώ σίγουρα θα έχετε κάνει και άλλες αναζητήσεις μόνοι σας) θα συναντήσατε ίσως συχνά κάποια μυστηριώδη κωδικοποίηση της μορφής $some_variable = shift. Αυτό σημαίνει ότι η μεταβλητή μας παίρνει την πρώτη στη σειρά τιμή της @_ (αν θέλαμε την τελευταία θα δηλώναμε $some_variable = pop). Έτσι, ο κώδικάς μας μπορεί να γίνει και:

sub justaroutine

{

my $value1 = shift;

# Παίρνει την πρώτη τιμή δηλαδή την $a

my $value2 = shift;

# Παίρνει την δεύτερη τιμή δηλαδή την $b

my $value3 = shift;

# Παίρνει την τρίτη τιμή δηλαδή την $c

my $sum = $value1 + $value2 + $value3 ;

return $sum;

}

Φυσικά αντί να δίνουμε απόλυτες τιμές (π.χ. $_[1]) πράγμα προβληματικό για μεγάλο αριθμό δεδομένων μπορούμε να δουλέψουμε με κάποιο loop αν αυτό είναι δυνατό. Έτσι, το παραπάνω παράδειγμα γίνεται:

my $a = 85;

my $b = 99;

my $c = 138;

&justaroutine ($a,$b,$c);

my $subtest = &justaroutine($a,$b,$c);

print "$subtest\n";

sub justaroutine

{

my $sum = 0 ;

foreach my $temp (@_)

{

$sum = $sum + $temp;

# ή $sum += $temp; αν προτιμάτε τη συντομογραφία

}

return $sum;

}

Επιστροφή στην Κορυφή

Η χρήση της local

Είδαμε ότι αν δηλώσουμε μια μεταβλητή χρησιμοποιώντας το my στο κύριο σώμα του προγράμματός μας, αυτή θα είναι διαθέσιμη και σε όλα τα blocks που αυτό περιλαμβάνει. Αν όμως δηλώσουμε με το my μια μεταβλητή σε κάποιο block, αυτή δεν θα είναι διαθέσιμη σε όσα blocks περιέχει αυτό.

Στον ακόλουθο κώδικα καλούμε ένα block (το justaroutine) που με τη σειρά του καλεί ένα άλλο (το insideroutine):

#!/usr/bin/perl -w

# use strict;

my $a = 85;

my $b = 99;

my $c = 138;

&justaroutine ($a,$b,$c);

sub justaroutine

{

my ($value1, $value2, $value3) = @_;

my $sum = $value1 + $value2 + $value3 ;

&insideroutine();

}

sub insideroutine

{print "$sum\n";}

Η υπορουτίνα insideroutine προσπαθεί να τυπώσει τη μεταβλητή $sum που ορίσαμε μέσα στην justaroutine με το my και αποτυγχάνει (το μήνυμα λάθους είναι «Name "main::sum" used only once: possible typo…»)

Ακολουθεί το ίδιο ακριβώς πρόγραμμα με τη διαφορά πως αντί για my $sum εδώ έχουμε local $sum. Με τον τρόπο αυτό η Perl θα «θυμάται» (θα αναγνωρίζει) αυτή τη μεταβλητή και σε όσα blocks περιέχονται στην insideroutine. (Υπενθυμίζουμε πως block θεωρούμε οτιδήποτε περικλείεται από άγκιστρα και όχι μόνο τις υπορουτίνες. Παρουσιάζουμε όμως παραδείγματα με υπορουτίνες διότι είναι πιο εύκολο να γίνουν κατανοητά.)

#!/usr/bin/perl -w

# use strict;

my $a = 85;

my $b = 99;

my $c = 138;

&justaroutine ($a,$b,$c);

sub justaroutine

{

my ($value1, $value2, $value3) = @_;

local $sum = $value1 + $value2 + $value3 ;

&insideroutine();

}

sub insideroutine

{print "$sum\n";}

Εδώ η εκτύπωση θα δώσει 322.

Επιστροφή στην Κορυφή

Arrays μέσα σε Arrays

Μια array δεν αποτελεί απλώς ένα σύνολο από scalar μεταβλητές, αλλά μπορεί να περιέχει ως στοιχεία της άλλες πλήρεις arrays. Για παράδειγμα:

my @AinA = (

['Luke', 'Anakin', 'Shmi' ],

['Yoda', 'Obi Wan Kenobi'],

['Sidious', 'Maul', 'Vader']

);

Η AinA είναι μια array αποτελούμενη από τρία στοιχεία κάθε ένα από τα οποία είναι και μια ξεχωριστή array. Λέμε λοιπόν ότι η AinA αποτελεί μια δισδιάστατη array (ονομάζεται και matrix). Όπως φαντάζεστε δεν υπάρχει λόγος να περιοριστούμε σε δισδιάστατες arrays, αλλά μπορούμε να προσθέσουμε και περισσότερες διαστάσεις.

print "$AinA[0][1] \n";

Εδώ ζητούμε την εκτύπωση του δεύτερου στοιχείου της πρώτης array. Δηλαδή του Anakin.

print "$AinA[1][1] \n";

Εδώ ζητούμε την εκτύπωση του δεύτερου στοιχείου της δεύτερης array. Δηλαδή του Obi Wan Kenobi.

Φυσικά στις περισσότερες περιπτώσεις η πρόσθεση τιμών σε μια πολυδιάστατη array δεν θα γίνεται με πληκτρολόγηση με το χέρι όπως στο παραπάνω παράδειγμα, αλλά αυτόματα. Ας υποθέσουμε ότι διαθέτουμε το αρχείο droids.txt με το ακόλουθο περιεχόμενο:

C-3PO:Tatooine:protocol droid:Rebel Alliance

R2-D2:Naboo:astromech droid:Rebel Alliance

FX-7:N/A:Medical assistant:Rebel Alliance

IT-O:Empire:Interrogator droid:Empire

droideka:Colicoids:Destroyer droid:Trade Federation

Πρόκειται για ένα text delimited αρχείο όπου κάθε γραμμή αναφέρεται σε ένα ξεχωριστό robot (droid) απ’ όσα εμφανίζονται στο Star Wars. Τα χαρακτηριστικά που περιλαμβάνει κάθε γραμμή είναι όνομα, προέλευση, ειδικότητα και οργανισμός που το χρησιμοποιεί. Μεταξύ των χαρακτηριστικών χρησιμοποιείται ο χαρακτήρας : για να τα ξεχωρίζει (delimiter).

Αυτό που θέλουμε είναι να δημιουργήσουμε μια array που κάθε τιμή της να είναι και μια ξεχωριστή array με όλα τα δεδομένα ενός droid. Με άλλα λόγια θέλουμε να δημιουργήσουμε το:

@droid_data = (

[‘C-3PO’,’Tatooine’,’protocol droid’,’Rebel Alliance’]

[‘R2-D2’,’Naboo’,’astromech droid’,’Rebel Alliance’]

[‘FX-7’,’N/A’,’Medical assistant’,’Rebel Alliance’]

[‘IT-O’,’Empire’,’Interrogator droid’,’Empire’]

[‘droideka’,’Colicoids’,’Destroyer droid’,’Trade Federation’]

);

Θέλουμε όμως η εργασία να γίνει αυτόματα μια και συνήθως τα δεδομένα μας θα είναι πάρα πολλά για να πληκτρολογηθούν με το χέρι από την αρχή. Ο κώδικας για να επιτευχθεί η αυτόματη εισαγωγή είναι:

open (STAR_WARS_DROIDS, 'droids.txt') or die "can't open droids.txt $!";

my @droid_data;

while (<STAR_WARS_DROIDS>)

Ανοίξαμε το αρχείο και με μια while αρχίζουμε να το διαβάζουμε γραμμή γραμμή.

{

my @temp = split (/:/, $_);

Δημιουργούμε μια προσωρινή array με το όνομα @temp και κάνουμε split τα δεδομένα κάθε γραμμής έτσι ώστε να «μπουν» στην @temp ως ξεχωριστά στοιχεία. Για παράδειγμα η πρώτη @temp θα είναι η 9‘C-3PO’,’Tatooine’,’protocol droid’,’Rebel Alliance’).

push (@droid_data, [@temp]);

}

Κάθε @temp τοποθετείται ολόκληρη μέσα στην @droid_data. Όταν η while έχει επεξεργαστεί όλες τις γραμμές του αρχείου η @droid_data θα είναι πλήρης.

print "$droid_data[0][0] $droid_data[0][3] \n";

print "$droid_data[1][0] $droid_data[1][3] \n";

print "$droid_data[2][0] $droid_data[2][3] \n";

print "$droid_data[3][0] $droid_data[3][3] \n";

print "$droid_data[4][0] $droid_data[4][3] \n";

Η εκτύπωση αυτή θα μας δώσει τα ονόματα των droids και τον οργανισμό για τον οποίον λειτουργούν:

C-3PO Rebel Alliance

R2-D2 Rebel Alliance

FX-7 Rebel Alliance

IT-O Empire

droideka Trade Federation

Αυτό που δεν μπορούμε να κάνουμε είναι να τυπώσουμε ολόκληρη την @droid_data. Το print "@droid_data\n" θα μας δώσει:

ARRAY(0x177f100) ARRAY(0x177f1e4) ARRAY(0x1775598) ARRAY(0x1775628) ARRAY(0x1771e9c) ARRAY(0x1771ee4) ARRAY(0x1771efc) ARRAY(0x1771f14) ARRAY(0x176f44c) ARRAY(0x176f470) ARRAY(0x176f494)

Αυτές είναι οι παραπομπές (references) που τηρεί εσωτερικά η Perl για τα δεδομένα που διαχειρίζεται. Για να εκτυπώσουμε τα ίδια τα δεδομένα θα πρέπει να χρησιμοποιήσουμε ένα loop:

my $row;

for $row (@droid_data)

{

print "@$row\n";

}

Επιστροφή στην Κορυφή

Hashes που περιέχουν άλλες Arrays

Μια όπως και στην περίπτωση των Arrays, μια Hash δεν αποτελεί απλώς ένα σύνολο από scalar μεταβλητές, αλλά μπορεί να περιέχει ως στοιχεία της άλλες πλήρεις arrays. Για παράδειγμα:

Επιστροφή στην Κορυφή

my %AinH = (

Skywalkers => ['Luke', 'Anakin', 'Shmi' ],

Jedis => ['Yoda', 'Obi Wan Kenobi'],

Darths => ['Sidious', 'Maul', 'Vader']

);

Εδώ τα keys είναι Skywalkers, Jedis, Darths και values τα περιεχόμενα των arrays.

Για την προσθήκη μιας ακόμη array θα δηλώσουμε:

$AinH{Droids} = [ 'C-3PO', 'R2-D2', 'FX-7'];

Για την προσθήκη ενός στοιχείου σε μια ήδη υπάρχουσα array έχουμε:

my $temp= 'new member';

my %AinH = (

Skywalkers => ['Luke', 'Anakin', 'Shmi' ],

Jedis => ['Yoda', 'Obi Wan Kenobi'],

Darths => ['Sidious', 'Maul', 'Vader']

);

push @{$AinH{Skywalkers}}, $temp;

και θα έχουμε πλέον:

Skywalkers => ['Luke', 'Anakin', 'Shmi', 'new member' ]

Η εκτύπωση

print "$AinH{Jedis}[1] \n";

print "$AinH{Droids}[0] \n";

μας δίνει:

Obi Wan Kenobi

C-3PO

Για να τυπώσουμε όλο το περιεχόμενο μιας hash of arrays όπου κάθε array περιέχει 3 elements θα δώσουμε:

my @keys = keys(%contents);

foreach my $key (@keys)

{

print "$key\n";

print "$contents{$key}[0]\n";

print "$contents{$key}[1]\n";

print "$contents{$key}[2]\n"

Το print %contents δεν θα λειτουργήσει σωστά (ως values θα τυπωθούν οι references των arrays)

Για να πάρουμε το περιεχόμενο μιας μόνο από τις arrays που περιέχει η hash θα δηλώσουμε:

my $test = $AinH{Jedis};

Αυτό μας δίνει τη reference της array

my @test2 = @$test;

print "@test2\n";

Η εκτύπωση μας δίνει:

Yoda Obi Wan Kenobi

Τέλος για να αφαιρέσουμε το περιεχόμενο ενός στοιχείου μιας array που βρίσκεται μέσα σε hash θα δηλώσουμε:

my $last_array_element = pop @{$AinH{Jedis} }

Αυτό αφαιρέσει την τιμή Obi Wan Kenobi

Επιστροφή στην Κορυφή

Hashes που περιέχουν άλλες Hashes

Στο παράδειγμα που ακολουθεί η hash HofH περιέχει ονόματα ηπείρων ως keys, ενώ η value κάθε ενός από αυτά τα keys περιέχει μερικές hashes που έχουν ως keys τα ονόματα χωρών και ως values τις πρωτεύουσές τους:

my %HofH = (

Europe => {

Italy => "Rome",

Germany => "Berlin",

Spain => "Madrid",

},

Asia => {

Japan => "Tokyo",

"South Corea" => "Seoul",

},

Africa => {

Egypt => "Kairo",

Soudan => "Hartum",

Nigeria => "Lagos",

},

);

Για να προσθέσουμε μια καινούρια τιμή στην % HofH θα δηλώσουμε:

$HofH {America} = {

Mexico => "Mexico City",

Argentina => "Buenos Aires",

Peru => "Lima",

};

Η μεταβολή μιας value γίνεται με τον ακόλουθο τρόπο:

$HofH{Africa}{Soudan} = "Khartoum";

Για την εκτύπωση μιας value έχουμε:

print "$HofH{Africa}{Soudan}\n";

Για να βρούμε τα keys της αρχικής hash έχουμε:

my @keys = keys(%HofH);

Αυτό μας δίνει: Africa Asia Europe America

Για να δούμε τα keys κάθε μιας από τις εσωτερικές hashes θα χρειαστεί να χρησιμοποιήσουμε μια foreach:

foreach my $key (@keys)

{

my @hash_keys = keys %{$HofH{$key}};

print "@hash_keys\n";

}

Εδώ χρησιμοποιήσαμε την print γιατί θέλουμε απλώς να δούμε τα keys. Η εκτύπωση μας δίνει:

Soudan Egypt Nigeria

South Corea Japan

Spain Italy Germany

Argentina Mexico Peru

Επιστροφή στην Κορυφή

Παραδείγματα διαχείρισης δεδομένων από text files

Ας υποθέσουμε ότι μας έδωσαν το αρχείο δεδομένων kairos.txt που περιέχει τα ακόλουθα:

Athens:-4:40:

Thessaloniki:-7:35:

Patra :19:80:

Larrisa:04:16:

Volos:32:55:

Karditsa:12:50:

Psaxna:14:32:

Kamena Vourla :10:20:

Κάθε γραμμή εδώ αναφέρει το όνομα της πόλης, τη χαμηλότερη και τη μεγαλύτερη θερμοκρασία της. Το αρχείο μας είναι ASCII delimited και delimiter είναι το :

Αυτό που θέλουμε να κάνουμε είναι να τυπώσουμε τα περιεχόμενα του αρχείου υπολογίζοντας ταυτόχρονα και τον μέσο όρο. Ας δούμε πώς:

#!/usr/bin/perl -w

use strict;

print " Kalosirthate \n";

open (FILE,'kairos.txt') || die "Den mporo na to anoikso\n $!" ;

Κατά τα γνωστά.

while (<FILE>)

{

my @temaxio = split (/:/,$_);

Η Perl διαβάζει το αρχείο γραμμή γραμμή (μια κάθε φορά) και τοποθετεί το περιεχόμενο στη scalar μεταβλητή $_. Εμείς σπάμε αυτή τη scalar σε μια λίστα με σημείο χωρισμού τον delimiter (το : ). Έτσι κάθε τιμή βρίσκεται τώρα σε ένα ξεχωριστό στοιχείο της array @temaxio

my $mesos = ($temaxio[1]+$temaxio[2])/2;

Υπολογίζουμε τον μέσο όρο.

print " H $temaxio[0] me mikri $temaxio[1] kai megali $temaxio[2] exei meso oro $mesos\n";

Τυπώνουμε το αποτέλεσμα της γραμμής.

}

Το loop ολοκληρώνεται και ξαναρχίζει από την αρχή με την επόμενη γραμμή του kairos.txt

Λίγο διαφορετική θα είναι η προσέγγισή μας αν το αρχείο (που τώρα λέγεται kairos2.txt) έχει την ακόλουθη (επίσης πολύ συνηθισμένη μορφή):

Athens

-4

40

Thessaloniki

-7

35

Patra

19

80

Larrisa

04

16

Volos

32

55

Karditsa

12

50

Psaxna

14

32

Kamena Vourla

10

20

Εδώ το πρόγραμμά μας θα είναι:

#!/usr/bin/perl -w

use strict;

print " Kalosirthate \n";

open (FILE,'kairos2.txt') || die "Den mporo na to anoikso\n $!" ;

Κατά τα γνωστά.

my $count ;

Η μεταβλητή αρίθμησης. Με αυτή θα παρακολουθούμε σε ποια σειρά του αρχείου βρισκόμαστε.

my $polis;

Όνομα πόλης.

my $max;

Μέγιστη θερμοκρασία.

my $min;

Ελάχιστη θερμοκρασία

while (<FILE>)

{

$count++;

Η $count αυξάνει κατά μια μονάδα.

if ($count == 1)

{

$polis = $_;

Αν η $count είναι ίση με 1 (προσοχή στα δύο ίσον) τότε η $_ περιέχει το όνομα της πόλης.

chomp $polis;

}

Πιθανόν να έχει πατηθεί Enter μετά την εισαγωγή του ονόματος της πόλης. Γι’ αυτό το καθαρίζουμε με το chomp.

elsif ($count == 2)

{

$min = $_;

chomp $min;

}

Αν η $count είναι ίση με 2 τότε βρισκόμαστε παρακάτω (έχει ήδη προηγηθεί άλλο πέρασμα του loop) και η $_ περιέχει την ελάχιστη θερμοκρασία.

else

{

$max = $_;

chomp $max;

Διαφορετικά η $_ μας εμφανίζει τη μέγιστη θερμοκρασία.

my $mesos = ($min+$max)/2;

print " H $polis me mikri $min kai megali $max exei meso oro $mesos\n";

Αν συμβαίνει αυτό τότε έχουμε ήδη όσα στοιχεία χρειαζόμαστε για να υπολογίσουμε τον μέσο όρο θερμοκρασίας αυτής της πόλης.

$count =0;

}

Η τιμή της $count γίνεται πάλι μηδέν και ξεκινάμε με την επόμενη γραμμή που περιέχει το όνομα (και παρακάτω τα στοιχεία) μιας άλλης πόλης.

}

Το πρόγραμμά μας έχει τώρα ολοκληρωθεί.

Επιστροφή στην Κορυφή

Πώς τοποθετούμε τα περιεχόμενα ενός ολόκληρου αρχείου μέσα σε μια scalar

Η Perl έχει μια σειρά από ειδικές μεταβλητές που ξεκινούν από $ και καλύπτουν διάφορες εργασίες. Για την ανάγνωση ενός αρχείου θεωρεί ότι κάθε σειρά αποτελεί και ένα διαφορετικό στοιχείο μιας λίστας. Γι’ αυτό και μπορούμε να βάλουμε εύκολα τα περιεχόμενα ενός αρχείου μέσα σε μια array π.χ. my @file_contents = (<FILE>)

undef $/;

Για να βάλουμε όλο το περιεχόμενο ενός αρχείου μέσα σε μια scalar πρέπει να «κλείσουμε» τον Input record separator ($/) που διαβάζει τις γραμμές ως ξεχωριστά στοιχεία μιας array.

open (FILE, 'test.txt') or die "can't open test.txt $!";

my $content = <FILE>;

Στη συνέχεια, απλώς τοποθετούμε τα περιεχόμενα του αρχείου στη scalar.

Προσοχή! Καλύτερα να χρησιμοποιείτε αυτή τη λειτουργία μέσα σε μια subroutine. Έτσι, δεν θα αλλάζετε τον default τρόπο με τον οποίο συμπεριφέρεται η Perl στο υπόλοιπο πρόγραμμά σας.

Ένας άλλος πιο απλός τρόπος για να πετύχουμε το ίδιο πράγμα είναι ο ακόλουθος:

open (FILE, ‘test.txt) or die "can't open test.txt $!";

my @contents = (<FILE>);

my $all_contents = join ("\n", @contents);

Δηλαδή τοποθετήσαμε κάθε γραμμή ως ξεχωριστό στοιχείο μιας λίστας και μετά ενώσαμε όλα τα στοιχεία της λίστας με τη join χρησιμοποιώντας ως διαχωριστικό τις αλλαγές γραμμών.

Επιστροφή στην Κορυφή

Last, Next, Redo

Η last χρησιμοποιείται για να τερματίσει ένα block. Μετά την last η Perl θα διακόψει τη συγκεκριμένη αλληλουχία (π.χ. μια while) και θα συνεχίσει στην εκτέλεση του υπόλοιπου προγράμματος.

Η next διακόπτει το τρέχον loop και το ξαναρχίζει από την αρχή.

Η redo διακόπτει το τρέχον loop και το ξαναρχίζει από την αρχή. Αντίθετα από τη next όμως δεν ελέγχει την αρχική συνθήκη. Ας δούμε ένα παράδειγμα:

my $input = 8;

until ($input ==5)

{

print "Type 1 or 2 or something\n";

my $input = <STDIN>;

chomp ($input);

Δίνουμε μια αρχική τιμή στην $input για να μπορέσει να τρέξει το until loop. Μέχρι τώρα η $input να πάρει την τιμή 5 εμείς θα δίνουμε κάποια στοιχεία και το πρόγραμμα θα ανταποκρίνεται ανάλογα.

if ($input == 1)

{

print "You typed 1\n";

last;

}

Αν ο χρήστης πληκτρολογήσει 1 τότε θα τυπωθεί το σχετικό μήνυμα και το until loop θα διακοπεί. Θα τυπωθεί δηλαδή το «Eyxaristoyme gia th dokimh toy programmatos» (που βρίσκεται εκτός του until loop στην τελευταία σειρά) και μετά το πρόγραμμα θα τερματίσει.

elsif ($input == 2)

{

print "You typed 2\n";

next;

}

Αν ο χρήστης πληκτρολογήσει 1 τότε θα τυπωθεί το σχετικό μήνυμα και το until loop θα ξαναρχίσει από την αρχή.

else

{

print "You typed $input\n";

redo;

}

Ό,τι άλλο και να εκτυπώσει ο χρήστης θα εμφανιστεί από την print και μετά θα ξανατρέξει το until loop. Προσέξτε ότι η συνθήκη του until μας λέει πώς αν το $input είναι 5 τότε το loop πρέπει να διακοπεί. Αν τρέξετε όμως το πρόγραμμα θα παρατηρήσετε πως ακόμη και με $input 5 ο πρόγραμμα συνεχίζει να τρέχει μια και η redo ξανατρέχει το loop χωρίς να ελέγξει την αρχική συνθήκη.

}

print "Eyxaristoyme gia th dokimh toy programmatos\n";

Το πρόγραμμα ολοκληρώνεται.

Επιστροφή στην Κορυφή

Loop labels

Μερικές φορές αυτό που θέλουμε από το πρόγραμμά μας είναι να διακόψει όχι μόνο το τρέχων loop (πράγμα που επιτυγχάνεται με την last) αλλά και κάποιο άλλο. Για να δηλώσουμε όμως στην Perl ποιο loop πρέπει να διακόψει χρειάζεται να τα έχουμε «βαφτίσει», να τους έχουμε δώσει δηλαδή ετικέτες. Οι ετικέτες γράφονται με κεφαλαία και ακολουθούνται από άνω και κάτω τελεία. Μπορούν δεν να έχουν οποιοδήποτε όνομα εκτός από BEGIN και END. Η γενική σύνταξη είναι:

ETTIKETA: while (κάτι) {κάνε αυτό}

Ας δούμε ένα παράδειγμα:

my $input = 8;

FIRST: while ($input ne '5')

{

Η πρώτη μας while ονομάστηκε FRIST.

print "Type 1 or 5 (to stop) or something else\n";

$input = <STDIN>;

chomp ($input);

Αλλάζουμε την τιμή της $input

if ($input == 1)

{

print "Type a number:\n";

my $input2 = <STDIN>;

chomp ($input2);

Αν η $input είναι ίση με 1 τότε δημιουργούμε την $input2 και της δίνουμε μια τιμή.

SECOND: until ($input2 == 3000)

{

Δημιουργούμε ένα δεύτερο loop και το ονομάζουμε SECOND

if ($input2 > 100)

{last FIRST;}

Αν $input2 > 100 τότε διακόπτουμε το while loop και φυσικά τελειώνει το πρόγραμμα.

else

{last SECOND;}

Διαφορετικά διακόπτουμε το until loop και το while ξαναρχίζει από την αρχή.

}

}

else

{

print "You typed $input\n";

}

}

print "Eyxaristoyme gia th dokimh toy programmatos\n";

Επιστροφή στην Κορυφή

References (παραπομπές)

Όλοι γνωρίζουμε τις παραπομπές του Web. Αν κάποια σελίδα περιέχει κάτι ενδιαφέρον μπορούμε, είτε να στείλουμε ολόκληρη τη σελίδα σε κάποιον φίλο ή φίλη μας, είτε να του δώσουμε απλώς τη διεύθυνσή της και να του πούμε: «Πήγαινε εκεί και δες μόνος σου». Επειδή κάθε μεταβλητή (scalar, array ή hash) αποθηκεύει τα περιεχόμενά της σε μια συγκεκριμένη διεύθυνση η Perl διαθέτει μια λειτουργία αντίστοιχη των παραπομπών του Web χάρη στην οποία μπορούμε να γνωρίζουμε τη διεύθυνση των μεταβλητών μας και να διαχειριζόμαστε αυτή την πληροφορία αντί για την ίδια τη μεταβλητή.

my $scalar_test = ' Just a test';

Η my δημιουργεί τη μεταβλητή $scalar_test (και έτσι αποκτά τη δική της διεύθυνση), ενώ το $scalar_test = ' Just a test' ορίζει τα περιεχόμενα της μεταβλητής αυτής.

my $scalar_ref = \$scalar_test;

Η μεταβλητή $scalar_ref περιέχει τη διεύθυνση της $scalar_test (όλες οι references είναι scalar μεταβλητές).

my @array_test = ('one','two','three');

my $array_ref = \@array_test;

Ίδιο με το παραπάνω αλλά για arrays.

my %hash_test = ( 'giorgos' => 'brother', 'sophia' => 'sister');

my $hash_ref = \%hash_test;

Ίδιο με το παραπάνω αλλά για hashes.

print "$scalar_ref\n";

print "$array_ref\n";

print "$hash_ref\n";

Μπορούμε να τυπώσουμε τα περιεχόμενα των reference scalars αλλά θα μας δώσουν απλώς τις διευθύνσεις μνήμης των μεταβλητών στο συγκεκριμένο μηχάνημα τη συγκεκριμένη στιγμή (αλλάζουν ανάλογα με τις διαθέσιμες θέσεις μηνήμης):

SCALAR(0x1775648)

ARRAY(0x177569c)

HASH(0x1771da0)

Για να αποκτήσουμε πρόσβαση στο περιεχόμενο μιας reference θα χρησιμοποιήσουμε την ακόλουθη σύνταξη:

my $scalar_content = $$scalar_ref;

my @array_content= @$array_ref;

my %hash_content = %$hash_ref;

Τονίζουμε και πάλι ότι η παραπομπή αναφέρεται απλώς σε μια διεύθυνση μνήμης και όχι στο περιεχόμενο της μεταβλητής. Μπορείτε να φανταστείτε ποιο θα είναι το αποτέλεσμα της εκτύπωσης του ακόλουθου κώδικα;

my $scalar_test = 'Just a test';

my $scalar_ref = \$scalar_test;

$scalar_test = 'Just a test 2';

my $scalar_content = $$scalar_ref;

print "$scalar_content\n";

Φυσικά Just a test 2. Παρά το γεγονός ότι η μεταβλητή άλλαξε, η παραπομπή έμεινε η ίδια και όταν ζητήθηκαν τα περιεχόμενα της παραπομπής, αυτή πήρε την τρέχουσα τιμή της $scalar_test.

Γιώργος Επιτήδειος © 2005


Επιστροφή στα Μαθήματα γλώσσας προγραμματισμού Perl

Επιστροφή στον Ελληνικό Οδηγό Χρήσης Internet