Guide de BufferedReader

Guide de BufferedReader

1. Vue d'ensemble

BufferedReader est une classe qui simplifie la lecture de texte à partir d'un flux d'entrée de caractères. Il met les caractères en mémoire tampon afin de permettre une lecture efficace des données textuelles.

Dans ce tutoriel, nous allons voir comment utiliser la classeBufferedReader.

2. Quand utiliserBufferedReader

En général,BufferedReader est pratique si nous voulons lire du texte à partir de n'importe quel type de source d'entrée, que ce soit des fichiers, des sockets ou autre chose.

En termes simples,it enables us to minimize the number of I/O operations by reading chunks of characters and storing them in an internal buffer. Alors que le tampon contient des données, le lecteur lira à partir de celui-ci au lieu de directement à partir du flux sous-jacent.

2.1. Mise en mémoire tampon d'un autre lecteur

Comme la plupart des classes d'E / S Java,BufferedReader implementsDecorator pattern, meaning it expects a Reader in its constructor. De cette manière, cela nous permet d'étendre de manière flexible une instance d'une implémentation deReader avec une fonctionnalité de mise en mémoire tampon:

BufferedReader reader =
  new BufferedReader(new FileReader("src/main/resources/input.txt"));

Mais, si la mise en mémoire tampon ne nous importe pas, nous pourrions simplement utiliser unFileReader  directement:

FileReader reader =
  new FileReader("src/main/resources/input.txt");

En plus de la mise en mémoire tampon,BufferedReader also provides some nice helper functions for reading files line-by-line. Ainsi, même s'il peut sembler plus simple d'utiliserFileReader  directement,BufferedReader peut être d'une grande aide.

2.2. Mise en mémoire tampon d'un flux

En général,we can configure BufferedReader to take any kind of input stream as an underlying source. Nous pouvons le faire en utilisantInputStreamReader et en l'enveloppant dans le constructeur:

BufferedReader reader =
  new BufferedReader(new InputStreamReader(System.in));

Dans l'exemple ci-dessus, nous lisons à partir deSystem.in qui correspond généralement à l'entrée du clavier. De même, nous pourrions transmettre un flux d’entrée pour la lecture d’un socket, d’un fichier ou de tout autre type d’entrée textuelle imaginable. La seule condition préalable est qu'il existe une implémentation deInputStream appropriée pour cela.

2.3. BufferedReader vs Scanner

Comme alternative, nous pourrions utiliser la classeScanner pour obtenir les mêmes fonctionnalités qu'avecBufferedReader.

Cependant, il existe des différences significatives entre ces deux classes qui peuvent les rendre plus ou moins pratiques pour nous, en fonction de notre cas d'utilisation:

  • BufferedReader est synchronisé (thread-safe) alors que Scanner n'est pas

  • Scanner can analyse les types primitifs et les chaînes à l'aide d'expressions régulières

  • BufferedReader permet de changer la taille du tampon tandis que le scanner a une taille de tampon fixe

  • BufferedReader a une taille de tampon par défaut plus grande

  • Scanner masqueIOException, tandis queBufferedReader nous oblige à le gérer

  • BufferedReader est généralement plus rapide queScanner car il ne lit que les données sans les analyser

Dans cet esprit,if we are parsing individual tokens in a file, then Scanner will feel a bit more natural than BufferedReader. But, just reading a line at a time is where BufferedReader shines.

Si nécessaire, nous avons égalementa guide on Scanner.

3. Lecture de texte avecBufferedReader

Passons en revue tout le processus de construction, d’utilisation et de destruction d’unBufferReader pour le lire à partir d’un fichier texte.

3.1. Initialisation d'unBufferedReader

Premièrement,let’s create a BufferedReader using its BufferedReader(Reader) constructor:

BufferedReader reader =
  new BufferedReader(new FileReader("src/main/resources/input.txt"));

Emballer lesFileReadercomme ceci est une bonne façon d'ajouter la mise en mémoire tampon comme aspect à d'autres lecteurs.

Par défaut, cela utilisera un tampon de 8 Ko. Cependant, si nous voulons mettre en mémoire tampon des blocs plus petits ou plus grands, nous pouvons utiliser le constructeurBufferedReader(Reader, int):

BufferedReader reader =
  new BufferedReader(new FileReader("src/main/resources/input.txt")), 16384);

Cela définira la taille de la mémoire tampon sur 16384 octets (16 Ko).

La taille optimale de la mémoire tampon dépend de facteurs tels que le type de flux d'entrée et le matériel sur lequel le code est exécuté. Pour cette raison, pour atteindre la taille idéale du tampon, nous devons le trouver nous-mêmes en expérimentant.

Il est préférable d’utiliser des puissances de 2 comme taille de tampon, car la plupart des périphériques matériels ont une puissance de 2 comme taille de bloc.

Enfin,there is one more handy way to create a BufferedReader using the Files helper class desjava.nioAPI:

BufferedReader reader =
  Files.newBufferedReader(Paths.get("src/main/resources/input.txt"))

Le créer like est une bonne façon de mettre en mémoire tampon si nous voulons lire un fichier car nous n’avons pas à créer manuellement unFileReader first puis à l’envelopper.

