Το προφίλ μας στο Google Plus
0

Ανάπτυξη εφαρμογών σε C# για το .NET [μέρος 5ο]

Έφτασε η ώρα να αφήσουμε πίσω την είσοδο δεδομένων από το πληκτρολόγιο και την εμφάνιση της εξόδου στην εκάστοτε κονσόλα. Σ’ αυτό το μέρος της σειράς μας θα προχωρήσουμε στην αλληλεπίδραση με το σύστημα αρχείων και θα εξερευνήσουμε τις δυνατότητες που προσφέρει το .NET framework, για την είσοδο και την έξοδο δεδομένων σε αρχεία.

Όλα τα προγράμματα, από τα πιο μικρά κι απλά μέχρι και τα πιο μεγάλα και σύνθετα, αποτελούν “μηχανές” που επεξεργάζονται δεδομένα. Η πληροφορία εισέρχεται σε αυτές τις μηχανές από κάποια “συσκευή εισόδου” και, όταν ολοκληρωθεί η επεξεργασία, κατευθύνεται σε κάποια “συσκευή εξόδου”. Ως γνωστόν, η τυπική συσκευή εισόδου (standard input device) είναι το πληκτρολόγιο, ενώ η τυπική συσκευή εξόδου είναι η οθόνη. Αυτό βέβαια ισχύει για τα πολύ απλά προγράμματα και συνήθως γι’ αυτά με τα οποία ξεκινά κανείς τη γνωριμία του με μια γλώσσα προγραμματισμού. Ε, λοιπόν, τα προγράμματα που έχουμε δει μέχρι στιγμής ανήκουν σε αυτήν ακριβώς την κατηγορία. Σε αυτό το άρθρο θα ανέβουμε επίπεδο (level up!) και θα δούμε μερικά προγράμματα που χρησιμοποιούν ως είσοδο αλλά κι ως έξοδο κάποια αρχεία. Ουσιαστικά, θα γνωρίσουμε τις κλάσεις και τις μεθόδους που παρέχει το .NET Framework για το χειρισμό των αρχείων — και του συστήματος αρχείων γενικότερα. Κι επειδή θα ήταν περιττό να σχολιάσουμε τη σπουδαιότητα αυτής της γνωριμίας, θα μπούμε κατευθείαν στο ψητό. Οι κλάσεις που θα μας απασχολήσουν ανήκουν στο namespace με όνομα System.IO.

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

  • File.WriteAllText(string path, string data). Με αυτή τη μέθοδο τα δεδομένα που περιέχει η συμβολοσειρά data εγγράφονται στο αρχείο που δηλώνει η παράμετρος path. Αν η συγκεκριμένη μεταβλητή δεν περιλαμβάνει κάποια απόλυτη διαδρομή (absolute path), τότε αντιμετωπίζεται ως σχετική ως προς τη θέση του εκτελούμενου αρχείου.
  • File.WriteAllLines(string path, string[ ] data). Αυτή τη φορά τα προς εγγραφή δεδομένα βρίσκονται σε έναν πίνακα. Η τιμή από κάθε κελί του πίνακα αποθηκεύεται σε μια ξεχωριστή γραμμή του αρχείου.
  • File.WriteAllBytes(string path, byte[ ] data). Αυτή η μέθοδος αποθηκεύει και πάλι τα δεδομένα ενός πίνακα. Αυτή τη φορά, όμως, δεν χρησιμοποιούνται διαφορετικές γραμμές και τα δεδομένα γράφονται σε διαδοχικές θέσεις, byte προς byte. Όπως μπορούμε εύκολα να μαντέψουμε, η WriteAllBytes χρησιμοποιείται για την εγγραφή αρχείων με δυαδικά δεδομένα (binary data).
  • File.AppendAllText(string path, string data). H AppendAllText λειτουργεί όπως και η WriteAllText, με τη διαφορά ότι δεν επικαλύπτει τα δεδομένα που ενδέχεται να περιλαμβάνει ήδη το δοθέν αρχείο. Τα νέα δεδομένα προστίθενται στο τέλος του αρχείου.

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

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

