Εισαγωγή στον Προγ/μό Υπολογιστών Ενότητα 9: Αλφαριθμητικά/Strings Διδάσκων: Μιχάλης Τίτσιας
Η αρχή της απαρίθμησης Οι ηλεκτρονικοί υπολογιστές είναι καλοί στο να επεξεργάζονται αριθμητικά δεδομένα. Όταν δηλώνετε μια μεταβλητή τύπου int, η εικονική μηχανή της Java δεσμεύει μια θέση μνήμης ικανή να αποθηκεύσει έναν ακέραιο. Η ικανότητα αποτύπωσης μιας ακέραιας τιμής, όμως, καθιστά εύκολη την εργασία με άλλους τύπους δεδομένων όσο αυτοί μπορούν να αναπαρασταθούν χρησιμοποιώντας ακέραιους. Για όσους αποτελούνται από ένα πεπερασμένο σύνολο τιμών, η πιο απλή προσέγγιση είναι η απαρίθμηση των στοιχείων της συλλογής. Για παράδειγμα, αν θέλετε να εργαστείτε με δεδομένα που αντιπροσωπεύουν μήνες του έτους, μπορείτε να ορίσετε απλά ακέραιους κωδικούς για τα ονόματα κάθε μήνα. Έτσι, ο Ιανουάριος είναι ο μήνας 1, Φεβρουάριος είναι ο 2, και ούτω καθεξής. Οι τύποι που προσδιορίζονται από την καταμέτρηση των στοιχείων τους ονομάζονται enumerated types.
Απαριθμητοί τύποι στη Java Μέσω σταθερών που αντιπροσωπεύουν τις τιμές της απαρίθμησης Μέσω του enum όπως εισαχθεί στην Java 5.0 Πριν από την κυκλοφορία της Java 5.0, αυτή δεν παρείχε εγγενή υποστήριξη των απαριθμητών τύπων. Μέχρι τότε, οι προγραμματιστές για να επιτύχουν το ίδιο αποτέλεσμα ορίζαν ακέραιες σταθερές για να αντιπροσωπεύουν τα στοιχεία και στη συνέχεια, χρησιμοποιούσαν μεταβλητές τύπου int για να αποθηκεύσουν τις τιμές. Για παράδειγμα, ο καθορισμός των ονομάτων των τεσσάρων σημείων της πυξίδας θα γινόταν ως εξής : public static final int NORTH = 0; public static final int EAST = 1; public static final int SOUTH = 2; public static final int WEST = 3; Μετά από αυτό ο προγραμματιστής μπορούσε να αποθηκεύσει μια κατεύθυνση σε μια ακέραια μεταβλητή.
Η λειτουργία του enum Οι πρόσφατες εκδόσεις της Java υποστηρίζουν ένα φαινομενικά πιο απλό και εκφραστικό τρόπο για τον καθορισμό της απαρίθμησης ως ένα ξεχωριστό τύπο. Στην απλούστερη μορφή του, το πρότυπο για τον ορισμό ενός απαριθμητικού τύπου είναι public enum name { list of element names } Εάν χρησιμοποιείτε το enum, μπορείτε να ορίσετε ένα νέο τύπο για τα τέσσερα σημεία της πυξίδας, έτσι: public enum Direction { NORTH, EAST, SOUTH, WEST } Μπορείτε να δηλώσετε στη συνέχεια μια μεταβλητή τύπου Direction και την χρησιμοποιείτε σε συνδυασμό με τις σταθερές που καθορίζονται στην τάξη.
Δυνατά και αδύναμα σημεία του enum Το enum έχει πλεονεκτήματα σε σχέση με τη χρήση integers: Ο compiler επιλέγει τους ακέραιους κωδικούς αυτόματα. Οι δηλώσεις μεταβλητών χρησιμοποιούν ένα πιο χαρακτηριστικό όνομα τύπου. Ο compiler βεβαιώνει ότι οι τιμές αναλογούν στον τύπο. Οι τιμές του τύπου εμφανίζονται ονόματα κατά την εκτύπωση. To enum, ωστόσο, έχει και μειονεκτήματα καθώς : Οι σταθερές πρέπει να περιλαμβάνουν τον τύπο, πχ Direction.EAST Παραδόξως, οι σταθερές που χρησιμοποιούνται σε case πρέπει να παραλείπουν τον τύπο. Κώδικας γραμμένος με enum λειτουργεί μόνο σε νεότερες εκδόσεις. To enum είναι πολύ πιο περίπλοκο από ό, τι περιγράφεται εδώ. Ο υπάρχον κώδικας Java χρησιμοποιεί το παλαιότερο στυλ, άρα πρέπει να το μάθετε έτσι κι αλλιώς.
Χαρακτήρες Οι υπολογιστές χρησιμοποιούν την αρχή της απαρίθμησης για τα δεδομένα χαρακτήρων μέσα στην μνήμη τους. Υπάρχει ένας πεπερασμένος αριθμός των χαρακτήρων στο πληκτρολόγιο. Εάν εκχωρήσετε έναν ακέραιο σε κάθε χαρακτήρα, μπορείτε να χρησιμοποιήσετε τον ακέραιο ως κωδικό για το χαρακτήρα που αντιπροσωπεύει. Αυτό ωστόσο, δεν είναι ιδιαίτερα χρήσιμο εκτός αν είναι τυποποιημένο. Αν διαφορετικοί κατασκευαστές υπολογιστών χρησιμοποιούν διαφορετικές κωδικοποιήσεις (όπως συνέβαινε στην αρχή), είναι δύσκολο να μεταφέρουμε αυτά τα δεδομένα ανάμεσα σε μηχανές. Η πρώτη ευρείας χρήσης κωδικοποίηση χαρακτήρων ήταν η ASCII (American Standard Code for Information Interchange). Με μόνο 256 πιθανούς χαρακτήρες, το ASCII αποδείχθηκε ανεπαρκές για τα πολλά αλφάβητα σε όλο τον κόσμο. Έτσι έχει αντικατασταθεί από το Unicode, που επιτρέπει ένα πολύ μεγαλύτερο αριθμό χαρακτήρων.
Το υποσύνολο ASCII του Unicode Ο παρακάτω πίνακας παρουσιάζει τους πρώτους 128 χαρακτήρες στο Unicode, που είναι ίδιοι με το παλαιότερο σύστημα ASCII : Το γράμμα Α, για παράδειγμα, έχει την τιμή 1018, που είναι το άθροισμα των κεφαλίδων γραμμής και στήλης. Η τιμή Unicode κάθε χαρακτήρα του πίνακα είναι το άθροισμα των οκταδικών αριθμών της εν λόγω γραμμής και στήλης. 1 2 3 4 5 6 7 00x \000 \001 \002 \003 \004 \005 \006 \007 \b \t \n \011 \f \r \016 \017 \020 \021 \022 \023 \024 \025 \026 \027 \030 \031 \032 \033 \034 \035 \036 \037 space ! " # $ % & ' ( ) * + , - . / 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ \177 \000 \001 \002 \003 \004 \005 \006 \007 \b \t \n \013 \f \r \016 \017 \020 \021 \022 \023 \024 \025 \026 \027 \030 \031 \032 \033 \034 \035 \036 \037 space ! " # $ % & ' ( ) * + , - . / 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ \177 01x 02x 03x 04x 05x 06x 07x 10x 11x 12x 13x 14x 15x 16x 17x
Επισημάνσεις για την εκπροσώπηση χαρακτήρων Το σημαντικό που πρέπει να θυμάστε από το πίνακα της προηγούμενης διαφάνειας είναι ότι δεν πρέπει να μάθετε τους αριθμητικούς κωδικούς για τους χαρακτήρες. Η σημαντική παρατήρηση είναι ότι ένας χαρακτήρας έχει μια καθορισμένη αριθμητική εκπροσώπηση. Για να καθορίσετε ένα χαρακτήρα, θα πρέπει να χρησιμοποιήσετε μία σταθερά χαρακτήρα, που αποτελείται από το επιθυμητό χαρακτήρα και περικλείεται σε μονά εισαγωγικά. Έτσι, η σταθερά ‘Α’ σε ένα πρόγραμμα υποδηλώνει την αναπαράσταση Unicode ενός κεφαλαίου Α. Ότι η τιμή αυτή είναι 1018 είναι μια ασήμαντη λεπτομέρια. Δύο ιδιότητες του πίνακα Unicode αξίζουν ειδική μνεία : Οι κωδικοί χαρακτήρων για τα ψηφία είναι διαδοχικοί. Τα γράμματα του αλφαβήτου χωρίζεται σε δύο περιοχές, μία για τα κεφαλαία και μια για τα πεζά γράμματα. Σε κάθε περιοχή, οι τιμές Unicode είναι διαδοχικές.
Ειδικοί χαρακτήρες Οι περισσότεροι Unicode χαρακτήρες του πίνακα είναι οι γνωστοί που εμφανίζονται στο πληκτρολόγιο. Οι χαρακτήρες αυτοί ονομάζονται εκτυπώσιμοι χαρακτήρες. Ο πίνακας περιλαμβάνει επίσης πολλούς ειδικούς χαρακτήρες που χρησιμοποιούνται για τον έλεγχο της μορφοποίησης. Οι ειδικοί χαρακτήρες εμφανίζονται στον πίνακα Unicode ως μια ακολουθία διαφυγής, που αποτελείται από μια ανάστροφη κάθετο και ακολουθείται από μια ακολουθία ψηφίων. Οι πιο κοινοί είναι : \b Backspace \f Form feed (ξεκινά μια νέα σελίδα) \n Newline (μετακινείται στην επόμενη γραμμή) \r Return (μετακινείται στην αρχή της τρέχουσας γραμμής) \t Tab (μετακινείται οριζόντια στο επόμενο στηλοθέτη) \\ Ο ίδιος ο χαρακτήρας ανάστροφης κάθετου \' Ο χαρακτήρας ' (απαιτείται μόνο σε σταθερές χαρακτήρα) \" Ο χαρακτήρας " (απαιτείται μόνο σε σταθερές συμβολοσειράς) \ddd Ο χαρακτήρας του οποίου η τιμή Unicode είναι ο οκταδικός αριθμός ddd
Χρήσιμες μέθοδοι της τάξης Character static boolean isDigit(char ch) Καθορίζει αν ο συγκεκριμένος χαρακτήρας είναι ψηφίο. static boolean isLetter(char ch) Καθορίζει αν ο συγκεκριμένος χαρακτήρας είναι γράμμα. static boolean isLetterOrDigit(char ch) Καθορίζει αν ο συγκεκριμένος χαρακτήρας είναι ψηφίο ή γράμμα. static boolean isLowerCase(char ch) Καθορίζει αν ο συγκεκριμένος χαρακτήρας είναι ένα πεζό γράμμα. static boolean isUpperCase(char ch) Καθορίζει αν ο συγκεκριμένος χαρακτήρας είναι ένα κεφαλαίο γράμμα. static boolean isWhitespace(char ch) Καθορίζει αν ο συγκεκριμένος χαρακτήρας είναι whitespace (κενά/στηλοθέτες). static char toLowerCase(char ch) Μετατρέπει το ch στο αντίστοιχο πεζό. Αν δεν υπάρχει επιστρέφει το ch. static char toUpperCase(char ch) Μετατρέπει το ch στο αντίστοιχο κεφαλαίο. Αν δεν υπάρχει επιστρέφει το ch.
Αριθμητική χαρακτήρων Το γεγονός ότι οι χαρακτήρες έχουν παραστάσεις ως ακέραιοι επιτρέπει την χρήση τους σε αριθμητικές εκφράσεις. Π.χ η έκφραση 'A' + 1, όπου η Java θα μετατρέψει το χαρακτήρα 'A' στον ακέραιο 65, θα προσθέσει 1 για να λάβει 66, που είναι ο κωδικός του χαρακτήρα 'B'. Για παράδειγμα, η ακόλουθη μέθοδος επιστρέφει ένα τυχαία επιλεγμένο κεφαλαίο γράμμα: public char randomLetter() { return (char) rgen.nextInt('A', 'Z'); } Ο παρακάτω κώδικας υλοποιεί την μέθοδο isDigit της τάξης Character: public boolean isDigit(char ch) { return (ch >= '0' && ch <= '9'); }
Χαρακτήρες char c = ’a’; // c είναι ο unicode κωδικός του a char teleia = ’.’; print(teleia); // εμφανίζει . print(’\’’); // εμφανίζει ’ print(’\\’); // εμφανίζει \ ειδικοί χαρακτήρες: ’\n’ Line Feed (LF) ’\t’ Tab ’\b’ Backspace (BS) ’\r’ Carriage Return (CR) ’\f’ Form Feed (FF)
Χαρακτήρες public void run() { char c = ’a’; / /H c παίρνει την τιμή του κωδικού (σε unicode) του συμβόλου a println(c); // Εμφανίζεται το σύμβολο με κωδικό την τιμή της c println((int) c); // Εμφανίζεται ο κωδικός (και όχι το σύμβολο) println(c+1); // Εμφανίζεται ο κωδικός του επόμενου συμβόλου μετά του a println((char) (c+1)); //Εμφανίζεται το επόμενο σύμβολο μετά το a }
Άσκηση: Αριθμητική χαρακτήρων Γράψτε μια μέθοδο toHexDigit που δέχεται ένα ακέραιο και επιστρέφει το αντίστοιχο δεκαεξαδικό ψηφίο ως χαρακτήρα. Αν το όρισμα είναι μεταξύ 0 και 9, η μέθοδος πρέπει να επιστρέψει το αντίστοιχο χαρακτήρα μεταξύ '0' και'9'. Αν είναι μεταξύ 10 και 15, θα πρέπει να επιστρέψει το κατάλληλο γράμμα στο διάστημα 'A' με 'F'. Αν είναι εκτός αυτής της περιοχής, θα πρέπει να επιστρέψει '?'. public char toHexDigit(int n) { if (n >= 0 && n <= 9) { return (char) ('0' + n); } else if (n >= 10 && n <= 15) { return (char) ('A' + n - 10); } else { return '?'; }
Συμβολοσειρές ως αφηρημένη έννοια Από το πρώτο πρόγραμμα σας, που εμφάνιζε στην οθόνη το μήνυμα "hello, world", χρησιμοποιήτε συμβολοσειρές για να επικοινωνήσετε με το χρήστη. Μέχρι τώρα, δεν ξέρατε πώς η Java αναπαριστά συμβολοσειρές στη μνήμη του υπολογιστή ή πώς μπορεί να χειριστεί τους χαρακτήρες που συνθέτουν ένα string. Όμως το γεγονός ότι δεν ξέρετε αυτά τα πράγματα δεν επηρέαζε την ικανότητά σας να χρησιμοποιείτε αποτελεσματικά συμβολοσειρές, επειδή έχετε τη δυνατότητα να σκέφτεστε τις συμβολοσειρές σαν ένα αρχέγονο είδος. Για τις περισσότερες εφαρμογές, η αφηρημένη άποψη που έχετε μέχρι σήμερα είναι η σωστή. Εσωτερικά, οι συμβολοσειρές είναι εκπληκτικά περίπλοκα αντικείμενα των οποίων τα στοιχεία είναι καλύτερα να μείνουν κρυμμένα. Η Java παρέχει μια υψηλού επιπέδου άποψη των συμβολοσειρών με τις μεθόδους της τάξης String να κρύβουν την υποκείμενη πολυπλοκότητα.
Ο τύπος String println(”Hello world”); // εμφανίζει Hello world import acm.program.*; public class test extends Program { public void run() { println(”Hello world”); // εμφανίζει Hello world }
Η τάξη String (όπως την έχουμε χρησιμοποιήσει ως τώρα) import acm.program.*; public class test extends Program { public void run() { String msg; // Δήλωση μεταβλητής τύπου String msg = ”Hello world” ; println(msg); // εμφανίζει Hello world }
Η τάξη String (όπως την έχουμε χρησιμοποιήσει ως τώρα) import acm.program.*; public class test extends Program { public void run() { String msg; msg = ”Hello ”+”world”; // Πρόσθεση String = συγκόλληση println(msg); // εμφανίζει Hello world }
Χρήση μεθόδων της String Η Java παρέχει πολλές χρήσιμες μεθόδους που λειτουργούν στην τάξη String. Πριν χρησιμοποιήσουμε αυτές τις μεθόδους, είναι σημαντικό να κατανοήσουμε πώς αυτές οι μέθοδοι λειτουργούν σε ένα πιο γενικό επίπεδο. Η String χρησιμοποιεί σύνταξη δέκτη όταν καλείτε μια μέθοδο σε μια συμβολοσειρά. Αντί να καλεί μια στατική μέθοδο (όπως για παράδειγμα, στην Character), το μοντέλο της Java’s είναι ότι στέλνει ένα μήνυμα σε ένα αλφαριθμητικό. Καμία από τις μεθόδους της String δεν αλλάζει την τιμή της συμβολοσειράς που δέχεται το μήνυμα. Αυτό που συμβαίνει, είναι ότι επιστρέφει μια νέα συμβολοσειρά στην οποία έχουν πραγματοποιηθεί οι επιθυμητές αλλαγές. Οι τάξεις που απαγορεύουν την μεταβολή της κατάστασης ενός αντικειμένου λέγονται αμετάβλητες. Αυτές έχουν πολλά πλεονεκτήματα και διαδραματίζουν σημαντικό ρόλο στον προγραμματισμό.
Συμβολοσειρές και Χαρακτήρες Οι εννοιολογικές διαφορές μεταξύ των συμβολοσειρών και των χαρακτήρων είναι εμφανείς μέσω παραδείγματος. Και η String και η Character έχουν μια μέθοδο toUpperCase που μετατρέπει πεζά γράμματα στα ισοδύναμα κεφαλαία. Στην Character, η toUpperCase καλείται σαν στατική μέθοδος: ch = Character.toUpperCase(ch); Στην String, εφαρμόζετε την toUpperCase σε ένα υπάρχον string: str = str.toUpperCase(); Σημειώστε ότι και οι δύο απαιτούν να αποθηκεύσετε το αποτέλεσμα πίσω στην αρχική μεταβλητή, αν θέλετε να αλλάξετε την τιμή του.
Επιλέγοντας χαρακτήρες από μια ακολουθία Θεωρητικά, ένα string είναι μια διατεταγμένη συλλογή χαρακτήρων Στη Java, οι θέσεις των χαρακτήρων μιας συμβολοσειράς προσδιορίζονται από ένα δείκτη που ξεκινά στο 0 και εκτείνεται μέχρι το μήκος της συμβολοσειράς μείον ένα. Για παράδειγμα, οι χαρακτήρες στη συμβολοσειρά "hello, world" διατάσσονται έτσι: h e 1 l 2 3 o 4 , 5 6 w 7 8 r 9 10 d 11 Μπορείτε να λάβετε τον αριθμό των χαρακτήρων καλώντας length. Μπορείτε να επιλέξετε ένα μεμονωμένο χαρακτήρα, καλώντας charAt(k), όπου k είναι ο δείκτης του επιθυμητού χαρακτήρα. Η έκφραση επιστρέφει τον πρώτο χαρακτήρα του str, που βρίσκεται στην θέση με δείκτη 0. str.charAt(0);
Συνένωση Μια από τις πιο χρήσιμες πράξεις με συμβολοσειρές είναι συνένωση, η οποία είναι ο συνδυασμός δύο συμβολοσειρών χωρίς ενδιάμεσους χαρακτήρες. Η τάξη String περιλαμβάνει μια μέθοδο με όνομα concat που κάνει ακριβώς αυτό, παρότι σπάνια χρησιμοποιείται. Η συνένωση είναι υλοποιημένη στην Java με την μορφή του τελεστή +. Αν χρησιμοποιήστε το + με αριθμητικούς τελεστέους, υποδηλώνει πρόσθεση. Αν τουλάχιστον ένας από τους τελεστέους είναι string, η Java ερμηνεύει το + ως συνένωση. Τότε η Java εκτελεί τα ακόλουθα βήματα: Αν ένας τελεστέος δεν είναι string, το μετατρέπει σε string με την εφαρμογή της toString της τάξης του. Εφαρμογή της μεθόδου concat για την συνένωση των τιμών.
Εξαγωγή συμβολοσειρών H μέθοδος substring κάνει δυνατή την εξαγωγή ενός κομματιού μιας μεγαλύτερης συμβολοσειράς παρέχοντας αριθμούς θέσης που καθορίζουν την έκταση του υποαλφαριθμητικού. όπου p1 είναι η πρώτη θέση του δείκτη στην επιθυμητή συμβολοσειρά και p2 είναι η θέση του δείκτη αμέσως μετά την τελευταία θέση της. Η γενική μορφή της substring είναι str.substring(p1, p2); Ως παράδειγμα, αν θέλετε να επιλέξετε μια υποσυμβολοσειρά "ell" από μια μεταβλητή str που περιέχει το "hello, world" θα καλούσατε την: str.substring(1, 4);
Έλεγχος Συμβολοσειρών για Ισότητα Πολλές εφαρμογές απαιτούν να ελέγξετε αν δύο συμβολοσειρές είναι ίσες, με την έννοια ότι περιέχουν τους ίδιους χαρακτήρες. Αν και μοιάζει λογικό, δεν μπορείτε να χρησιμοποιήσετε το == για το σκοπό αυτό. Ενώ είναι συντακτικά ορθό να γράψετε if (s1 == s2) . . . το if δεν θα έχει το επιθυμητό αποτέλεσμα. Όταν χρησιμοποιείτε το == σε δύο αντικείμενα, αυτό ελέγχει αν τα αντικείμενα είναι όμοια, πράγμα που σημαίνει ότι οι αναφορές τους δείχνουν την ίδια διεύθυνση. Αυτό που πρέπει να κάνετε αντ 'αυτού είναι να καλέσετε τη μέθοδο : if (s1.equals(s2)) . . .
Σύγκριση χαρακτήρων και Συμβολοσειρών Το ότι οι χαρακτήρες είναι αρχέγονοι τύποι με αριθμητική εσωτερική αναπαράσταση επιτρέπει την σύγκριση με σχεσιακούς τελεστές. Αν οι c1 και c2 είναι χαρακτήρες η έκφραση c1 < c2 είναι true αν η Unicode τιμή του c1 είναι μικρότερη του c2. H String επιτρέπει τη σύγκριση δύο συμβολοσειρών ως προς την αλφαβητική τους σειρά (βάσει της Unicode) χρησιμοποιώντας το compareTo αντί των σχεσιακών τελεστών: s1.compareTo(s2) Αυτή η κλήση επιστρέφει έναν ακέραιο μικρότερο του 0, εάν το s1 είναι μικρότερο του s2, μεγαλύτερο από 0, εάν το s1 είναι μεγαλύτερο του s2, και 0 αν οι δύο οι συμβολοσειρές είναι ίσες.
Αναζήτηση σε ένα String H Ταξη String της Java περιλαμβάνει διάφορες μεθόδους για την αναζήτηση ενός συγκεκριμένου χαρακτήρα ή ακολουθίας σε μια συμβολοσειρά. H indexOf παίρνει ή ένα string ή έναν χαρακτήρα και επιστρέφει την θέση στην οποία αυτή η τιμή πρωτοεμφανίζεται στην συμβολοσειρά. Αν το string ή ο χαρακτήρας δεν υπάρχουν καθόλου, επιστρέφει -1. π.χ. αν η str περιέχει το "hello, world": str.indexOf('h') επιστρέφει str.indexOf("o") 4 str.indexOf("ell") 1 str.indexOf('x') -1 επιστρέφει επιστρέφει επιστρέφει Η μέθοδος indexOf λαμβάνει ένα προαιρετικό δεύτερο όρισμα που δείχνει την αρχική θέση για την αναζήτηση. Ετσι: str.indexOf("o", 5) 8 επιστρέφει
Άλλες μέθοδοι της String int lastIndexOf(char ch) or lastIndexOf(String str) Επιστρέφει την θέση της τελευταίας εμφάνισης του ch, ή -1 εάν δεν υπάρχει. boolean equalsIgnoreCase(String str) Επιστρέφει true αν το string και το str είναι ίδια, αγνοώντας πεζά/κεφαλαία boolean startsWith(String str) Επιστρέφει true αν το string ξεκινά με το str. boolean endsWith(String str) Επιστρέφει true αν το τελειώνει με το str. . String replace(char c1, char c2) Επιστρέφει ένα αντίγραφο του string με όλα τα c1 αντικατεστημένα με c2. String trim() Επιστρέφει ένα αντίγραφο του string χωρίς κενά στην αρχή κα το τέλος. removed. String toLowerCase() Επιστρέφει ένα αντίγραφο του string με όλα τα κεφαλαία αλλαγμένα σε πεζά String toUpperCase() Επιστρέφει ένα αντίγραφο του string με όλα τα πεζά αλλαγμένα σε κεφαλαία.
Απλοί ιδιωματισμοί συμβολοσειρών Όταν εργάζεστε με strings, υπάρχουν δύο μοτίβα που είναι ιδιαίτερα σημαντικά : 1. Επανάληψη στους χαρακτήρες μιας συμβολοσειράς. for (int i = 0; i < str.length(); i++) { char ch = str.charAt(i); . . . code to process each character in turn . . . } 2. Μεγαλώνοντας μια συμβολοσειρά κατά ένα χαρακτήρα String result = ""; for (whatever limits are appropriate to the application) { . . . code to determine the next character to be added . . . result += ch; }
Άσκηση: Επεξεργασία Συμβολοσειράς Ως πελάτη της String, πώς θα υλοποιήσετε την toUpperCase(str) ώστε να επιστρέφει ένα αντίγραφο του str με κεφαλαία? public String toUpperCase(String str) { String result = ""; for (int i = 0; i < str.length(); i++) { char ch = str.charAt(i); result += Character.toUpperCase(ch); } return result; Υποθέστε, ότι υλοποιήτε την τάξη String. Πώς θα γράφατε τη μέθοδο indexOf(ch)? public int indexOf(char ch) { for (int i = 0; i < length(); i++) { if (ch == charAt(i)) return i; } return -1;
H Μέθοδος reverseString public void run() { println("This program reverses a string."); String str = readLine("Enter a string: "); String rev = reverseString(str); println(str + " spelled backwards is " + rev); } str STRESSED rev DESSERTS public void run() { println("This program reverses a string."); String str = readLine("Enter a string: "); String rev = reverseString(str); println(str + " spelled backwards is " + rev); } private String reverseString(String str) { String result = ""; for ( int i = 0; i < str.length(); i++ ) { result = str.charAt(i) + result; } return result; i str result rev str DESSERTS STRESSED RTS ESSERTS TS S SERTS ERTS DESSERTS SSERTS STRESSED 8 7 2 1 6 3 4 5 ReverseString This program reverses a string. Enter a string: STRESSED STRESSED spelled backwards is DESSERTS skip simulation
Μια μελέτη περίπτωσης επεξεργασίας συμβολοσειρών Θέλουμε να σχεδιάσουμε και να υλοποιήσουμε ένα πρόγραμμα για τη μετατροπή μιας πρότασης από Αγγλικά σε Pig Latin. Σε αυτή τη διάλεκτο, η Pig Latin έκδοση μιας λέξης σχηματίζεται από τους παρακάτω κανόνες: 1. Αν η λέξη αρχίζει με σύμφωνο, μετακινούμε τα αρχικά σύμφωνα στο τέλος της λέξης και στη συνέχεια προσθέτουμε την κατάληξη ay, ως εξής: scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scr am scram ay Αν η λέξη αρχίζει με ένα φωνήεν, απλά προσθέτουμε την κατάληξη way, ως εξής: 2. apple apple way
Ξεκινώντας από την αρχή Σύμφωνα με την αρχή του top-down σχεδιασμού, είναι λογικό να ξεκινήσουμε με τη μέθοδο run, που έχει την ακόλουθη μορφή ψευδοκώδικα: public void run() { Πείτε το χρήστη τι κάνει το πρόγραμμα. Ζητήστε από το χρήστη μια γραμμή κειμένου. Μεταφράστε τη γραμμή σε Pig Latin και εκτυπώσετε την στην κονσόλα. } public void run() { println("This program translates a line into Pig Latin."); String line = readLine("Enter a line: "); println(translateLine(line)); } Αυτός ο ψευτοκώδικας είναι εύκολο να μεταφραστεί σε Java, εφόσον είστε πρόθυμοι να συμπεριλάβετε μεθόδους που δεν έχετε γράψει ακόμα:
Σχεδιάζοντας την translateLine H translateLine πρέπει να διαιρεί τη γραμμή εισόδου σε λέξεις, να μεταφράσει κάθε λέξη, και στη συνέχεια να συγκεντρώσει εκ νέου αυτές τις λέξεις. Αν και δεν είναι δύσκολο να γραφεί κώδικας που χωρίζει ένα string σε λέξεις, είναι ακόμα πιο εύκολο να γίνει χρήση της βιβλιοθήκης της Java για αυτήν την εργασία. Ένας τρόπος είναι να χρησιμοποιηθεί η StringTokenizer του πακέτου java.util, που χωρίζει ένα string σε ανεξάρτητες μονάδες αποκαλούμενες tokens. Το σύνολο των tokens που παραδίδονται από τον tokenizer ονομάζεται token stream. Ο ακριβής ορισμός του τι συνιστά ένα token εξαρτάται από την εφαρμογή. Για το Pig Latin, tokens είναι είτε λέξεις είτε χαρακτήρες που διαχωρίζουν λέξεις, που ονομάζονται χαρακτήρες οριοθέτησης. Η εφαρμογή δεν μπορεί να λειτουργήσει με τις λέξεις και μόνο, επειδή οι χαρακτήρες οριοθέτησης είναι αναγκαίοι ώστε να διασφαλιστεί ότι οι λέξεις δεν θα κολλήσουν μεταξύ τους στην έξοδο.
H τάξη StringTokenizer Μια συμβολοσειρά που δείχνει την πηγή των tokens. Μια συμβολοσειρά που καθορίζει τους χαρακτήρες οριοθέτησης. Σαν προεπιλογή, οι χαρακτήρες οριοθέτησης ειναι οι χαρακτήρες κενού Ένα flag που καθορίζει εάν το tokenizer θα πρέπει να επιστρέψει τους χαρακτήρες οριοθέτησης ως μέρος της ροής token. Από προεπιλογή, μια StringTokenizer τα αγνοεί. Αφού έχετε δημιουργήσει μια StringTokenizer, μπορείτε να τη χρησιμοποιήσετε με τη δημιουργία ενός βρόχου με την ακόλουθη γενική μορφή: while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); κώδικας για την επεξεργασία του token }
H Mέθοδος translateLine Η ύπαρξη της StringTokenizer κάνει εύκολη την υλοποίηση της translateLine, με αυτό τον τρόπο: private String translateLine(String line) { String result = ""; StringTokenizer tokenizer = new StringTokenizer(line, DELIMITERS, true); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (isWord(token)) { token = translateWord(token); } result += token; return result; H σταθερά DELIMITERS είναι ένα string που περιέχει όλα τα σημεία στίξης για να διασφαλιστεί ότι δεν θα συνδυάζονται με τις λέξεις.
H Μέθοδος translateWord Η μέθοδος translateWord αποτελείται από τους κανόνες για τη δημιουργία Pig Latin λέξεων, γραμμένους σε Java : private String translateWord(String word) { int vp = findFirstVowel(word); if (vp == -1) { return word; } else if (vp == 0) { return word + "way"; } else { String head = word.substring(0, vp); String tail = word.substring(vp); return tail + head + "ay"; } Οι υπόλοιπες μέθοδοι (isWord και findFirstVowel) είναι απλές. Η προσομοίωση στην επόμενη διαφάνεια υποθέτει ότι αυτές οι μέθοδοι λειτουργούν όπως θα έπρεπε.
isthay isway igpay atinlay. isthay isway igpay atinlay Το πρόγραμμα PigLatin public void run() { println("This program translates a line into Pig Latin."); String line = readLine("Enter a line: "); println( translateLine(line) ); } line this is pig latin. public void run() { println("This program translates a line into Pig Latin."); String line = readLine("Enter a line: "); println( translateLine(line) ); } private String translateLine(String line) { String result = ""; StringTokenizer tokenizer = new StringTokenizer(line, DELIMITERS, true); while ( tokenizer.hasMoreTokens() ) { String token = tokenizer.nextToken(); if ( isWord(token) ) token = translateWord(token); result += token; } return result; line result token tokenizer this is pig latin. private String translateWord(String word) { int vp = findFirstVowel(word); if ( vp == -1 ) { return word; } else if ( vp == 0 ) { return word + "way"; } else { String head = word.substring(0, vp); String tail = word.substring(vp); return tail + head + "ay"; } word is tail head vp private String translateWord(String word) { int vp = findFirstVowel(word); if ( vp == -1 ) { return word; } else if ( vp == 0 ) { return word + "way"; } else { String head = word.substring(0, vp); String tail = word.substring(vp); return tail + head + "ay"; } word pig tail head vp private String translateWord(String word) { int vp = findFirstVowel(word); if ( vp == -1 ) { return word; } else if ( vp == 0 ) { return word + "way"; } else { String head = word.substring(0, vp); String tail = word.substring(vp); return tail + head + "ay"; } word this tail head vp private String translateWord(String word) { int vp = findFirstVowel(word); if ( vp == -1 ) { return word; } else if ( vp == 0 ) { return word + "way"; } else { String head = word.substring(0, vp); String tail = word.substring(vp); return tail + head + "ay"; } word latin tail head vp line this is pig latin. this is pig latin. this is pig latin. this is pig latin. this is pig latin. this is pig latin. this is pig latin. this is pig latin. this is pig latin. this is pig latin. igpay isthay atinlay isway latin pig this . is isthay isway igpay isthay isway igpay atinlay. isthay isway isthay isway igpay isthay isway igpay atinlay isthay isthay isway isthay this is pig latin. 2 1 1 p l th ig is atin PigLatin This program translates a line into Pig Latin. Enter a line: this is pig latin. isthay isway igpay atinlay. skip simulation
Διάβασμα για το σπίτι Κεφάλαιο 8 από «Η Τέχνη και Επιστήμη της JAVA: Μια εισαγωγή στην Επιστήμη των Υπολογιστών», E. Roberts