1. Flux de données

Il est rare qu'un programme existe comme une île en soi. Les programmes interagissent généralement d'une manière ou d'une autre avec le "monde extérieur". Cela peut se produire en lisant des données à partir du clavier, en envoyant des messages, en téléchargeant des pages depuis Internet ou, à l'inverse, en téléchargeant des fichiers sur un serveur distant.

Nous pouvons résumer tous ces comportements en un mot : échange de données entre le programme et le monde extérieur. Attendez, ce n'est pas juste un mot.

Bien sûr, l'échange de données lui-même peut être divisé en deux parties : la réception de données et l'envoi de données. Par exemple, vous lisez des données à partir du clavier à l'aide d'un Scannerobjet — celui-ci reçoit des données. Et vous affichez des données à l'écran à l'aide d'une System.out.println()commande - c'est l'envoi de données.

En programmation, le terme "flux" est utilisé pour décrire l'échange de données. D'où vient ce terme ?

Dans la vraie vie, vous pouvez avoir un courant d'eau ou un courant de conscience. En programmation, nous avons des flux de données .

Les flux sont un outil polyvalent. Ils permettent au programme de recevoir des données de n'importe où (flux d'entrée) et d'envoyer des données n'importe où (flux de sortie). Ainsi, il existe deux types :

  • Un flux d'entrée sert à recevoir des données
  • Un flux de sortie sert à envoyer des données

Pour rendre les flux "tangibles", les créateurs de Java ont écrit deux classes : InputStreamet OutputStream.

La InputStreamclasse a une read()méthode qui vous permet de lire des données à partir de celle-ci. Et la OutputStreamclasse a une write()méthode qui vous permet d'y écrire des données. Ils ont également d'autres méthodes, mais nous en reparlerons plus tard.

Flux d'octets

De quel type de données parle-t-on ? Quel format prend-il ? En d'autres termes, quels types de données ces classes prennent-elles en charge ?

Ce sont des classes génériques, elles prennent donc en charge le type de données le plus courant — le byte. Un OutputStreampeut écrire des octets (et des tableaux d'octets), et un InputStreamobjet peut lire des octets (ou des tableaux d'octets). C'est tout - ils ne prennent en charge aucun autre type de données.

Par conséquent, ces flux sont également appelés flux d'octets .

Une caractéristique des flux est que leurs données ne peuvent être lues (ou écrites) que séquentiellement. Vous ne pouvez pas lire des données au milieu d'un flux sans lire toutes les données qui le précèdent.

C'est ainsi que la lecture des données du clavier fonctionne dans la Scannerclasse : vous lisez les données du clavier de manière séquentielle, ligne par ligne. Nous lisons une ligne, puis la ligne suivante, puis la ligne suivante, et ainsi de suite. À juste titre, la méthode de lecture des lignes s'appelle nextLine().

L'écriture de données dans un OutputStreamse produit également de manière séquentielle. Un bon exemple de ceci est la sortie de la console. Vous sortez une ligne, suivie d'une autre et d'une autre. Il s'agit d'une sortie séquentielle. Vous ne pouvez pas afficher la première ligne, puis la dixième, puis la seconde. Toutes les données sont écrites dans un flux de sortie uniquement de manière séquentielle.

Flux de caractères

Vous avez récemment appris que les chaînes sont le deuxième type de données le plus populaire, et elles le sont effectivement. De nombreuses informations sont transmises sous forme de caractères et de chaînes entières. Un ordinateur excelle dans l'envoi et la réception de tout sous forme d'octets, mais les humains ne sont pas si parfaits.

Tenant compte de ce fait, les programmeurs Java ont écrit deux autres classes : Readeret Writer. La Readerclasse est analogue à la InputStreamclasse, mais sa read()méthode lit non pas des octets, mais des caractères ( char). La Writerclasse correspond à la OutputStreamclasse. Et tout comme la Readerclasse, cela fonctionne avec des caractères ( char), pas des octets.

Si nous comparons ces quatre classes, nous obtenons l'image suivante :

Octets (octet) Caractères (char)
Lecture des données
InputStream
Reader
Données d'écriture
OutputStream
Writer

Application pratique

Les classes InputStream, et elles-mêmes ne sont utilisées directement par personne, car elles ne sont associées à aucun objet concret à partir duquel des données peuvent être lues (ou dans lesquelles des données peuvent être écrites) OutputStream. Mais ces quatre classes ont beaucoup de classes descendantes qui peuvent faire beaucoup.ReaderWriter


2. InputStreamclasse

La InputStreamclasse est intéressante car c'est la classe mère de centaines de classes descendantes. Il n'a pas de données propres, mais il a des méthodes dont héritent toutes ses classes dérivées.

En général, il est rare que des objets de flux stockent des données en interne. Un flux est un outil de lecture/écriture de données, mais pas de stockage. Cela dit, il y a des exceptions.

Méthodes de la InputStreamclasse et de toutes ses classes descendantes :

Méthodes Description
int read()
Lit un octet du flux
int read(byte[] buffer)
Lit un tableau d'octets à partir du flux
byte[] readAllBytes()
Lit tous les octets du flux
long skip(long n)
Ignore nles octets dans le flux (les lit et les supprime)
int available()
Vérifie le nombre d'octets restants dans le flux
void close()
Ferme le flux

Passons brièvement en revue ces méthodes :

read()méthode