Επειδή έχουμε βάσιμες υποψίες ότι μας κατέκλυσε ακατάσχετη λογοδιάρροια και καθώς δεν είναι του χαρακτήρα μας να αρκούμαστε μόνο στη θεωρία, θα εξετάσουμε τώρα μερικά παραδείγματα. Θα ξεκινήσουμε με την WriteAllText. Το πρόγραμμά μας θα αποθηκεύει το όνομα του χρήστη στο αρχείο name.txt, στην ίδια θέση με το εκτελέσιμο. Ανοίγουμε λοιπόν το αγαπημένο μας SharpDevelop, δημιουργούμε ένα νέο project εφαρμογής κονσόλας και εισάγουμε τον ακόλουθο κώδικα:

using System;
using System.IO;

namespace FileSystem {
  class Program {
    public static void Main( string[] args ) {
      Console.WriteLine( "Πως σε λένε;" );
      string usrin = Console.ReadLine();

      try {
        File.WriteAllText( "name.txt", usrin );
       Console.WriteLine( "Το όνομά σου αποθηκεύτηκε επιτυχώς στο αρχείο 'name.txt'" );
      } catch( Exception error ) {
        Console.WriteLine( error.Message );
      }
      Console.ReadKey( true );
   }
  }
}

Στη γραμμή 02 δηλώνουμε τη χρήση του namespace System.IO. Όπως είπαμε ήδη, το συγκεκριμένο namespace περιλαμβάνει τις κλάσεις για το χειρισμό των αρχείων. Νομίζουμε ότι μέχρι και τη γραμμή 08 τα πάντα είναι γνώριμα και απλά. Το πρόγραμμα τυπώνει ένα μήνυμα προτροπής και τα δεδομένα που θα εισαγάγει ο χρήστης θα αποθηκευτούν στη μεταβλητή usrin. Στη γραμμή 10 ξεκινάει ένα block τύπου try, που το χρησιμοποιούμε για την αντιμετώπιση των πιθανών εξαιρέσεων. Μέσα στο block συναντάμε τη χρήση της μεθόδου WriteAllText, με την οποία αποθηκεύουμε την τιμή της μεταβλητής usrin, στο αρχείο name.txt. Στη συνέχεια (γραμμή 12) εμφανίζουμε ένα μήνυμα επιτυχίας. Εδώ χρειάζεται λίγη προσοχή: Το συγκεκριμένο μήνυμα δεν θα εμφανιστεί καθόλου, αν κατά την προσπάθεια εγγραφής του αρχείου προκύψει κάποιο πρόβλημα. Σε αυτή την περίπτωση η ροή του προγράμματος θα μεταφερθεί αμέσως στο μπλοκ catch. Μάλιστα, το σύστημα θα τροφοδοτήσει το συγκεκριμένο μπλοκ με μια παράμετρο που, όπως μπορείτε να δείτε στη γραμμή 13, αποτελεί ένα αντικείμενο τύπου Exception. Στη γραμμή 14 φροντίζουμε για την ενημέρωση του χρήστη, με την εμφάνιση του σφάλματος που προέκυψε. Το σχετικό μήνυμα το λαμβάνουμε από την ιδιότητα Message, του αντικειμένου Exception που παρείχε το σύστημα. Κατά τα γνωστά, στη γραμμή 16 χρησιμοποιούμε τη μέθοδο Console.ReadKey, για να εισάγουμε μία παύση στην εκτέλεση του προγράμματος.

Με τη χρήση της WriteAllText, κάθε φορά που θα εκτελούμε το πρόγραμμα το περιεχόμενο του αρχείου name.txt θα αντικαθίσταται από μια νέα συμβολοσειρά. Στην περίπτωση που θα θέλαμε να διατηρούνται τα υπάρχοντα δεδομένα, θα ήταν αρκετό να αντικαταστήσουμε τη WriteAllText με την AppendAllText. Ας προχωρήσουμε τώρα σε ένα ακόμα παράδειγμα, με τη WriteAllLines. Αυτό το πρόγραμμα θα κατασκευάζει ένα τμήμα του πίνακα της προπαίδειας, ανάλογα με τον αριθμό που εισάγει ο χρήστης. Το περιεχόμενο του πίνακα θα αποθηκεύεται στο αρχείο prop-X.txt, με το Χ να αποτελεί τον αριθμό που έδωσε ο χρήστης.

using System;
using System.IO;

