Aρχεία και ρεύµατα (I/O Streams) https://docs.oracle.com/javase/tutorial/essential/io/index.html υο βασικοί τύποι αρχείων που µπορούν να οριστούν είναι τα sequential flies (σειριακά αρχεία) direct access (απ'ευθείας πρόσβασης) Ένα I/O Stream/ρεύµα, αντιπροσωπεύει µια πηγή εισόδου ή εξόδου. Ένα ρεύµα µπορεί να αντιπροσωπεύει πολλά διαφορετικά είδη πηγών και προορισµών, στα οποία συµπεριλαµβανονται τα αρχεία στο δίσκο, συσκευές, άλλα προγράµµατα, και συστοιχίες µνήµης. Τα ρεύµατα υποστηρίζουν πολλά διαφορετικά είδη δεδοµένων, όπως είναι τα bytes, οι primitive τύποι δεδοµένων, οι τοπικοί χαρακτήρες και τα αντικείµενα. Μερικά από τα ρεύµατα απλώς µεταφέρουν δεδοµένα, άλλα χειρίζονται και µετατρέπουν τα δεδοµένα µε χρήσιµους τρόπους. εν έχει σηµασία πώς λειτουργούν εσωτερικά, όλα τα ρεύµατα παρουσιάζουν το ίδιο απλό µοντέλο στα προγράµµατα που τα χρησιµοποιούν: Ένα ρεύµα είναι µια ακολουθία (σειριακή ροή) δεδοµένων από µία πηγή προς έναν προορισµό. Ένα πρόγραµµα χρησιµοποιεί ένα ρεύµα εισόδου/input για να διαβάσει δεδοµένα από µια πηγή, ένα στοιχείο την φορά : Ανάγνωση πληροφοριών από µια πηγή. Ένα πρόγραµµα χρησιµοποιεί ένα ρεύµα εξόδου/οutput για να γράψει δεδοµένα σε µια πηγή, ένα στοιχείο την φορά : Γράψιµο πληροφοριών σε µια πηγή. Οι πιο πολλές I/O Streams κλάσεις είναι µέσα στο java.io package. Οι πιο πολλές File I/O κλάσεις είναι µέσα στο java.nio.file package. [1]
Class Hierarchy http://docs.oracle.com/javase/7/docs/api/java/io/package-tree.html java.lang.object java.io.console (implements java.io.flushable) java.io.file (implements java.lang.comparable<t>, java.io.serializable) java.io.filedescriptor java.io.inputstream (implements java.io.closeable) o java.io.bytearrayinputstream o java.io.fileinputstream o java.io.filterinputstream o java.io.bufferedinputstream o java.io.datainputstream (implements java.io.datainput) o java.io.linenumberinputstream o java.io.pushbackinputstream o java.io.objectinputstream (implements java.io.objectinput, java.io.objectstreamconstants) byte o java.io.pipedinputstream (8bits) o java.io.sequenceinputstream o java.io.stringbufferinputstream java.io.outputstream (implements java.io.closeable, java.io.flushable) o java.io.bytearrayoutputstream o java.io.fileoutputstream o java.io.filteroutputstream o java.io.bufferedoutputstream o java.io.dataoutputstream (implements java.io.dataoutput) o java.io.printstream (implements java.lang.appendable, java.io.closeable) o java.io.objectoutputstream (implements java.io.objectoutput, java.io.objectstreamconstants) o java.io.pipedoutputstream java.io.randomaccessfile (implements java.io.closeable, java.io.datainput, java.io.dataoutput) java.io.reader (implements java.io.closeable, java.lang.readable) o java.io.bufferedreader o java.io.linenumberreader o java.io.chararrayreader o java.io.filterreader o java.io.pushbackreader o java.io.inputstreamreader char o java.io.filereader o java.io.pipedreader (16bits) o java.io.stringreader java.io.writer (implements java.lang.appendable, java.io.closeable, java.io.flushable) o java.io.bufferedwriter o java.io.chararraywriter o java.io.filterwriter o java.io.outputstreamwriter o java.io.filewriter o java.io.pipedwriter o java.io.printwriter o java.io.stringwriter [2]
I/O Streams 1. Byte Streams χειρίζονται I/O ακατέργαστα binary δεδοµένα. ΓΙΑ δεδοµένα εικόνας(gif) FileInputStream in = new FileInputStream(" ") while ( c=in.read()!=-1 ) FileOutputStream out = new FileOutputStream(" ") out.write(c) in.close() ;out.close(); 2. Character Streams χειρίζονται I/O character δεδοµένα, µε αυτόµατη µετάφραση από και προς το τοπικό character set. ΓΙΑ δεδοµένα κειµένου (txt). FileReader in = new FileReader(" ") while ( c=in.read()!=-1 ) //int c; FileWriter out = new FileWriter(" ") out.write(c) 3. Buffered Streams βελτιστοποιούν τα I/O ελαττώνοντας τις κλήσεις προς τα native API. BufferedReader in= new BufferedReader(new FileReader(("..")); while ((l = in.readline())!= null) PrintWriter out= new PrintWriter(new FileWriter("..")); out.println(l); 4. Scanning and Formatting επιτρέπει σε ένα πρόγραµµα για να διαβάσει και να γράψει µορφοποιηµένο κείµενο Scanner s = new Scanner(new BufferedReader(new FileReader(fileIn))); while (s.hasnext()) System.out.println(s.next()); System.out.format("The square root of %d is %.3f.%n", i, r); // 2.236. 5. I/O from the Command Line περιγράφει τα Standard Streams και το αντικείµενο Console Standard Streams Τα System.in, System.out και System.err ορίζονται ως PrintStream αντικείµενα. Console Console c = System.console(); if (c == null) { System.err.println("No console."); System.exit(1); String login = c.readline("enter your login: "); char [] oldpassword = c.readpassword("enter your old password: "); 6. Data Streams χειρίζονται binary I/O για δεδοµένα των βασικών τύπων αλλά και String τιµές. in = new DataInputStream(new BufferedInputStream(new FileInputStream(fileIn))); out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileOut))); for (int i = 0; i < prices.length; i ++)out.writedouble(prices[i]); out.writeint(units[i]); out.writeutf(descs[i]); while (true) { price = in.readdouble(); //.. 7. Object Streams χειρίζονται binary I/O αντικειµένων. ObjectInputStream in; ObjectOutput Stream out; Object o = new Object(); out.writeobject(o); in.readobject(); [3]
1. Byte Streams Τα προγράµµατα συνήθως εκτελούν Ι/Ο λειτουργίες µε 8-bit, bytes. Όλες οι κλάσεις των bytes προέρχονται από τις InputStream και OutputStream. Υπάρχουν πολλές κλάσεις byte streams. Θα εστιάσουµε στα I/O byte streams FileInputStream και FileOutputStream. Τα άλλα είδη ρευµάτων byte χρησιµοποιούνται µε τον ίδιο τρόπο, αλλά διαφέρουν κυρίως ως προς τον τρόπο που κατασκευάζονται. final static String filein = "F:/txt/A.txt" ; //έστω ότι το txt αρχείο έχει μέσα το «1 2 3» final static String fileout = "F:/txt/B.txt" ; public static void CopyBytes() throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream(fileIn); out = new FileOutputStream(fileOut); int c; while ((c = in.read() )!= -1) { out.write(c) ; finally { if (in!= null) in.close(); if (out!= null) out.close(); CopyBytes είναι low-level I/O, και καλό είναι να αποφεύγεται. Αφού το αρχείο είναι text file, καλύτερα να χρησιµοποιηθεί αντί για bytestream, character stream. 2. Character Stream Όλες οι κλάσεις των character stream προέρχονται από τις Reader και Writer Εδώ αλλάζει και αντί FileInputStream µπαίνει FileReader και αντί FileOutputStream µπαίνει FileWriter public static void CopyCharacters() throws IOException { FileReader in = null; FileWriter out = null; try { in = new FileReader(fileIn); //έστω ότι το txt αρχείο έχει μέσα το «APPLE» [4] //Αντιγράφει το αρχείο in στο οut Αντιγράφει ένα byte την φορά Ascii value Η read επιστρέφει ένα ακέραιο, που αντιστοιχεί σε ένα byte του stream in (49 32 50 32 51-1) Eπιστρέφει -1, όταν συναντήσει το τέλος του αρχείου Η write γράφει ένα μόνο byte στο stream out
out = new FileWriter(fileOut); int c; while ((c = in.read() )!= -1) { out.write(c) ; finally { if (in!= null) in.close(); if (out!= null) out.close(); Αντιγράφει 1 χαρακτήρα την φορά Ascii value Η read επιστρέφει ένα ακέραιο, που αντιστοιχεί σε ένα χαρακτήρα, του stream in (65 80 80 76 69) Eπιστρέφει -1, όταν συναντήσει το τέλος του αρχείου Η write γράφει ένα μόνο χαρακτήρα στο stream out 3. Buffered Streams Στα προηγούµενα παραδείγµατα έγινε η χρήση unbuffered I/O. Αυτό σηµαίνει ότι κάθε αίτηση για ανάγνωση ή εγγραφή χειρίζεται απευθείας από το λειτουργικό σύστηµα που είναι από κάτω. Αυτό µπορεί να κάνει ένα πρόγραµµα πολύ λιγότερο αποτελεσµατικό, δεδοµένου ότι κάθε τέτοιο αίτηµα απαιτεί συχνά ή πρόσβαση στο δίσκο, ή δραστηριότητα δικτύου, ή κάποια άλλη ενέργεια που είναι σχετικά «ακριβή». Για αυτό η πλατφόρµα Java θέτει σε εφαρµογή τα buffered I/O ρεύµατα εισόδου, που διαβάζουν δεδοµένα από µια περιοχή µνήµης γνωστή ως buffer, ενώ η φυσική API είσοδος, καλείται µόνο όταν το buffer είναι άδειο. Οµοίως, τα buffered I/O ρεύµατα εξόδου, γράφουν δεδοµένα σε ένα buffer, και η φυσική έξοδος API, καλείται µόνο όταν το buffer είναι πλήρες. Ένα πρόγραµµα µπορεί να µετατρέψει ένα unbuffered I/O ρεύµα µέσα σε ένα buffered I/O ρεύµα χρησιµοποιώντας το ιδίωµα, όπου το unbuffered I/O ρεύµα περνά στον δοµητή του buffered I/O ρεύµατος. Ετσι µπορείτε να τροποποιήσετε τις κλήσεις του δοµητή στο παράδειγµα CopyCharacters ώστε να χρησιµοποιούν buffered I / O: in = new ΒufferedReader(FileReader(fileIn)); out = new ΒufferedWriter (FileWriter(fileOut)); Υπάρχουν 4 κατηγορίες buffered I/O ρευµάτων που χρησιµοποιούνται για να µετατρέψουν τα unbuffered ρεύµατα: BufferedInputStream και BufferedOutputStream δηµιουργούν buffered I/O ρεύµατα bytes, ενώ BufferedReader και BufferedWriter δηµιουργούν buffered I/O ρεύµατα ρεύµατα χαρακτήρα. Flushing Buffered Streams Συχνά έχει νόηµα να γραφτεί ένας buffer σε κρίσιµα σηµεία, χωρίς να περιµένουµε για να γεµίσει. Αυτό είναι γνωστό ως flushing the buffer. Μερικές buffered κλάσεις εξόδου, υποστηρίζουν autoflush, που καθορίζεται από µια προαιρετική παράµετρο στο δοµητή. Όταν το autoflush είναι ενεργοποιηµένο, ορισµένα βασικά γεγονότα, προκαλούν εκκένωση/flush του buffer. [5]
Για παράδειγµα, ένα αντικείµενο autoflush PrintWriter, εκκενώνει τον buffer σε κάθε κλήση του println ή format. public static void CopyLines () throws IOException { BufferedReader in = null; PrintWriter out= null; try in= new BufferedReader(new FileReader(fileIn)); out= new PrintWriter(new FileWriter(fileOut)); String line; while ((line = in.readline())!= null) { out.println(l); finally { if (in!= null) in.close(); if (out!= null) out.close(); 4. Scanning & Formatting Scanning Το Scanner είναι χρήσιµο για κόψιµο των formatted input σε κοµµάτια/tokens και µετάφραση των tokens ανάλογα µε τον τύπο των δεδοµένων τους. Από προεπιλογή, ένας scanner χρησιµοποιεί τα κενά (κενό/κενά, tab, line terminators) για να ξεχωρίζει τα tokens. public static void printwords() throws IOException { Scanner s = null; try { s = new Scanner(new BufferedReader(new FileReader(fileIn))); while (s.hasnext()) { System.out.println(s.next()); finally { if (s!= null) s.close(); Αν και ένας Scanner δεν είναι ένα ρεύµα, θα πρέπει να κλείσει για να δείξει ότι είvαι έτοιµο το υποκείµενο ρεύµα. [6]
Formatting Αντικείμενα stream που εφαρμόζουν μορφοποίηση είναι instances είτε PrintWriter (κατηγορία ρευμάτων χαρακτήρα), ή PrintStream (μια κατηγορία ρευμάτων byte). Σημείωση: Το μόνο PrintStream αντικείμενo που χρειάζεστε είναι το System.out και System.err. Όταν θέλετε να δημιουργήσετε ένα μορφοποιημένο ρεύμα εξόδου, θα βάζετε PrintWriter όχι PrintStream. Οι PrintStream και PrintWriter instances έχουν ένα τυποποιημένο σύνολο μεθόδων εγγραφής για εξόδους απλών byte και χαρακτήρα. Επιπλέον, τόσο οι PrintStream όσο και οι PrintWriter εφαρμόζουν το ίδιο σύνολο των μεθόδων για τη μετατροπή των εσωτερικών δεδομένων σε μορφοποιημένη έξοδο. Παρέχονται δύο επίπεδα μορφοποίησης: print και println, μορφοποιούν μεμονωμένες τιμές με τυποποιημένο τρόπο. Format, μορφοποιεί σχεδόν οσεσδήποτε τιμές, βασισμένο σε ένα μορφοποιημένο string, με πολλές επιλογές για την ακριβή μορφοποίηση. int i; double r = Math.sqrt(5); System.out.println("The square root of " + i + " is " + r + "."); // 2.23606797749979. System.out.format("The square root of %d is %f.%n", i, r); // 2.236068. System.out.format("The square root of %d is %.3f.%n", i, r); // 2.236. 5. IO from the Command Line Η Java πλατφόρµα υποστηρίζει αυτό το είδος της αλληλεπίδρασης µε δύο τρόπους, µέσω των Standard Streams και µέσω της Console. Standard Streams Τα Standard Streams είναι χαρακτηριστικό πολλών λειτουργικών συστηµάτων. Από προεπιλογή, διαβάζουν είσοδο από το πληκτρολόγιο και εµφανίζουν/γράφουν την έξοδο στην οθόνη. Υποστηρίζουν, επίσης, I / O σε αρχεία και µεταξύ των προγραµµάτων, αλλά η λειτουργία τους ελέγχεται από τον διερµηνέα γραµµών εντολής, και όχι από το πρόγραµµα. Η πλατφόρµα Java υποστηρίζει 3 Standard Streams: Standard Input, πρόσβαση µέσω System.in Standard Output, πρόσβαση µέσω System.out Standard Error, πρόσβαση µέσω System.err Αυτά τα αντικείµενα καθορίζονται αυτόµατα και δεν χρειάζεται να τα ανοίξεις. Το Standard Output και το Standard Error και τα δυο για την έξοδο, έχοντας το Standard Error χωριστά επιτρέπει στο χρήστη να εκτρέψει την τακτική έξοδο σε ένα αρχείο και να εξακολουθεί να είναι σε θέση να διαβάσει τα µηνύµατα λάθους. Θα περιµένατε τα Standard Streams να είναι ρεύµατα χαρακτήρα, αλλά, για ιστορικούς λόγους, είναι ρεύµατα byte. Τα System.out και System.err ορίζονται ως PrintStream αντικείµενα. Παρόλο που είναι τεχνικά ένα ρεύµα byte, η PrintStream χρησιµοποιεί ένα εσωτερικό ρεύµα [7]
χαρακτήρα ως αντικείµενο ώστε να µιµηθεί πολλά από τα χαρακτηριστικά των ρευµάτων χαρακτήρα. Αντίθετα, το System.in είναι µια ροή byte χωρίς χαρακτηριστικά ρεύµατος χαρακτήρα. Για να χρησιµοποιήσετε την κανονική είσοδο σαν ένα ρεύµα χαρακτήρα, βάλτε το System.in µέσα σε InputStreamReader. InputStreamReader cin = new InputStreamReader(System.in) Console Μια πιο προηγµένη εναλλακτική λύση στα Standard Streams είναι το Console. Είναι ένα µοναδικό, προκαθορισµένο αντικείµενο τύπου Console που έχει τις περισσότερες από τις δυνατότητες που παρέχονται από το Standard Streams, και άλλα εκτός από αυτά. Το Console είναι ιδιαίτερα χρήσιµο για την ασφαλή εισαγωγή ενός κωδικού πρόσβασης. Το αντικείµενο Console παρέχει επίσης ρεύµατα εισόδου και εξόδου που είναι αληθινά ρεύµατα χαρακτήρα, µέσω των reader και writer µεθόδων του. Πριν µπορέσει να χρησιµοποιήσει Console ένα πρόγραµµα, θα πρέπει να προσπαθήσει να ανακτήσει το αντικείµενο Console µε την κλήση System.console(). Αν το αντικείµενο Console είναι διαθέσιµο, η µέθοδος αυτή το επιστρέφει. Αν η System.console, επιστρέφει null, τότε οι λειτουργίες του δεν επιτρέπονται, ή επειδή το λειτουργικό σύστηµα δεν το υποστηρίζει ή επειδή το πρόγραµµα ξεκίνησε σε ένα περιβάλλον µη αλληλεπιδραστικό (noninteractive). Το αντικείµενο Console υποστηρίζει την ασφαλή εισαγωγή του κωδικού πρόσβασης µέσω της µεθόδου του readpassword. Αυτή η µέθοδος βοηθά στην ασφαλή εισαγωγή κωδικού πρόσβασης µε δύο τρόπους. Πρώτον, καταστέλλει την ηχώ, έτσι ώστε ο κωδικός πρόσβασης να µην είναι ορατός στην οθόνη του χρήστη. εύτερον, το readpassword επιστρέφει ένα Πίνακα χαρακτήρων, όχι ένα String, έτσι ώστε ο κωδικός πρόσβασης µπορεί να επαναγραφεί, αφαιρώντας τον από τη µνήµη, όταν πλέον δεν είναι απαραίτητος. public static void changepassword () throws IOException { Console c = System.console(); if (c == null) { System.err.println("No console."); System.exit(1); String login = c.readline("enter your login: "); char [] oldpassword = c.readpassword("enter your old password: "); if (verify(login, oldpassword)) { //if true boolean nomatch; do { char [] newpassword1 = c.readpassword("enter your new password: "); char [] newpassword2 = c.readpassword("enter new password again: "); nomatch =! Arrays.equals(newPassword1, newpassword2); if (nomatch) { c.format("passwords don't match. Try again.%n"); else { change(login, newpassword1); //ότι κάνει.. [8]
c.format("password for %s changed.%n", login); Arrays.fill(newPassword1, ' '); Arrays.fill(newPassword2, ' '); while (nomatch); Arrays.fill(oldPassword, ' '); 6. Data Streams Τα data streams υποστηρίζουν binary I/O για δεδοµένα µε τιµές των βασικών τύπων (boolean, char, byte, short, int, long, float, και double) αλλά και µε String τιµές. Όλα τα data streams υλοποιούν ή το DataInput ή το DataOutput interface. Οι πιο γνωστές υλοποιήσεις είναι η DataInputStream and η DataOutputStream. Επειδή ένα DataOutputStream µπορεί να δηµιουργηθεί µόνο σαν «περιτύλιγµα/wrapper» ενός υπάρχοντος byte stream object, τα DataStreams παρέχουν ένα buffered file output byte stream. static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 ; DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileOut))); for (int i = 0; i < prices.length; i ++) { out.writedouble(prices[i]); out.writeint(units[i]); out.writeutf(descs[i]); 7. Οbject Streams The object stream κλάσεις είναι οι ObjectInputStream και ObjectOutputStream. Το παράδειγµα µε ObjectStreams (αµέσως µετά) µοιάζει µε το προηγούµενο των DataStreams, µε την διαφορά π.χ. ότι τα prices είναι τώρα BigDecimal objects, ώστε να εκπροσωπούνται καλύτερα οι κλασµατικές τιµές. static final BigDecimal[] prices = { new BigDecimal("19.99"), ObjectOutputStream out = null; try { out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream (fileout))); out.writeobject(calendar.getinstance()); for (int i = 0; i < prices.length; i ++) { out.writeobject(prices[i]); out.writeint(units[i]); out.writeutf(descs[i]); finally {out.close(); [9]
Διαχείριση Αρχείων μέσω της κλάσης File To java.nio.file package και τα σχετικά package, java.nio.file.attribute, παρέχουν υποστήριξη στα file I/O και στην προσπέλαση του default file system. Η κλάση Path είναι µια βασική κλάση στο java.nio.file package. Creating a Path Path p1 = Paths.get("/tmp/foo"); Path p2 = Paths.get(args[0]); Path p3 = Paths.get(URI.create("file:///Users/joe/FileTest.java")); Path p4 = FileSystems.getDefault().getPath("/users/sally"); Path p5 = Paths.get(System.getProperty("user.home"),"logs", "foo.log"); Η κλάση Files είναι άλλη µια βασική κλάση στο java.nio.file package Reading, Writing, Creating, and Opening files Μπορείτε να επιλέξετε από ένα ευρύ φάσµα I/O µεθόδων αρχείων. Οι µέθοδοι αυτοί, σύµφωνα µε την πολυπλοκότητα τους (από το λιγότερο προς το περισσότερο), είναι: readallbytes readalllines Το πιο συνηθισμένο, για μικρά αρχεία new BufferedReader new BufferedWriter Αρχεία Κειμένου new InputStream new OutputStream Streams, unbuffereds, χρήση με ΑPIs new ByteChannel Channels and ByteBuffers FileChannel Πιο προηγμένα χαρακτηριστικά 1. ΑΝΑΓΝΩΣΗ ΜΕ ΜΙΑΣ: Files.readAllBytes(fileName) Αν έχετε ένα µικρό αρχείο και θα θέλατε να διαβάσετε ολόκληρο το περιεχόµενό µε ένα πέρασµα, µπορείτε να χρησιµοποιήσετε τις readallbytes(path) ή readalllines(path, Charset). [10]
Αυτές οι µέθοδοι φροντίζουν για το µεγαλύτερο µέρος της εργασίας σας, όπως άνοιγµα και κλείσιµο της ροής, αλλά δεν προορίζονται για το χειρισµό µεγάλων αρχείων. Μπορείτε να χρησιµοποιήσετε µία από τις µεθόδους που γράφουν για να γράψετε bytes, ή γραµµές, σε ένα αρχείο. write(path, byte[], OpenOption...) write(path, Iterable< extends CharSequence>, Charset, OpenOption...) Ο κώδικας που ακολουθεί δείχνει πώς χρησιµοποιούν τη µέθοδο readallbytes Path p =...; byte[] data; //ορίζει ένα πίνακα από bytes Aνάγνωση data =Files.readAllBytes(p); Εγγραφή Files.write(p, data); 2.Reader Buffered InputStream File BUFFERED I/O Mέθοδοι για αρχεία ΤΕΧΤ Aνάγνωση Charset charset = Charset.forName("US-ASCII"); try (BufferedReader br = Files.newBufferedReader(p, charset)) { String line = null; while ((line = br.readline())!= null) { System.out.println(line); catch (IOException x) { System.err.format("IOException: %s%n", x); Εγγραφή String s =...; try (BufferedWriter wr = Files.newBufferedWriter(fileName, charset)) { wr.write(s, 0, s.length()); catch (IOException x) { System.err.format("IOException: %s%n", x); I/O Mέθοδοι για UNBUFFERED STREAMS Aνάγνωση Path p =...; try (InputStream in = Files.newInputStream(p); [11]
BufferedReader br = new BufferedReader(new InputStreamReader(in))) { String line = null; while ((line = br.readline())!= null) { System.out.println(line); catch (IOException x) { System.err.println(x); Eγγραφή String s = "Hello World! "; byte δεδοµένα[] = s.getbytes(); try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(p,CREATE, APPEND))) { out.write(δεδοµένα, 0, δεδοµένα.length); catch (IOException x) { System.err.println(x); [12]