La read()méthode lit un octet du flux et le renvoie. Vous pourriez être confus par le inttype de retour. Ce type a été choisi car intil s'agit du type entier standard. Les trois premiers octets de intseront zéro.

read(byte[] buffer)méthode

C'est la deuxième variante de la read()méthode. Il vous permet de lire un tableau d'octets à partir d'un InputStreamtout à la fois. Le tableau qui stockera les octets doit être passé en argument. La méthode renvoie un nombre — le nombre d'octets réellement lus.

Supposons que vous disposiez d'un tampon de 10 kilo-octets et que vous lisiez des données à partir d'un fichier à l'aide de la FileInputStreamclasse. Si le fichier ne contient que 2 kilo-octets, toutes les données seront chargées dans le tableau de tampons et la méthode renverra le nombre 2048 (2 kilo-octets).

readAllBytes()méthode

Une très bonne méthode. Il lit simplement toutes les données du InputStreamjusqu'à ce qu'il soit épuisé et les renvoie sous la forme d'un tableau à un seul octet. C'est très pratique pour lire de petits fichiers. Les fichiers volumineux peuvent ne pas tenir physiquement dans la mémoire et la méthode lèvera une exception.

skip(long n)méthode

Cette méthode vous permet d'ignorer les n premiers octets de l' InputStreamobjet. Étant donné que les données sont lues de manière strictement séquentielle, cette méthode lit simplement les n premiers octets du flux et les supprime.

Renvoie le nombre d'octets qui ont été réellement ignorés (au cas où le flux s'est terminé avant que nles octets aient été ignorés).

int available()méthode

La méthode renvoie le nombre d'octets qui restent dans le flux

void close()méthode

Le close()procédé ferme le flux de données et libère les ressources externes qui lui sont associées. Une fois qu'un flux est fermé, plus aucune donnée ne peut être lue à partir de celui-ci.

Écrivons un exemple de programme qui copie un très gros fichier. Nous ne pouvons pas utiliser la readAllBytes()méthode pour lire le fichier entier en mémoire. Exemple:

Code Note
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = new byte[65536]; // 64Kb
   while (input.available() > 0)
   {
      int real = input.read(buffer);
      output.write(buffer, 0, real);
   }
}



InputStreampour lire depuis le fichier
OutputStreampour écrire dans le fichier

Tampon dans lequel nous allons lire les données
Tant qu'il y a des données dans le flux

Lire les données dans le tampon
Écrire les données du tampon dans le second flux

Dans cet exemple, nous avons utilisé deux classes : FileInputStreamest un descendant de InputStreampour lire des données dans un fichier et FileOutputStreamest un descendant de OutputStreampour écrire des données dans un fichier. Nous parlerons de la deuxième classe un peu plus tard.

Un autre point intéressant ici est la realvariable. Lorsque le dernier bloc de données est lu à partir d'un fichier, il peut facilement contenir moins de 64 Ko de données. En conséquence, nous devons sortir non pas l'intégralité du tampon, mais seulement une partie de celui-ci - les premiers realoctets. C'est exactement ce qui se passe dans la write()méthode.



3. Readerclasse

La Readerclasse est un analogue complet de la InputStreamclasse. La seule différence est que cela fonctionne avec des caractères ( char), pas avec des octets. Tout comme la InputStreamclasse, la Readerclasse n'est utilisée nulle part seule : c'est la classe parente de centaines de classes descendantes et elle définit des méthodes communes pour toutes.

Méthodes de la Readerclasse (et de toutes ses classes descendantes) :

Méthodes Description
int read()
En lit un charà partir du flux
int read(char[] buffer)
Lit un chartableau à partir du flux
long skip(long n)
Ignore n charsle flux (le lit et le supprime)
boolean ready()
Vérifie s'il reste encore quelque chose dans le flux
void close()
Ferme le flux

Les méthodes sont très similaires à celles de la InputStreamclasse, bien qu'il existe de légères différences.

int read()méthode

Cette méthode en lit un chardans le flux et le renvoie. Le chartype s'élargit à un int, mais les deux premiers octets du résultat sont toujours zéro.

int read(char[] buffer)méthode

C'est la deuxième variante de la read()méthode. Il vous permet de lire un tableau de caractères à partir d'un Readertout à la fois. Le tableau qui stockera les caractères doit être passé en argument. La méthode renvoie un nombre — le nombre de caractères réellement lus.

skip(long n)méthode

Cette méthode vous permet d'ignorer les n premiers caractères de l' Readerobjet. Cela fonctionne exactement de la même manière que la méthode analogue de la InputStreamclasse. Renvoie le nombre de caractères réellement ignorés.

boolean ready()méthode

Renvoie trues'il y a des octets non lus dans le flux.

void close()méthode

Le close()procédé ferme le flux de données et libère les ressources externes qui lui sont associées. Une fois qu'un flux est fermé, plus aucune donnée ne peut être lue à partir de celui-ci.

A titre de comparaison, écrivons un programme qui copie un fichier texte :

Code Note
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileReader reader = new FileReader(src);
FileWriter writer = new FileWriter(dest))
{
   char[] buffer = new char[65536]; // 128Kb
   while (reader.ready())
   {
      int real = reader.read(buffer);
      writer.write(buffer, 0, real);
   }
}



Readerpour lire à partir d'un fichier
Writerpour écrire dans un fichier

Tampon dans lequel nous allons lire les données
Tant qu'il y a des données dans le flux

Lire les données dans un tampon
Écrire les données du tampon dans le second flux