namespace Propaideia {
  class Program {
    public static void Main( string[] args ) {      
     Console.Write( "Πίνακας προπαίδειας για τον αριθμό: " );
     string usrin = Console.ReadLine();
     int usrnum;
     try {
       usrnum = int.Parse( usrin );
     } catch( Exception err ) {
       Console.WriteLine( err.Message );
       Console.ReadKey( true );
       return;
     }
     
     string[] prop = new string[11];
     for( int i=0; i<prop.Length; i++ ) {
       int apotelesma = usrnum * i;
       prop[i] = i.ToString() + " x " + usrnum.ToString() + " = " + apotelesma.ToString();
      }
      
      string filename = "prop-" + usrnum.ToString() + ".txt";
      try {
        File.WriteAllLines( filename, prop );
        Console.WriteLine( "Η προπαίδεια για το " + usrnum + " αποηθεύτηκε επιτυχώς στο αρχείο '" + filename + "'" );
      } catch( Exception err ) {
       Console.WriteLine( err.Message );
      }
      Console.ReadKey( true );
    }
  }
}

Η μελέτη μας θα ξεκινήσει από τις γραμμές 08 και 09. Εκεί λαμβάνουμε τα δεδομένα που εισάγει ο χρήστης και τα αποθηκεύουμε στη μεταβλητή usrin, ενώ ορίζουμε και τη μεταβλητή usrnum στην οποία θα αποθηκεύσουμε τον αντίστοιχο αριθμό. Η μετατροπή της συμβολοσειράς σε ακέραιο αριθμό συντελείται στο μπλοκ try (γραμμή 11), με τη βοήθεια της μεθόδου int.Parse(). Αν η συμβολοσειρά usrin δεν μπορεί να μετατραπεί σε ακέραιο αριθμό τότε εμφανίζουμε το μήνυμα της σχετικής εξαίρεσης, επιβάλλουμε μια παύση και στη συνέχεια τερματίζουμε τη λειτουργία του προγράμματος. Αν επιτευχθεί η μετατροπή, η ροή του προγράμματος μεταφέρεται πλέον στη γραμμή 18. Εκεί ορίζεται ένας πίνακας συμβολοσειρών, με 11 θέσεις. Όπως αντιλαμβάνεστε, σε αυτές τις θέσεις θα αποθηκευτούν τα αποτελέσματα από τον πολλαπλασιασμό των αριθμών 0 έως 10, με εκείνον που έδωσε ο χρήστης. Οι απαιτούμενες πράξεις και η συμπλήρωση του πίνακα πραγματοποιούνται στο βρόχο For (γραμμές 19 ως 2). Ο κορμός του προγράμματός μας (η μέθοδος main) ολοκληρώνεται με την αποθήκευση των περιεχομένων του πίνακα σε κάποιο αρχείο. Το όνομα του αρχείου συγκροτείται από το σταθερό τμήμα “prop-“, τον αριθμό που έδωσε ο χρήστης και την κατάληξη “.txt” (γραμμή 24). Στη συνέχεια συναντάμε και πάλι ένα μπλοκ try, όπου γίνεται χρήση της μεθόδου WriteAllLines. Αν η εγγραφή του αρχείου ολοκληρωθεί χωρίς προβλήματα, εμφανίζεται το αντίστοιχο μήνυμα επιτυχίας (γραμμή 27). Σε διαφορετική περίπτωση εκτελείται το μπλοκ catch, που τυπώνει το σχετικό μήνυμα σφάλματος.

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

Το πρόγραμμά μας ετοίμασε ένα αρχείο με τον πίνακα προπαίδειας του 7. Δεν ξέρουμε για εσάς, αλλά το ξαδερφάκι που πηγαίνει δημοτικό ξετρελάθηκε :D

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

  • File.ReadAllText(string path). Αυτή η μέθοδος επιστρέφει ολόκληρο το περιεχόμενο του αρχείου σαν μια (ενιαία) συμβολοσειρά.
  • File.ReadAllLines(string path). Κάθε γραμμή του αρχείου επιστρέφεται σαν μια ξεχωριστή συμβολοσειρά. Με αυτή τη μέθοδο μπορούμε να μεταφέρουμε όλες τις γραμμές ενός αρχείου σε έναν πίνακα.
  • File.ReadAllBytes(string path). Η συγκεκριμένη μέθοδος προσφέρεται για αρχεία σαν αυτά που δημιουργούνται με τη μέθοδο WriteAllBytes. Η ReadAll Bytes επιστρέφει ένα προς ένα τα bytes που απαρτίζουν το περιεχόμενο του αρχείου.

