Standard Είσοδος και Έξοδος δεδομενων.
Στην Perl η είσαγωγή και η έξαγωγή των δεδομένων μπορεί να γίνει με διάφορους τρόπους. H default/standard επιλογή και για τα δύο είναι η οθόνη. Έτσι όταν ζητάμε σε ένα πρόγραμμα μια εκτύπωση ενός μηνήματος ή μιας μεταβλητής:
print "Hello World\n";
αυτό εμφανίζεται στην οθόνη (που θα δείτε σε διάφορα βιβλία και ως STDOUT = standard output).
Mε τον ίδιο τρόπο ένα πρόγραμμα Perl μπορεί να δεχτεί δεδομένα από το πλήκτρολόγιό μας μέσω της οθόνης. Για να γίνει αυτό χρειάζεται μέσα σε ένα πρόγραμμα η εκχώρηση της τιμής μιας μεταβλητής να δηλώνεται με την παράσταση <STDIN>. Το STDIN σημαίνει εδώ standard input, αναφέρεται στο τερματικό (την οθόνη) και εισάγεται με τον τελεστή <> (diamond) που συμβολίζει την ανάγνωση δεδομένων από το στοιχείο που περιέχεται μέσα στα < και >. Ένα παράδειγμα ανάγνωσης μιας μεταβλητής από την οθόνη είναι:
#! /usr/bin/perl
use warnings;
print "give me value of a,\n"; # we need a message to tell the user that he has to provide input
$a=<STDIN>; # reading a value from the screen. Users needs to press Enter/Return after the value
print $a,"\n"; # prints $a to the screen. You don't need to specify STDOUT as it is a default
Αρχεία/Filehandles
Οι περιπτώσεις στις οποίες τα δεδομένα μπορούν να εισαχθούν από την οθόνη είναι ελάχιστες και με μικρή σημασία. Τα περισσότερα προγράμματα που θα χρειαστεί να γράψουμε θα πρέπει να μπορούν να προσπελάσουν αρχεία που θα περιέχουν έναν μεγάλο όγκο δεδομένων. Επιπλέον η εκτύπωση των αποτελεσμάτων στην οθόνη πέρα από αμεσότητα προσφέρει ελάχιστα. Πολύ συχνά θα θέλουμε τόσο να κρατήσουμε σε κάποιο αρχείο το αποτέλεσμα της εκτέλεσης ενός προγράμματος, ενώ άλλες φορές θα χρειαστεί το αποτέλεσμα ενός προγράμματος να χρησιμοποιηθεί σαν input για ένα άλλο. Με λίγα λόγια είναι καλό να μάθουμε να γράφουμε προγράμματα που θα χρησιμοποιούν αρχεία τόσο για την εισαγωγή όσο και την εξαγωγή δεδομένων. Τα αρχεία στην Perl καθορίζονται ως αρχεία ανάγνωσης, εγγραφής ή ανανέωσης με βάση τη χρήση συγκεκριμένων συμβόλων "τελεστών" κατά την αρχική τους δήλωση. Η δήλωση αυτή γίνεται με την εντολή open().
Η σύνταξη της εντολής είναι της μορφής:
open INTERNALFILENAME, "operator(>,<,>>) externalfilename"
για παράδειγμα
open SEQ, "<sequence.fa";
Στην πιο πάνω εντολή:
Mια εναλλακτική σύνταξη της open είναι η παρακάτω:
open($intfile, '<', "sequence.fa");
Ωστόσο θεωρούμε πως η πιο πάνω είναι προτιμότερη καθώς με την αναφορά στα αρχεία ως references (με όλα τα γράμματα κεφαλαία) γίνεται ασφαλέστερα η διάκριση ανάμεσα στις μεταβλητές και στα filehandles, τις μεταβλητές δηλαδή αρχείων.
Στην περίπτωση τώρα που επιθυμούμε να ανοίξουμε αρχείο για εγγραφή (εξαγωγή) δεδομένων, η σύνταξη αλλάζει μόνο στον τελεστή:
open OUT, ">output.dat";
Το < δηλαδή γίνεται >. Σε αυτήν την περίπτωση το αρχείο εγγραφής μπορεί είτε να υπάρχει με αυτό το όνομα, είτε να μην υπάρχει οπότε θα δημιουργηθεί. Στην περίπτωση που υπάρχει χρειάζεται ιδιαίτερη προσοχή καθώς και μόνο η εκτέλεση της εντολής open θα σβήσει όλα του τα περιεχόμενα, προετοιμάζοντάς το για νέα εγγραφή. Προσοχή πάντοτε στο άνοιγμα αρχείων εγγραφής.
Στην περίπτωση που θέλουμε να ανοίξουμε ένα αρχείο που υπάρχει ήδη αλλά να μην χάσουμε ταπεριεχόμενα του και απλώς να το ενημερώσουμε προσθέτοντας νέα αποτελέσματα στο τέλος τους τότε απλώς αντί του μονού τελεστή εγγραφής > χρησιμοποιούμε τον διπλο >>.
οpen OUT1, ">>moreoutput.dat";
Οι παραπάνω εντολές αναφέρονται στο άνοιγμα αρχείων, στην αρχική δήλωση δηλαδή αρχείων από τα οποία μπορεί να γίνει ανάγνωση και εγγραφή. Οι καθαυτές διαδικασίες της ανάγνωσης και της εγγραφής περιγράφονται στη συνέχεια.
Ανάγνωση από αρχείο
H ανάγνωση από αρχείο γίνεται ουσιαστικά με τρόπο απολύτως ανάλογο αυτού της ανάγνωσης από την οθόνη. Με τον ίδιο τρόπο δηλαδή που για να διαβάσουμε από την οθόνη γράφουμε:
$a=<STDIN>;
για να διαβάσουμε από ένα αρχείο τοποθετούμε το filehandle εντός του <>. Στο πιο πάνω παράδειγμα για να διαβάσουμε από το sequence.fa (που αποδώσαμε στο filehandle SEQ) θα γράψουμε:
$a=<SEQ>;
Υπάρχει όμως κάτι που πρέπει να προσέξουμε εδώ. Θυμηθείτε πως στο παράδειγμα της οθόνης η εισαγωγή δεδομένων θεωρείται ολοκληρωμένη με την προσθήκε ενός "Εnter" στο τέλος των δεδομένων. Το "Enter" αντιστοιχεί πρακτικά στην αλλαγή γραμμής σε ένα αρχείο, έτσι η εντολή
$a=<SEQ>;
εξ ορισμού θα δεχθεί input μέχρι το πρώτο "Εnter" κάτι που σημαίνει πως ανεξαρτήτως του μεγέθους και της δομής του αρχείου θα διαβαστεί από αυτό μόνο η πρώτη γραμμή. Οτιδήποτε ακολουθεί μετά το πρώτο "Enter" θα αγνοηθεί.
Πώς θα διαβάσουμε όλο το αρχείο τότε; Για τον σκοπό αυτό θα πρέπει να χρησιμοποιήσουμε μια δομή επανάληψεις (δείτε αντίστοιχο κεφάλαιο), θα πρέπει δηλαδή να δώσουμε στο πρόγραμμα την εντολή να επαναλάβει τη διαδικασία ανάγνωσης μέχρι το "Enter" για όλο το αρχείο, γραμμή-γραμμή, να διαβαστεί δηλαδή το αρχείο στο σύνολό του σε κομμάτια που θα διαχωρίζονται από αλλαγές γραμμής. Αυτό είναι σχετικά απλό εφόσον ανατρέξετε στο κεφάλαιο των βρόχων επανάληψεις (Loop Structures) και χρησιμοποιήσετε την εντολή while () {}. Η συγκεκριμένη εντολή δεν προϋποθέτει τη γνώση του μεγέθους του αρχείου και απλώς ζητά από το πρόγραμμα να διαβάζει γραμμή-γραμμή το αρχείο SEQ μέχρι να φτάσει στο τέλος του. Η σειρά των εντολών:
open SEQ, "<sequence.fa";
while($a=<SEQ>){
print $a;
}
Ουσιαστικά θα ξανατυπώσει το σύνολο του αρχείου SEQ στην οθόνη, γραμμή-γραμμή. Για την ακρίβεια θα τυπώνει την κάθε γραμμή μαζί με τον τελικό "\n" χαρακτήρα που κωδικοποιείται από το "Enter" εντός του αρχείου και γι' αυτό τον λόγο δεν χρειάζεται να τον ενσωματώσουμε στην εντολή print.
Εγγραφή σε αρχείο
Mε τρόπο ανάλογο του open η εγγραφή δεδομένων σε αρχείο γίνεται με τη χρήση της εντολής print με τη διαφορά ότι αντί για την "γυμνή" προσθήκη των μεταβλητών/δεδομένων θα πρέπει να αναφέρεται το αρχείο εγγραφής με τη μορφή του filehandle:
print OUT $a;
Στην περίπτωση του print δεν χρειάζεται κόμμα ή κάποιο άλλο σημείο στίξης. Η σύνταξη
print filehandle [variables];
είναι αρκετή.
Να σημειωθεί επίσης πως ο τρόπος με τον οποίον θα τυπωθούν οι μεταβλητές είναι εδώ καθαρά θέμα επιλογής της σύνταξης. Αλλαγές γραμμών εισάγονται μόνο μέσα από τη σύνταξη (εκτός αν είναι μέρος της ίδιας της μεταβλητής). Το ίδιο ισχύει και για άλλους delimiters (π.χ. tabs). Η εντολή:
$a=1;
$b=2;
$c=3;
$d=4;
print OUT $a,"\t",$b,":",$c,"\n",$d,"\n";
Θα τυπώσουν στο αρχείο με filehandle OUT τις δύο παρακάτω γραμμές:
1 2:3
4
Eισαγωγή αρχείων από τη γραμμή εντολών (ARGV)
Η εισαγωγή των ονομάτων των αρχείων εντός του σώματος του προγράμματος με εντολές open είναι μια καλή πρακτική για την περίπτωση που τα ονόματα των αρχείων χειρισμού είναι σταθερά και γενικώς ακολουθούν μια τυπική ονοματολογία. Τι συμβαίνει στην περίπτωση που θέλουμε να εκτελέσουμε ένα πρόγραμμα του οποίου τα ονόματα αρχείων εισόδου/εξόδου θα μπορούμε να αλλάζουμε κατά βούληση κάθε φορά που εκτελούμε το πρόγραμμα;
Η Perl μας παρέχει για αυτό το λόγο τη δυνατότητα δήλωσης ονομάτων αρχείων μέσω του πίνακα @ARGV. Μπορείτε να δείτε περισσότερα για τους πίνακες στο αντίστοιχο κεφάλαιο (Arrays) αλλά για την ώρα μπορείτε απλώς να διαβάσετε παρακάτω την απλή χρήση ενός πίνακα όπως ο @ARGV.
Ο @ARGV είναι ο πίνακας στον οποίο η Perl αποθηκεύει αυτόματα όλα τα στοιχεία της γραμμής εντολών κατά την εκτέλεση ενός προγράμματος. Η αποθήκευση γίνεται με διαχωριστή (delimiter) οποιοδήποτε συνδυασμό κενών χαρακτήρων με πιο προφανή το κενό " " (backspace). Σε μια απλή εντολή εκτέλεσης ενός προγράμματος (έστω program.pl) όπως η παρακάτω:
>perl program.pl sequence.fa output.dat
η Perl αυτόματα δημιουργεί έναν πίνακα τον οποίον ονομάζει @ARGV και στον οποίον τοποθετεί με τη σειρά τα στοιχεία της γραμμής εντολών ξεκινώντας από το πρώτο που ακολουθεί το όνομα του προγράμματος. Στο παραπάνω παράδειγμα το πρώτο στοιχείο του @ARGV (το @ εδώ είναι το σύμβολο του πίνακα, όπως το $ είναι το σύμβολο των βαθμωτών μεταβλητών) είναι το "sequence.fa" και το δευτερο το "output.dat". H σύνταξη των πινάκων μας λέει ότι μπορούμε να δούμε το καθένα από τα στοιχεία ενός πίνακα καλώντας τα ως βαθμωτές μεταβλητές με βάση τη θέση τους στον πίνακα (όπου η αρίθμηση ξεκινά από 0). Έτσι το πρώτο στοιχείο του @ARGV είναι η βαθμωτή μεταβλητή $ΑRGV[0] και είναι ίση με "sequence.fa", και το δεύτερο $ΑRGV[1] ίσο με "output.dat". Tην ονοματολογία του πίνακα τώρα μπορούμε να εκμεταλλευτούμε μέσα στο πρόγραμμα κάνοντας τις εντολές open να αναφέρονται όχι σε σταθερά ονόματα μεταβλητών αλλά στις @ARGV μεταβλητές:
open SEQ, "<$ARGV[0]";
open OUT, ">$ARGV[1]";
Oι δύο παραπάνω εντολές προσπερνούν τη δέσμευση να αναφέρονται τα αρχεία με συγκεκριμένο όνομα εντός του προγράμματος. Με αυτόν τον τρόπο λέμε στο πρόγραμμα πως θα πρέπει να διαβάσει από το αρχείο που θα βρίσκεται στη γραμμή εντολών αμέσως μετά το όνομα του προγράμματος και να γράψει τα αποτελέσματα σε αυτό που ακολουθεί αμέσως μετά. Έτσι κάθε εκτέλεση του προγράμματος μπορεί να αναφέρεται και σε διαφορετικούς συνδυασμούς αρχείων χωρίς να χρειάζεται κάθε φορά να τροποποιούμε το script.
Με τον ίδιο τρόπο που είδαμε ως τώρα ότι μπορούμε να διαβάσουμε μεταβλητές από την οθόνη, μπορούμε να διαβάσουμε και απο αρχεία. Ο τελεστής είναι το σύμβολο <> (diamond) μέσα στο οποίο αντί για το STDIN τοποθετούμε το εσωτερικό όνομα του αρχείου όπως αυτό έχει δηλωθεί σε μια εντολή open. Η χρήση του τελεστή είναι συνδεδεμένη με την απόδοση σε μία μεταβλητή . Η σύνταξη είναι απλή και έχει ως εξής:
$scalar=<FILEHANDLE>;
Δοκιμάστε την παρακάτω σειρά, εντολών αφού κατεβάσετε τα αρχεία του Practical 02 από εδώ.
#!/usr/bin/perl
#use warnings;
# opening of files
open SEQ, "<human.fa";
open SITES, "<sites.tab";
# reading from files into variables
$sequence=<SEQ>;
$sites=<SITES>;
# printing of variables
print $sequence,"\n";
print $sites,"\n";
exit;
Στο αποτέλεσμα που θα δείτε παρατηρήστε ότι και στα δύο αρχεία αυτό που έχουμε καταφέρει είναι πρακτικά να διαβάσουμε την πρώτη γραμμή. Για να διαβάσουμε όλο το αρχείο, γραμμή-γραμμή θα πρέπει να καταφύγουμε σε μια επαναληπτική διαδικασία. Περισσότερα για αυτή στο κέφαλαιο των βρόχων επανάληψης.