3.2. Lecture ligne par ligne

Ensuite, lisons le contenu du fichier en utilisant la méthodereadLine:

public String readAllLines(BufferedReader reader) throws IOException {
    StringBuilder content = new StringBuilder();
    String line;

    while ((line = reader.readLine()) != null) {
        content.append(line);
        content.append(System.lineSeparator());
    }

    return content.toString();
}

We can do the same thing as above using the lines method introduced in Java 8 un peu plus simplement:

public String readAllLinesWithStream(BufferedReader reader) {
    return reader.lines()
      .collect(Collectors.joining(System.lineSeparator()));
}

3.3. Fermer le flux

Après avoir utilisé lesBufferedReader, nous devons appeler sa méthodeclose() pour libérer toutes les ressources système qui lui sont associées. Cela se fait automatiquement si nous utilisons un bloctry-with-resources:

try (BufferedReader reader =
       new BufferedReader(new FileReader("src/main/resources/input.txt"))) {
    return readAllLines(reader);
}

4. Autres méthodes utiles

Concentrons-nous maintenant sur diverses méthodes utiles disponibles dansBufferedReader.

4.1. Lire un seul personnage

Nous pouvons utiliser la méthoderead() pour lire un seul caractère. Lisons tout le contenu caractère par caractère jusqu'à la fin du flux:

public String readAllCharsOneByOne(BufferedReader reader) throws IOException {
    StringBuilder content = new StringBuilder();

    int value;
    while ((value = reader.read()) != -1) {
        content.append((char) value);
    }

    return content.toString();
}

Cela lira les caractères (retournés sous forme de valeurs ASCII), les transtypera enchar et les ajoutera au résultat. Nous répétons cela jusqu'à la fin du flux, qui est indiquée par la valeur de réponse -1 de la méthoderead().

4.2. Lire plusieurs caractères

Si nous voulons lire plusieurs caractères à la fois, nous pouvons utiliser la méthoderead(char[] cbuf, int off, int len):

public String readMultipleChars(BufferedReader reader) throws IOException {
    int length;
    char[] chars = new char[length];
    int charsRead = reader.read(chars, 0, length);

    String result;
    if (charsRead != -1) {
        result = new String(chars, 0, charsRead);
    } else {
        result = "";
    }

    return result;
}

Dans l'exemple de code ci-dessus, nous allons lire jusqu'à 5 caractères dans un tableau de caractères et construire une chaîne à partir de celui-ci. Dans le cas où aucun caractère n'a été lu lors de notre tentative de lecture (c'est-à-dire nous avons atteint la fin du flux), nous retournerons simplement une chaîne vide.

4.3. Sauter les caractères

Nous pouvons également sauter un nombre donné de caractères en appelant la méthodeskip(long n):

@Test
public void givenBufferedReader_whensSkipChars_thenOk() throws IOException {
    StringBuilder result = new StringBuilder();

    try (BufferedReader reader =
           new BufferedReader(new StringReader("1__2__3__4__5"))) {
        int value;
        while ((value = reader.read()) != -1) {
            result.append((char) value);
            reader.skip(2L);
        }
    }

    assertEquals("12345", result);
}

Dans l'exemple ci-dessus, nous lisons une chaîne d'entrée contenant des nombres séparés par deux traits de soulignement. Afin de construire une chaîne contenant uniquement les nombres, nous sautons les traits de soulignement en appelant la méthodeskip .

4.4. mark etreset

Nous pouvons utiliser les méthodesmark(int readAheadLimit) etreset() pour marquer une position dans le flux et y revenir plus tard. À titre d’exemple quelque peu artificiel, utilisonsmark() etreset() pour ignorer tous les espaces au début d’un flux:

@Test
public void givenBufferedReader_whenSkipsWhitespacesAtBeginning_thenOk()
  throws IOException {
    String result;

    try (BufferedReader reader =
           new BufferedReader(new StringReader("    Lorem ipsum dolor sit amet."))) {
        do {
            reader.mark(1);
        } while(Character.isWhitespace(reader.read()))

        reader.reset();
        result = reader.readLine();
    }

    assertEquals("Lorem ipsum dolor sit amet.", result);
}

Dans l'exemple ci-dessus, nous utilisons la méthodemark() pour marquer la position que nous venons de lire. Si vous lui attribuez la valeur 1, seul le code se souviendra de la marque d’un caractère. It’s handy here because, once we see our first non-whitespace character, we can go back and re-read that character without needing to reprocess the whole stream. Without having a mark, we’d lose the L in our final string.

Notez que commemark() peut lancer unUnsupportedOperationException, il est assez courant d'associermarkSupported() avec du code qui invoquemark(). , bien que nous n'en ayons pas réellement besoin ici. That’s because markSupported() always returns true for BufferedReader.

Bien sûr, nous pourrions être en mesure de faire ce qui précède de manière un peu plus élégante par d'autres moyens, et en effet, les méthodesmark andresetne sont pas très typiques. They certainly come in handy, though, when there is a need to look ahead.

5. Conclusion

Dans ce rapide didacticiel, nous avons appris à lire les flux d'entrée de caractères sur un exemple pratique utilisantBufferedReader.

Enfin, le code source des exemples est disponibleover on Github.