Θα εξετάσουμε τώρα ένα απλό πρόγραμμα που ομοιάζει με το cat — το περίφημο εργαλείο που γνωρίζουμε από τη γραμμή εντολών των συστημάτων Unix. Το πρόγραμμά μας θα δέχεται σαν παράμετρο το όνομα του αρχείου που θέλουμε να τυπωθεί στην οθόνη. Εάν δεν του δώσουμε αυτή την παράμετρο, θα εμφανίζει ένα μήνυμα και θα περιμένει να πληκτρολογήσουμε κάποιο όνομα αρχείου.

using System;
using System.IO;

namespace FileContents {
 class Program {
    public static void Main( string[] args) {
      string fileName;
      string fileContents;

      if( args.Length > 0) {
        fileName = args[0];
      } else {
        Console.Write( "Εμφάνιση περιεχομένων του: " );
        fileName = Console.ReadLine();
      } 

      if( File.Exists( filename ) ) {
        try  {
          fileContents = File.ReadAllText( fileName );
          Console.WriteLine();	
          Console.WriteLine( fileContents );
        }  catch ( Exception err ) {
          Console.WriteLine( err.Message );
        } 
      } else {
        Console.WriteLine( "Το αρχείο " + fileName + " δεν υπάρχει!" );
      } 

      Console.ReadKey( true );
    } 
  } 
}

Στις γραμμές 07 και 08 ορίζουμε δύο μεταβλητές τύπου string, από τις οποίες η πρώτη θα περιέχει το όνομα του αρχείου και η δεύτερη το περιεχόμενό του. Στη γραμμή 10 συναντάμε για πρώτη φορά τον πίνακα args. Όπως μαντεύετε, ο εν λόγω πίνακας περιλαμβάνει τις παραμέτρους που δόθηκαν κατά την εκτέλεση του προγράμματος από τη γραμμή εντολών. Το πρόγραμμά μας ελέγχει το μήκος αυτού του πίνακα και, ουσιαστικά, τσεκάρει αν ο χρήστης έδωσε κάποια παράμετρο ή όχι. Αν έχουν δοθεί παράμετροι, θεωρούμε ότι η πρώτη από αυτές αντιστοιχεί στο όνομα του αρχείου που θέλει να τυπώσει ο χρήστης. Παρεμπιπτόντως, αν ο χρήστης θέλει να εμφανίσει ένα αρχείο με όνομα (ή διαδρομή) που περιλαμβάνει τον χαρακτήρα του κενού, θα πρέπει να περικλείσει την παράμετρο σε διπλά αγγλικά εισαγωγικά. Στην περίπτωση που δεν έχει οριστεί κάποια παράμετρος (γραμμές 12 έως 14), το πρόγραμμα εμφανίζει μια προτροπή και περιμένει από το χρήστη να εισάγει το όνομα κάποιου αρχείου.

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

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

Η δική μας υλοποίηση του εργαλείου cat, για το .NET Framework. Εδώ βλέπουμε τα περιεχόμενα του αρχείου mit.txt (με την άδεια χρήσης MIT).

Νομίζουμε ότι τα παραδείγματα που εξετάσαμε ως τώρα ήταν απλά και απολύτως κατανοητά. Στη συνέχεια θα μελετήσουμε ένα ελαφρώς πιο σύνθετο παράδειγμα. Το σχετικό πρόγραμμα θα μετράει τις γραμμές ενός αρχείου, θα εντοπίζει τις κενές και θα μπορεί να δημιουργεί αντίγραφα του δοθέντος αρχείου, με αριθμημένες γραμμές. Εν ολίγοις, θα μελετήσουμε ένα πρόγραμμα αντίστοιχο του lines.py, που μπορείτε να βρείτε εδώ. Ας δούμε τώρα την υλοποίηση σε C#.

using System;
using System.IO;

