| Both sides previous revision
Previous revision
Next revision
|
Previous revision
Next revision
Both sides next revision
|
java:byte_streams [2020/02/27 20:52] gthanos [Ροές Δυαδικών Δεδομένων] |
java:byte_streams [2020/03/08 22:53] gthanos [Buffered Streams] |
| |
| Για να δείξουμε πώς δουλεύουν τα byte streams, θα ξεκινήσουμε από τις κλάσεις **[[http://docs.oracle.com/javase/7/docs/api/java/io/FileInputStream.html|FileInputStream]]** και **[[http://docs.oracle.com/javase/7/docs/api/java/io/FileOutputStream.html|FileOutputStream]]** που είναι οι βασικότερες. Άλλες κλάσεις χτίζουν πάνω σε αυτές προκειμένου να προσφέρουν επιπλέον λειτουργικότητα. | Για να δείξουμε πώς δουλεύουν τα byte streams, θα ξεκινήσουμε από τις κλάσεις **[[http://docs.oracle.com/javase/7/docs/api/java/io/FileInputStream.html|FileInputStream]]** και **[[http://docs.oracle.com/javase/7/docs/api/java/io/FileOutputStream.html|FileOutputStream]]** που είναι οι βασικότερες. Άλλες κλάσεις χτίζουν πάνω σε αυτές προκειμένου να προσφέρουν επιπλέον λειτουργικότητα. |
| | |
| ==== Χρησιμοποιώντας Byte Streams ==== | ==== Χρησιμοποιώντας Byte Streams ==== |
| |
| Θα εξερευνήσουμε τις κλάσεις FileInputStream και FileOutputStream με χρήση του παρακάτω παραδείγματος //CopyBytes//, το οποίο λαμβάνει προς αντιγραφή ένα αρχείο από την γραμμή εντολών. Μπορείτε να χρησιμοποιήσετε ένα αρχείο κειμένου (π.χ. [[xanadu.txt]]) ή οποιαδήποτε [[http://bellard.org/bpg/lena30.jpg|εικόνα]]. | Θα εξερευνήσουμε τις κλάσεις FileInputStream και FileOutputStream με χρήση του παρακάτω παραδείγματος //CopyBytes//, το οποίο λαμβάνει προς αντιγραφή ένα αρχείο από την γραμμή εντολών. Μπορείτε να χρησιμοποιήσετε ένα αρχείο κειμένου (π.χ. [[xanadu.txt]]), μία εικόνα ή οποιοδήποτε άλλο αρχείο για να το εκτελέσετε. Το παράδειγμα χρησιμοποιεί την εικόνα [[http://optipng.sourceforge.net/pngtech/img/lena.png|lena.png]]. |
| |
| <code java CopyBytes.java> | <code java CopyBytes.java> |
| public static void main(String[] args) throws IOException { | public static void main(String[] args) throws IOException { |
| |
| FileInputStream in = null; | String filename = "lena.png"; |
| FileOutputStream out = null; | |
| |
| try { | try (FileInputStream in = new FileInputStream(filename); |
| String filename; | FileOutputStream out = new FileOutputStream("__"+filename)) { |
| if(args.length > 0) | |
| filename = args[0]; | |
| else | |
| filename = "xanadu.txt"; | |
| | |
| in = new FileInputStream(filename); | |
| out = new FileOutputStream("__"+filename); | |
| int c; | int c; |
| |
| out.write(c); | out.write(c); |
| } | } |
| } finally { | } |
| if (in != null) { | |
| in.close(); | |
| } | |
| if (out != null) { | |
| out.close(); | |
| } | |
| } | |
| } | } |
| } | } |
| </code> | </code> |
| |
| Η μέθοδος main της κλάσης //CopyBytes// περνάει τον περισσότερο χρόνο επεξεργασίας μέσα σε ένα βρόχο που διαβάζει από το ρεύμα εισόδου και γράφει στο ρεύμα εξόδου ένα byte σε κάθε ανακύκλωση, όπως φαίνεται στην παρακάτω εικόνα. | Η μέθοδος main της κλάσης //CopyBytes// περνάει τον περισσότερο χρόνο επεξεργασίας μέσα σε ένα βρόχο που διαβάζει από το ρεύμα εισόδου και γράφει στο ρεύμα εξόδου ένα byte σε κάθε επανάληψη. |
| |
| {{ :java:bytestream.gif |}} | |
| |
| <WRAP tip 80% center round> | ===== Διαβάζοντας και γράφοντας περισσότερα από ένα bytes ===== |
| |
| Το κλείσιμο των streams που ανοίγουν είναι εξαιρετικά σημαντικό. Η κλάση //CopyBytes// χρησιμοποιεί ένα //τελικό// block για να εγγυηθεί ότι και τα δύο stream θα κλείσουν ακόμα και αν παρουσιαστεί κάποιο σφάλμα. Η πρακτική αυτή βοηθά στην αποφυγή διαρροής πόρων. | Το παράδειγμα που είδαμε προηγουμένως το πρόγραμμα διαβάζει και γράφει ένα byte κάθε φορά. Παρακάτω θα δούμε το ίδιο πρόγραμμα, μόνο που αυτή τη φορά διαβάζουμε και γράφουμε περισσότερα του ενός bytes σε κάθε εντολή read και write. |
| |
| Ένα πιθανό σφάλμα είναι ότι το //CopyBytes// __δεν__ μπόρεσε να ανοίξει το ένα ή και τα δύο αρχεία. Σε αυτή την περίπτωση θα παραχθεί ένα //I/O Exception//. Όταν συμβαίνει αυτό, η μεταβλητή που αντιστοιχεί στο stream που απέτυχε να ανοίξει θα έχει την τιμή **null**. Αυτός είναι ο λόγος για τον οποίο η κλάση //CopyBytes// φροντίζει ώστε κάθε μεταβλητή τύπου stream να περιέχει μια αναφορά αντικειμένου πριν από την κλήση του //close//. | <code java CopyBytes.java> |
| </WRAP> | import java.io.*; |
| |
| ==== Σε ποιες περιπτώσεις να μην χρησιμοποιείτε τα Byte Streams ==== | public class CopyBytes { |
| | public static void main(String[] args) throws IOException { |
| |
| Είδαμε ότι το παραπάνω πρόγραμμα δουλεύει τόσο με αρχεία κειμένου όσο και με δυαδικά αρχεία. Στις περιπτώσεις που θέλετε να γράψετε δυαδικά αρχεία, τα byte streams δουλεύουν εξαιρετικά. Εάν πάλι θέλετε να διαβάσετε και να γράψετε αρχεία κειμένου αφού προηγουμένως επεξεργαστείτε την πληροφορία κειμένου που διαβάσατε τότε τα [[java:character_streams|streams χαρακτήρων]] (θα δούμε στη συνέχεια) είναι πιο ενδεδειγμένα. | String filename = "lena.png"; |
| |
| ===== Διαβάζοντας και γράφοντας περισσότερα από ένα bytes σε δυαδικό αρχείο ===== | try (FileInputStream in = new FileInputStream(filename); |
| | FileOutputStream out = new FileOutputStream("__"+filename)) { |
| | |
| | byte []array = new byte[512]; |
| | int length; |
| | |
| | while ((length = in.read(array)) != -1) { |
| | System.out.println(length); |
| | out.write(array, 0, length); |
| | } |
| | } |
| | } |
| | } |
| | </code> |
| | |
| | ===== Buffered Streams ===== |
| |
| Το παράδειγμα που είδαμε προηγουμένως το πρόγραμμα διαβάζει και γράφει ένα byte κάθε φορά. Προκειμένου να μπορούμε να χειριζόμαστε περισσότερα δεδομένα σε κάθε κλήση συστήματος, η πλατφόρμα της Java υλοποιεί τις κλάσεις [[http://docs.oracle.com/javase/7/docs/api/java/io/BufferedInputStream.html|BufferedInputStream]] και [[http://docs.oracle.com/javase/7/docs/api/java/io/BufferedOutputStream.html|BufferedOutputStream]] για την διαχείριση ροών δεδομένων όπου διαβάζονται ή γράφονται περισσότερα του ενός byte κάθε φορά. | Στα παραπάνω streams, υπάρχει η δυνατότητα να χρησιμοποιήσετε επιπλέον buffered streams, δηλαδή streams που κάνουν εσωτερικά buffering ώστε να βελτιστοποιήσουν την διαδικασία ανάγνωσης εγγραφής. Οι κλάσεις αυτές είναι οι [[https://docs.oracle.com/javase/8/docs/api/java/io/BufferedInputStream.html|BufferedInputStream]] και [[https://docs.oracle.com/javase/8/docs/api/java/io/BufferedOutputStream.html|BufferedOutputStream]]. Το μέγεθος του χρησιμοποιούμενου buffer είναι παραμετροποιήσιμο στον κατασκευαστή των παραπάνω κλάσεων. Προτείνεται να μην επιλέγετε εσείς το μέγεθος του buffer (εκτός αν ξέρετε τι κάνετε...). |
| |
| Παρακάτω θα ξαναγράψουμε το πρόγραμμα CopyBytes μόνο που αυτή τη φορά το διάβασμα των bytes δεν θα γίνει ένα-ένα (unbuffered), αλλά θα διαβάζονται και θα γράφονται ακολουθίες των 512 bytes με την βοήθεια των κλάσεων [[http://docs.oracle.com/javase/7/docs/api/java/io/BufferedInputStream.html|BufferedInputStream]] και [[http://docs.oracle.com/javase/7/docs/api/java/io/BufferedOutputStream.html|BufferedOutputStream]]. | Παρακάτω δίνεται το προηγούμενο παράδειγμα χρησιμοποιώντας τις κλάσεις [[https://docs.oracle.com/javase/8/docs/api/java/io/BufferedInputStream.html|BufferedInputStream]] και [[https://docs.oracle.com/javase/8/docs/api/java/io/BufferedOutputStream.html|BufferedOutputStream]]. |
| |
| <code java CopyBufferedBytes.java> | <code java CopyBufferedBytes.java> |
| public static void main(String[] args) throws IOException { | public static void main(String[] args) throws IOException { |
| |
| BufferedInputStream in = null; | String filename = "lena.png"; |
| BufferedOutputStream out = null; | |
| final int buffer_size = 256; | |
| byte []buffer = new byte[buffer_size]; | |
| |
| try { | try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(filename)); |
| String filename; | BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("__"+filename)) ) { |
| if(args.length > 0) | |
| filename = args[0]; | |
| else | |
| filename = "xanadu.txt"; | |
| | |
| in = new BufferedInputStream(new FileInputStream(filename)); | byte []array = new byte[512]; |
| out = new BufferedOutputStream(new FileOutputStream("__"+filename)); | int length; |
| int size; | |
| |
| while ((size = in.read(buffer, 0, buffer_size)) != -1) { | while ((length = in.read(array)) != -1) { |
| out.write(buffer, 0, size); | System.out.println(length); |
| | out.write(array, 0, length); |
| } | } |
| } finally { | } |
| if (in != null) { | |
| in.close(); | |
| } | |
| if (out != null) { | |
| out.close(); | |
| } | |
| } | |
| } | } |
| } | } |
| </code> | </code> |
| |
| |Προηγούμενο: [[:java:file_io_intro | Είσοδος και έξοδος αρχείων ]] | [[:toc | Περιεχόμενα ]] | Επόμενο: [[:java:character_streams | Ροές Χαρακτήρων]] | | <WRAP tip 80% center round> |
| | O ρόλος του buffer είναι πάντα η βελτιστοποίηση της απόδοσης. Για παράδειγμα, διαβάζοντας από το δίσκο είναι γνωστό ότι ο δίσκος διαβάζει σε τμήματα των 2Κ ή 4Κ κατ' ελάχιστο. Ακόμη και εάν εσείς διαβάζετε λιγότερα δεδομένα κάθε φορά, χρησιμοποιώντας ένα buffered stream επιτυγχάνετε η κάθε σελίδα του δίσκου να προσπελαστεί μόνο μία φορά και η συνολική πληροφορία της σελίδας να αποθηκευτεί στο buffer. |
| | </WRAP> |
| | |
| | |Προηγούμενο: [[:java:file_io_intro | Είσοδος και έξοδος αρχείων ]] | [[:toc | Περιεχόμενα ]] | Επόμενο: [[:java:byte_streams_to_data | [[:java:byte_streams_to_data|Μετασχηματισμός των ροών δεδομένων σε τύπους δεδομένων]] | |
| |