namespace Lines  {
  class Program  {
    public static void Main( string[] args ) {
      string fileName;
      string[] fileLines;
      int lines;
      int emptyLines;

      if( args.Length > 0 ) {
        fileName = args[0];
      } else  {
        Console.Write( "Αρχείο: " );
        fileName = Console.ReadLine();
      } 

      if( File.Exists( fileName ) ) {
        try {
          fileLines = File.ReadAllLines( fileName );
        }  catch( Exception err ) {
          Console.WriteLine( err.Message );
          return;
        }
      } else {
        Console.WriteLine( "Το αρχείο " + fileName + " δεν υπάρχει!" );
        return;
      }

      lines = fileLines.Length;
      emptyLines = 0;
       
      int temp = lines;
      string format = "";
      while(  temp > 0 ) {
        format += "0";
        temp /= 10;
      } 

      for( int i=0; i<fileLines.Length; i++ ) {
        if( fileLines[i] == "" ) {
          emptyLines++;
        } 
        fileLines[i] = i.ToString( format ) + " " + fileLines[i];
      } 

      Console.WriteLine( "Στο αρχείο '" + fileName + "' βρέθηκαν " + lines + " γραμμές" );
      Console.WriteLine( "από τις οποίες οι " + emptyLines + " ήταν κενές.\n" );  

      Console.Write( "Θέλετε να δημιουργηθεί νέο αρχείο με αριθμημένες\nτις γραμμές του '" + fileName + "'( Ναι, Οχι );" );
      string userAnswer = Console.ReadLine();
      if( userAnswer.StartsWith( "ν", StringComparison.CurrentCultureIgnoreCase ) || userAnswer.StartsWith( "n", StringComparison.CurrentCultureIgnoreCase ) ) {
        try {
          string newFileName = fileName+".lines-numbered";
          File.WriteAllLines( newFileName, fileLines );
          Console.WriteLine( "Δημιουργήθηκε επιτυχώς το '" + newFileName + "'" );
        } catch( Exception err ) {
          Console.WriteLine( err.Message );
        } 
      } 

      Console.ReadKey( true );
    } 
  } 
}

Μέχρι και τη γραμμή 30 δεν συναντάμε μεγάλες διαφορές σε σχέση με το προηγούμενο πρόγραμμα. Μια αλλαγή που αξίζει να παρατηρήσουμε, είναι η αντικατάσταση της μεταβλητής fileContents από τον πίνακα fileLines (γραμμή 07). Αυτή τη φορά, βλέπετε, χρησιμοποιούμε τη μέθοδο ReadAllLines (γραμμή 20), η οποία επιστρέφει το περιεχόμενο του δοθέντος αρχείου γραμμή προς γραμμή. Μια ακόμα διαφοροποίηση σχετίζεται με την προσθήκη των μεταβλητών lines και emptyLines, οι οποίες θα συγκρατούν το πλήθος των γραμμών και το πλήθος των κενών γραμμών αντίστοιχα. Οι συγκεκριμένες μεταβλητές αρχικοποιούνται στις γραμμές 30 και 31.

Στη συνέχεια πραγματοποιείται μια εργασία που θα σας φανεί τουλάχιστον παράξενη. Κατ’ αρχάς, αντιγράφουμε το συνολικό πλήθος των γραμμών στη μεταβλητή temp. Στο βρόχο που ακολουθεί (γραμμές 35 έως 38) διαιρούμε τη μεταβλητή temp με τον αριθμό δέκα, έως ότου μηδενιστεί. (Σας κάνει εντύπωση το ότι κάποια στιγμή θα μηδενιστεί; Εφόσον πρόκειται για μεταβλητή τύπου Int, δεν διαθέτει δεκαδικό τμήμα. Έτσι, το αποτέλεσμα της διαίρεσης υπόκειται κάθε φορά σε μια βάρβαρη στρογγυλοποίηση.) Σε κάθε επανάληψη του βρόχου προσθέτουμε στη συμβολοσειρά format ένα μηδενικό. Με αυτόν τον τρόπο, η εν λόγω συμβολοσειρά αποκτά τόσα μηδενικά, όσα είναι και τα αριθμητικά ψηφία που εκφράζουν το συνολικό πλήθος των γραμμών του αρχείου. Η συμβολοσειρά format αξιοποιείται αργότερα (γραμμή 44), κατά το σχηματισμό των αριθμών γραμμής. Με τη βοήθειά της κάθε αριθμός θα έχει τόσα μηδενικά στα αριστερά του, ώστε όλοι να έχουν το ίδιο πλάτος! Με λίγα λόγια, η συμβολοσειρά format θα χρειαστεί για το λεγόμενο padding των αριθμών γραμμής. Ίσως να θεωρείτε περιττή όλη αυτή τη φασαρία, αλλά η δική μας τελειομανία επέβαλε την προσοχή και την επιμέλεια σε κάθε λεπτομέρεια — ακόμα και στο ζήτημα της μορφοποίησης των αριθμών γραμμής.

Ο βρόχος που ξεκινά στη γραμμή 40 πραγματοποιεί την καταμέτρηση των κενών γραμμών. Επιπρόσθετα, με τη βοήθεια της μεθόδου toString ο αριθμός της τρέχουσας γραμμής μετατρέπεται σε συμβολοσειρά και προστίθεται στην αρχή της γραμμής. Για την ώρα, όμως, οι ανανεωμένες γραμμές παραμένουν στον πίνακα fileLines και δεν αποθηκεύονται σε κάποιο αρχείο. Αμέσως μετά, το πρόγραμμα ενημερώνει το χρήστη για το συνολικό πλήθος των γραμμών του αρχείου, όπως και για το πλήθος των κενών. Τέλος, ζητάει την άδειά του για τη δημιουργία ενός αντιγράφου με αριθμημένες γραμμές. Η απόκριση του χρήστη συλλέγεται στη γραμμή 51 κι αποθηκεύεται στη μεταβλητή userAnswer. Ο έλεγχος της απάντησης πραγματοποιείται με τον πιο “ελαστικό” τρόπου που μπορούσαμε να φανταστούμε. Το πρόγραμμα δεν ελέγχει ούτε την ορθογραφία, ούτε και το πλήρες περιεχόμενο της απάντησης. Ο έλεγχος αφορά μόνο στο πρώτο γράμμα: Αν η απάντηση του χρήστη ξεκινά με το γράμμα “ν” ή το “n” (για τους φίλους που επιμένουν να γράφουν σε greeklish), θεωρούμε ότι ο χρήστης απάντησε καταφατικά. Στη γραμμή 52 συναντάμε τη μέθοδο StartsWith της κλάσης string, με την οποία υλοποιείται ο έλεγχος που περιγράψαμε. Η συγκεκριμένη μέθοδος δέχεται ως παραμέτρους τους χαρακτήρες που μας ενδιαφέρουν. Προαιρετικά, για κάθε χαρακτήρα δέχεται και μια πρόσθετη παράμετρο, που καθορίζει το πώς θα πραγματοποιηθεί η αναζήτηση. Αυτές οι βοηθητικές παράμετροι μπορούν να πάρουν μία από τις τιμές του enumeration ονόματι StringComparison (μπορείτε να δείτε όλα τα μέλη του συγκεκριμένου enumeration). Εμείς επιλέξαμε το IgnoreCase, για να αποφύγουμε τη διάκριση μεταξύ πεζών και κεφαλαίων. Εφόσον το πρόγραμμα συμπεράνει ότι έλαβε θετική απάντηση, αποθηκεύει τα δεδομένα του πίνακα fileLines με τη βοήθεια της μεθόδου WriteAllLines (γραμμή 55). Κατά τα γνωστά, η εγγραφή του αρχείου είναι τοποθετημένη σε ένα μπλοκ try, ώστε μια ενδεχόμενη αποτυχία κατά την εγγραφή να μην επιφέρει τον τερματισμό του προγράμματος.

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

Τα παραδείγματα που εξετάσαμε μπορούν να δεχτούν πολλές προσθήκες, όπως και αρκετές βελτιώσεις. Μέχρι το επόμενο τεύχος θα σας προτείναμε να προσπαθήσετε να τα επεκτείνετε, με τις δικές σας ιδέες ή και με ιδέες που είδατε κάπου αλλού. Σε αυτό το στάδιο, εξάλλου, η συγγραφή προγραμμάτων και οι ατελείωτες δοκιμές έχουν μεγαλύτερη σημασία από το αν θα φτιάξετε κάτι πρωτότυπο ή όχι. Σημειώστε ότι μπορείτε να κατεβάσετε τον δικό μας κώδικα. Αν ανοίξετε το πακετάκι με τον κώδικα, θα διαπιστώσετε ότι έχουμε συμπεριλάβει και τα τέσσερα διαφορετικά projects που παρουσιάσαμε, κάτω από το ίδιο solution. Θα σας αφήσουμε να εξερευνήσετε αυτόν τον μυστηριώδη τρόπο οργάνωσης των αρχείων και είμαστε 102,59% σίγουροι, ότι μέχρι το επόμενο τεύχος θα έχετε λύσει τo μυστήριο και θα έχετε κατανοήσει τη σχέση μεταξύ project και solution ;)

Leave a Reply

You must be logged in to post a comment.

Σύνδεση

Αρχείο δημοσιεύσεων