Comment lire / convertir un InputStream en une chaîne en Java?


4065

Si vous avez un java.io.InputStreamobjet, comment devez-vous le traiter et en produire un String?


Supposons que j'aie un InputStreamqui contient des données texte et que je souhaite le convertir enString un fichier en un fichier, par exemple, je peux l'écrire dans un fichier journal.

Quelle est la façon la plus simple de prendre le InputStreamet de le convertir en un String?

public String convertStreamToString(InputStream is) {
    // ???
}

36
Les réponses à cette question ne fonctionnent que si vous souhaitez lire entièrement le contenu du flux (jusqu'à ce qu'il soit fermé). Étant donné que cela n'est pas toujours prévu (les requêtes http avec une connexion persistante ne seront pas fermées), ces méthodes appellent block (sans vous donner le contenu).
f1sh

21
Vous devez connaître et spécifier l'encodage de caractères pour le flux, ou vous aurez des bogues d'encodage de caractères, car vous utiliserez un encodage choisi au hasard en fonction de la machine / du système d'exploitation / de la plate-forme ou de la version de celui-ci sur laquelle votre code est exécuté. Autrement dit, n'utilisez pas de méthodes qui dépendent du codage par défaut de la plate-forme.
Christoffer Hammarström

11
Juste pour m'amuser avec mon propre commentaire d'il y a 9 ans, ces jours-ci, j'utilise "String s = new File (" SomeFile.txt "). Text" de Groovy pour lire un fichier entier en même temps et cela fonctionne très bien. Je suis heureux d'utiliser groovy pour mon code de non-production (script) et - bien honnêtement, vous obliger à gérer l'encodage et les fichiers extrêmement longs comme le fait java est de toute façon une très bonne idée pour le code de production, donc cela fonctionne pour son but, Groovy fonctionne pour des scripts rapides sur lesquels Java n'est pas génial - Utilisez simplement le bon outil pour le travail et tout fonctionne.
Bill K

Simplement simplifier: ByteArrayOutputStream outputBytes = new ByteArrayOutputStream(); for(byte[] b = new byte[512]; 0 < inputStream.read(b); outputBytes.write(b)); return new String(outputBytes.toByteArray(), StandardCharsets.UTF_8);
Felypp Oliveira

@BillK avec Java 11, vous pouvez utiliser String s = Files.readString​(Path.of("SomeFile.txt"));ce qui est aussi bon qu'un langage peut obtenir, qui ne supportera jamais de telles conversions de type magique comme celle que vous avez décrite.
Holger

Réponses:


2531

Une bonne façon de le faire est d'utiliser les communs Apache IOUtils pour copier le InputStreamdans un StringWriter... quelque chose comme

StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer, encoding);
String theString = writer.toString();

ou même

// NB: does not close inputStream, you'll have to use try-with-resources for that
String theString = IOUtils.toString(inputStream, encoding); 

Alternativement, vous pouvez utiliser ByteArrayOutputStreamsi vous ne souhaitez pas mélanger vos flux et écrivains


75
Pour les développeurs Android, il semble que Android ne soit pas fourni avec IOUtils d'Apache. Vous pourriez donc envisager de vous référer à d'autres réponses.
Chris.Zou

47
C'est une question incroyablement ancienne à ce stade (elle a été posée en 2008). Cela vaut la peine de lire des réponses plus modernes. Certains utilisent des appels natifs de la bibliothèque Java 8.
Shadoninja

36
Cette réponse est largement dépassée et il faut pouvoir la marquer comme telle (malheureusement ce n'est pas possible en atm).
codepleb

7
IOUtils.toString () est depuis longtemps obsolète. Cette réponse n'est certainement plus la voie recommandée.
Roshan

7
puis modifiez- le pour expliquer pourquoi il est déconseillé d'aider les futurs lecteurs.
Jean-François Fabre

2487

Résumez les autres réponses. J'ai trouvé 11 façons principales de le faire (voir ci-dessous). Et j'ai écrit quelques tests de performances (voir les résultats ci-dessous):

Façons de convertir un InputStream en une chaîne:

  1. Utilisation IOUtils.toString(Apache Utils)

    String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
  2. Utilisation de CharStreams(goyave)

    String result = CharStreams.toString(new InputStreamReader(
          inputStream, Charsets.UTF_8));
  3. Utilisation de Scanner(JDK)

    Scanner s = new Scanner(inputStream).useDelimiter("\\A");
    String result = s.hasNext() ? s.next() : "";
  4. Utilisation de l' API Stream (Java 8). Avertissement : cette solution convertit différents sauts de ligne (comme \r\n) en \n.

    String result = new BufferedReader(new InputStreamReader(inputStream))
      .lines().collect(Collectors.joining("\n"));
  5. Utilisation de l' API Stream parallèle (Java 8). Avertissement : cette solution convertit différents sauts de ligne (comme \r\n) en \n.

    String result = new BufferedReader(new InputStreamReader(inputStream)).lines()
       .parallel().collect(Collectors.joining("\n"));
  6. Utilisation de InputStreamReaderet StringBuilder(JDK)

    final int bufferSize = 1024;
    final char[] buffer = new char[bufferSize];
    final StringBuilder out = new StringBuilder();
    Reader in = new InputStreamReader(stream, StandardCharsets.UTF_8);
    int charsRead;
    while((charsRead = in.read(buffer, 0, buffer.length)) > 0) {
        out.append(buffer, 0, charsRead);
    }
    return out.toString();
  7. Utilisation de StringWriteret IOUtils.copy(Apache Commons)

    StringWriter writer = new StringWriter();
    IOUtils.copy(inputStream, writer, "UTF-8");
    return writer.toString();
  8. Utilisation de ByteArrayOutputStreamet inputStream.read(JDK)

    ByteArrayOutputStream result = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length;
    while ((length = inputStream.read(buffer)) != -1) {
        result.write(buffer, 0, length);
    }
    // StandardCharsets.UTF_8.name() > JDK 7
    return result.toString("UTF-8");
  9. Utilisation de BufferedReader(JDK). Avertissement: cette solution convertit différents sauts de ligne (comme \n\r) en line.separatorpropriété système (par exemple, dans Windows en "\ r \ n").

    String newLine = System.getProperty("line.separator");
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    StringBuilder result = new StringBuilder();
    boolean flag = false;
    for (String line; (line = reader.readLine()) != null; ) {
        result.append(flag? newLine: "").append(line);
        flag = true;
    }
    return result.toString();
  10. Utilisation de BufferedInputStreamet ByteArrayOutputStream(JDK)

    BufferedInputStream bis = new BufferedInputStream(inputStream);
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    int result = bis.read();
    while(result != -1) {
        buf.write((byte) result);
        result = bis.read();
    }
    // StandardCharsets.UTF_8.name() > JDK 7
    return buf.toString("UTF-8");
  11. Utilisation de inputStream.read()et StringBuilder(JDK). Avertissement : cette solution a des problèmes avec Unicode, par exemple avec du texte russe (ne fonctionne correctement qu'avec du texte non Unicode)

    int ch;
    StringBuilder sb = new StringBuilder();
    while((ch = inputStream.read()) != -1)
        sb.append((char)ch);
    reset();
    return sb.toString();

Attention :

  1. Les solutions 4, 5 et 9 convertissent différents sauts de ligne en un seul.

  2. La solution 11 ne peut pas fonctionner correctement avec du texte Unicode

Des tests de performance

Tests de performances pour les petits String(longueur = 175), URL dans github (mode = temps moyen, système = Linux, le score de 1 343 est le meilleur):

              Benchmark                         Mode  Cnt   Score   Error  Units
 8. ByteArrayOutputStream and read (JDK)        avgt   10   1,343 ± 0,028  us/op
 6. InputStreamReader and StringBuilder (JDK)   avgt   10   6,980 ± 0,404  us/op
10. BufferedInputStream, ByteArrayOutputStream  avgt   10   7,437 ± 0,735  us/op
11. InputStream.read() and StringBuilder (JDK)  avgt   10   8,977 ± 0,328  us/op
 7. StringWriter and IOUtils.copy (Apache)      avgt   10  10,613 ± 0,599  us/op
 1. IOUtils.toString (Apache Utils)             avgt   10  10,605 ± 0,527  us/op
 3. Scanner (JDK)                               avgt   10  12,083 ± 0,293  us/op
 2. CharStreams (guava)                         avgt   10  12,999 ± 0,514  us/op
 4. Stream Api (Java 8)                         avgt   10  15,811 ± 0,605  us/op
 9. BufferedReader (JDK)                        avgt   10  16,038 ± 0,711  us/op
 5. parallel Stream Api (Java 8)                avgt   10  21,544 ± 0,583  us/op

Tests de performances pour big String(longueur = 50100), url dans github (mode = temps moyen, système = Linux, le score 200 715 est le meilleur):

               Benchmark                        Mode  Cnt   Score        Error  Units
 8. ByteArrayOutputStream and read (JDK)        avgt   10   200,715 ±   18,103  us/op
 1. IOUtils.toString (Apache Utils)             avgt   10   300,019 ±    8,751  us/op
 6. InputStreamReader and StringBuilder (JDK)   avgt   10   347,616 ±  130,348  us/op
 7. StringWriter and IOUtils.copy (Apache)      avgt   10   352,791 ±  105,337  us/op
 2. CharStreams (guava)                         avgt   10   420,137 ±   59,877  us/op
 9. BufferedReader (JDK)                        avgt   10   632,028 ±   17,002  us/op
 5. parallel Stream Api (Java 8)                avgt   10   662,999 ±   46,199  us/op
 4. Stream Api (Java 8)                         avgt   10   701,269 ±   82,296  us/op
10. BufferedInputStream, ByteArrayOutputStream  avgt   10   740,837 ±    5,613  us/op
 3. Scanner (JDK)                               avgt   10   751,417 ±   62,026  us/op
11. InputStream.read() and StringBuilder (JDK)  avgt   10  2919,350 ± 1101,942  us/op

Graphiques (tests de performances en fonction de la longueur du flux d'entrée dans le système Windows 7)
entrez la description de l'image ici

Test de performance (durée moyenne) en fonction de la longueur du flux d'entrée dans le système Windows 7:

 length  182    546     1092    3276    9828    29484   58968

 test8  0.38    0.938   1.868   4.448   13.412  36.459  72.708
 test4  2.362   3.609   5.573   12.769  40.74   81.415  159.864
 test5  3.881   5.075   6.904   14.123  50.258  129.937 166.162
 test9  2.237   3.493   5.422   11.977  45.98   89.336  177.39
 test6  1.261   2.12    4.38    10.698  31.821  86.106  186.636
 test7  1.601   2.391   3.646   8.367   38.196  110.221 211.016
 test1  1.529   2.381   3.527   8.411   40.551  105.16  212.573
 test3  3.035   3.934   8.606   20.858  61.571  118.744 235.428
 test2  3.136   6.238   10.508  33.48   43.532  118.044 239.481
 test10 1.593   4.736   7.527   20.557  59.856  162.907 323.147
 test11 3.913   11.506  23.26   68.644  207.591 600.444 1211.545

17
Lorsque vous écrivez la «réponse récapitulative», vous devez noter que certaines solutions convertissent automatiquement différents sauts de ligne (comme \r\n) en \nlesquels peuvent être indésirables dans certains cas. Il serait également intéressant de voir la mémoire supplémentaire requise ou au moins la pression d'allocation (au moins vous pouvez exécuter JMH avec -prof gc). Pour le post vraiment cool, ce serait génial de voir les graphiques (en fonction de la longueur de chaîne dans la même taille d'entrée et en fonction de la taille d'entrée dans la même longueur de chaîne).
Tagir Valeev

16
A voté; le plus drôle est que les résultats sont plus que prévu: il faut utiliser le sucre syntaxique JDK et / ou Apache Commons standard.
Aleksei Matiushkin

25
Post incroyable. Juste une chose. Java 8 met en garde contre l'utilisation de flux parallèles sur des ressources qui vous forceront à verrouiller et à attendre (comme ce flux d'entrée), de sorte que l'option de flux parallèle est plutôt lourde et n'en vaut pas la peine?
mangusbrother

10
Le flux parallèle maintient-il réellement l'ordre des lignes?
Natix

6
À quoi sert reset()dans l'exemple 11?
Rob Stewart

2307

Voici un moyen d'utiliser uniquement la bibliothèque Java standard (notez que le flux n'est pas fermé, votre kilométrage peut varier).

static String convertStreamToString(java.io.InputStream is) {
    java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
    return s.hasNext() ? s.next() : "";
}

J'ai appris cette astuce dans l'article "Astuces du scanner stupide" . La raison pour laquelle cela fonctionne est que le scanner parcourt les jetons dans le flux, et dans ce cas, nous séparons les jetons en utilisant le "début de la limite d'entrée" (\ A), nous donnant ainsi un seul jeton pour tout le contenu du flux.

Remarque, si vous devez être précis sur l'encodage du flux d'entrée, vous pouvez fournir le deuxième argument au Scannerconstructeur qui indique le jeu de caractères à utiliser (par exemple "UTF-8").

La pointe du chapeau va également à Jacob , qui m'a un jour indiqué ledit article.


8
Merci, pour ma version de ceci, j'ai ajouté un bloc finalement qui ferme le flux d'entrée, donc l'utilisateur n'a pas à le faire depuis que vous avez fini de lire l'entrée. Simplifie considérablement le code de l'appelant.

4
@PavelRepin @Patrick dans mon cas, un inputStream vide a provoqué un NPE pendant la construction du scanner. J'ai dû ajouter if (is == null) return "";dès le début de la méthode; Je crois que cette réponse doit être mise à jour pour mieux gérer les entrées nulles.
CFL_Jeff

115
Pour Java 7, vous pouvez fermer dans un essai avec: try(java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; }
earcam

5
Malheureusement, cette solution semble perdre les exceptions levées dans mon implémentation de flux sous-jacente.
Taig

11
Pour info , hasNext bloque sur les flux d'entrée de la console (voir ici ). (Je suis juste tombé sur ce problème en ce moment.) Cette solution fonctionne bien sinon ... juste un avertissement.
Ryan

848

Apache Commons permet:

String myString = IOUtils.toString(myInputStream, "UTF-8");

Bien sûr, vous pouvez choisir d'autres encodages de caractères en plus de l'UTF-8.

Voir aussi: ( documentation )


1
En outre, il existe une méthode qui accepte uniquement un argument inputStream, si vous le trouvez avec l'encodage par défaut.
Guillaume Coté

13
@Guillaume Coté Je suppose que le message ici est que vous ne devriez jamais être "d'accord avec l'encodage par défaut", car vous ne pouvez pas être sûr de ce que c'est, selon la plate-forme sur laquelle le code java est exécuté.
Par Wiklander

7
@Per Wiklander Je ne suis pas d'accord avec vous. Le code qui va fonctionner sur un seul peut être sûr que le codage par défaut sera correct. Pour le code qui n'ouvre que le fichier local, il est raisonnable de leur demander d'être encodé dans l'encodage par défaut de la plateforme.
Guillaume Coté

39
Pour éviter à quiconque les tracas de la recherche sur Google - <dependency> <groupId> org.apache.commons </groupId> <artifactId> commons-io </artifactId> <version> 1.3.2 </version> </dependency>
Chris

7
De même, peu d'améliorations consisteraient à utiliser apache io (ou une autre) constante pour le codage de caractères au lieu d'utiliser un littéral de chaîne simple - par exemple: IOUtils.toString (myInputStream, Charsets.UTF_8);

300

En tenant compte du fichier, il faut d'abord obtenir une java.io.Readerinstance. Cela peut ensuite être lu et ajouté à un StringBuilder(nous n'avons pas besoin StringBuffersi nous n'y accédons pas dans plusieurs threads, et StringBuilderc'est plus rapide). L'astuce ici est que nous travaillons en blocs, et en tant que tels, nous n'avons pas besoin d'autres flux de mise en mémoire tampon. La taille de bloc est paramétrée pour l'optimisation des performances d'exécution.

public static String slurp(final InputStream is, final int bufferSize) {
    final char[] buffer = new char[bufferSize];
    final StringBuilder out = new StringBuilder();
    try (Reader in = new InputStreamReader(is, "UTF-8")) {
        for (;;) {
            int rsz = in.read(buffer, 0, buffer.length);
            if (rsz < 0)
                break;
            out.append(buffer, 0, rsz);
        }
    }
    catch (UnsupportedEncodingException ex) {
        /* ... */
    }
    catch (IOException ex) {
        /* ... */
    }
    return out.toString();
}

8
Cette solution utilise des caractères multi-octets. L'exemple utilise le codage UTF-8 qui permet l'expression de la plage unicode complète (y compris le chinois). Le remplacement de "UTF-8" par un autre codage permettrait d'utiliser ce codage.
Paul de Vrieze

27
@ User1 - J'aime utiliser les bibliothèques dans mon code pour pouvoir faire mon travail plus rapidement. C'est génial quand vos managers disent "Wow James! Comment as-tu pu faire ça si vite?!". Mais lorsque nous devons passer du temps à réinventer la roue simplement parce que nous avons des idées mal placées sur l'inclusion d'un utilitaire commun, réutilisable, éprouvé, nous abandonnons du temps que nous pourrions consacrer à la poursuite des objectifs de notre projet. Lorsque nous réinventons la roue, nous travaillons deux fois plus dur tout en atteignant la ligne d'arrivée beaucoup plus tard. Une fois arrivés à la ligne d'arrivée, il n'y a plus personne pour nous féliciter. Lorsque vous construisez une maison, ne construisez pas le marteau aussi
jmort253

10
Désolé, après avoir relu mon commentaire, il ressort un peu arrogant. Je pense simplement qu'il est important d'avoir une bonne raison d'éviter les bibliothèques et que la raison est valable, ce qui pourrait très bien être :)
jmort253

4
@ jmort253 Nous avons remarqué une régression des performances après avoir mis à jour plusieurs fois la bibliothèque de notre produit. Heureusement, nous construisons et vendons notre propre produit, nous n'avons donc pas vraiment les soi-disant délais. Malheureusement, nous construisons un produit qui est disponible sur de nombreuses machines virtuelles Java, bases de données et serveurs d'applications sur de nombreux systèmes d'exploitation.Nous devons donc penser aux utilisateurs utilisant des machines médiocres ... Et une optimisation du fonctionnement des chaînes peut améliorer la performance de 30 à 40%. Et un correctif: In our product, I even replaceddevrait être «nous avons même remplacé».
coolcfan

10
@ jmort253 Si vous utilisiez déjà apache commons, je dirais, allez-y. En même temps, l'utilisation des bibliothèques a un coût réel (comme le montre la prolifération des dépendances dans de nombreuses bibliothèques Java Apache). Si c'était la seule utilisation de la bibliothèque, il serait exagéré d'utiliser la bibliothèque. D'autre part, en déterminant votre propre taille de tampon, vous pouvez régler votre équilibre d'utilisation mémoire / processeur.
Paul de Vrieze

248

Utilisation:

InputStream in = /* Your InputStream */;
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String read;

while ((read=br.readLine()) != null) {
    //System.out.println(read);
    sb.append(read);
}

br.close();
return sb.toString();

11
Le fait est que vous vous séparez d'abord en lignes, puis que vous l'annulez. Il est plus facile et plus rapide de simplement lire des tampons arbitraires.
Paul de Vrieze

20
En outre, readLine ne fait pas de distinction entre \ n et \ r, vous ne pouvez donc pas reproduire à nouveau le flux exact.
María Arias de Reyna Domínguez

2
très inefficace, car readLinelu caractère par caractère pour rechercher EOL. De plus, s'il n'y a pas de saut de ligne dans le flux, cela n'a pas vraiment de sens.
njzk2

3
@Gops AB: Si vous essayez cela et que votre échantillon contient des sauts de ligne, vous verrez que la façon dont cette boucle est construite à l'aide de readline () et StringBuilder.append () ne conserve pas en fait les sauts de ligne.
Russ Bateman

4
Ce n'est pas la meilleure réponse car ce n'est pas strictement octet par octet. Le lecteur sélectionne les nouvelles lignes, vous devez donc faire attention à les maintenir.
Jeffrey Blattman

173

Si vous utilisez Google-Collections / Guava, vous pouvez procéder comme suit:

InputStream stream = ...
String content = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8));
Closeables.closeQuietly(stream);

Notez que le deuxième paramètre (c'est-à-dire Charsets.UTF_8) pour le InputStreamReadern'est pas nécessaire, mais c'est généralement une bonne idée de spécifier l'encodage si vous le connaissez (ce que vous devriez!)


2
@harschware: Étant donné que la question était: "Si vous avez un objet java.io.InputStream, comment devez-vous traiter cet objet et produire une chaîne?" J'ai supposé qu'un flux était déjà présent dans la situation.
Sakuraba

Vous n'avez pas très bien expliqué votre réponse et avez eu des variables étrangères; user359996 a dit la même chose que vous, mais beaucoup plus clairement.
Uronym

2
+1 pour la goyave, -1 pour ne pas spécifier l'encodage du flux d'entrée. par exemple. nouveau InputStreamReader (stream, "UTF-8")
andras

@Chris Noldus D'un autre côté, certaines personnes ont déjà de la goyave dans leur projet, comme moi, et pensent que cette solution est plus élégante que la version sdk uniquement.
CorayThan

@Vadzim cette réponse est la même que celle-ci - les deux utilisent CharStreams.toString
Tom

125

Il s'agit de la meilleure solution Java pure qui s'adapte parfaitement à Android et à toute autre machine virtuelle Java.

Cette solution fonctionne incroyablement bien ... elle est simple, rapide et fonctionne tout de même sur les petits et les grands flux !! (voir référence ci-dessus .. n ° 8 )

public String readFullyAsString(InputStream inputStream, String encoding)
        throws IOException {
    return readFully(inputStream).toString(encoding);
}

public byte[] readFullyAsBytes(InputStream inputStream)
        throws IOException {
    return readFully(inputStream).toByteArray();
}

private ByteArrayOutputStream readFully(InputStream inputStream)
        throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length = 0;
    while ((length = inputStream.read(buffer)) != -1) {
        baos.write(buffer, 0, length);
    }
    return baos;
}

4
Fonctionne bien sur Android en comparaison avec d'autres réponses qui ne fonctionnent qu'en Java d'entreprise.
vortexwolf

Crashed dans Android avec une erreur OutOfMemory sur la ligne ".write", à chaque fois, pour les chaînes courtes.
Adam

J'ai ajouté l'encodage. juste comme note latérale, la méthode readFully d'origine que j'ai dans mon code ne retourne pas String, elle retourne byte [] pour une fonctionnalité plus générale. L'implémentation de la nouvelle chaîne (...) avec l'encodage est la responsabilité du on qui utilise l'API!
TacB0sS

2
Note rapide: L'empreinte mémoire de ceci est maximisée par 2*n, où n est la taille du flux, selon le ByteArrayInputStreamsystème de croissance automatique.
njzk2

3
Inutile de doubler l'utilisation de la mémoire, ce qui est précieux sur les appareils mobiles. Vous feriez mieux d'utiliser InputStreamReader et de l'ajouter à StringReader, la conversion d'octet en caractère se fera à la volée, pas en masse à la fin.
Oliv

84

Pour être complet, voici la solution Java 9 :

public static String toString(InputStream input) throws IOException {
    return new String(input.readAllBytes(), StandardCharsets.UTF_8);
}

Le readAllBytesest actuellement dans la base de code principale de JDK 9, il devrait donc apparaître dans la version. Vous pouvez l'essayer dès maintenant en utilisant les versions d'instantanés JDK 9 .


La méthode n'alloue-t-elle pas beaucoup de mémoire pour la lecture? byte[] buf = new byte[DEFAULT_BUFFER_SIZE];MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;qui donne MAX_BUFFER_SIZE = 2147483639. Google dit que c'est environ 2,147 Go.
Rekin

Désolé, j'ai fait une erreur de calcul. C'est 2 Go. J'ai édité le commentaire. Donc, même si je lis comme un fichier de 4 Ko, j'utilise 2 Go de mémoire?
Rekin

2
@ChristianHujer, je ne le vois pas dans le dernier commit jdk8u . Les nouvelles méthodes AFAIK ne sont jamais introduites dans les mises à jour Java, uniquement dans les versions majeures.
Tagir Valeev

4
@ChristianHujer, la question portait sur InputStream, pas sur Path. Le InputStreampeut être créé à partir de nombreuses sources différentes, pas seulement de fichiers.
Tagir Valeev

5
Cela a été écrit il y a un an, donc pour la mise à jour, je confirme que cette méthode est bien dans la version publique JDK 9. En outre, si votre encodage est "ISO-Latin-1", cela sera extrêmement efficace puisque Java 9 Strings utilise maintenant une byte[]implémentation si tous les caractères sont dans les 256 premiers points de code. Cela signifie que la nouvelle chaîne (octet [], "ISO-Latin-1") sera une simple copie de tableau.
Klitos Kyriacou

66

Utilisation:

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;

public static String readInputStreamAsString(InputStream in)
    throws IOException {

    BufferedInputStream bis = new BufferedInputStream(in);
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    int result = bis.read();
    while(result != -1) {
      byte b = (byte)result;
      buf.write(b);
      result = bis.read();
    }
    return buf.toString();
}

@ DanielDeLeón Non, ce n'est pas le cas. C'est un BufferedInputStream. Les lectures sous-jacentes sont de 8192 octets à la fois.
Marquis de Lorne le

2
@EJP Je l'ai trouvé plus lent que l'utilisation de BufferedInputStream et la lecture dans un tampon de tableau d'octets au lieu d'un octet à la fois. Exemple: 200 ms contre 60 ms lors de la lecture d'un fichier de 4,56 Mio.
jk7

Bizarre que personne n'ait signalé l'autre problème majeur ici (oui, la lecture de contenu octet par octet est un gaspillage même avec la mise en mémoire tampon): cela dépend de ce qui se trouve être le "codage par défaut" - c'est rarement un bon moyen. Assurez-vous plutôt de passer le codage comme argument à buf.toString().
StaxMan

@ jk7 Le temps de lecture d'un fichier de 4,56 Mo est si minime que les différences ne peuvent pas avoir été significatives.
Marquis de Lorne

63

Voici la solution la plus élégante, pure Java (sans bibliothèque) que j'ai trouvée après quelques expérimentations:

public static String fromStream(InputStream in) throws IOException
{
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    StringBuilder out = new StringBuilder();
    String newLine = System.getProperty("line.separator");
    String line;
    while ((line = reader.readLine()) != null) {
        out.append(line);
        out.append(newLine);
    }
    return out.toString();
}

8
@TorbenKohlmeier, les lecteurs et les tampons n'ont pas besoin d'être fermés. Le fourni InputStreamdoit être fermé par l'appelant.
Drew Noakes

7
N'oubliez pas de mentionner qu'il existe un constructeur plus préférable dans InputStreamReader qui prend un CharSet.
jontejj

7
pourquoi les gens continuent-ils à utiliser readLine? si vous n'utilisez pas les lignes en soi, à quoi cela sert-il (sauf d'être très lent?)
njzk2

4
Ne lisez pas par lignes. Et si une ligne est si longue qu'elle ne rentre pas dans le tas?
voho

4
@voho, si une ligne est aussi longue, il n'y a aucun moyen d'allouer de toute façon la valeur de retour qui doit être de taille égale ou supérieure à cette ligne. Si vous traitez des fichiers aussi volumineux, vous devez les diffuser. Il existe cependant de nombreux cas d'utilisation pour le chargement de petits fichiers texte en mémoire.
Drew Noakes

55

J'ai fait un benchmark sur 14 réponses distinctes ici (désolé de ne pas fournir de crédits mais il y a trop de doublons).

Le résultat est très surprenant. Il s'avère que Apache IOUtils est la solution la plus lente et ByteArrayOutputStreamla plus rapide:

Voici donc d'abord la meilleure méthode:

public String inputStreamToString(InputStream inputStream) throws IOException {
    try(ByteArrayOutputStream result = new ByteArrayOutputStream()) {
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) != -1) {
            result.write(buffer, 0, length);
        }

        return result.toString(UTF_8);
    }
}

Résultats de référence, de 20 Mo d'octets aléatoires en 20 cycles

Temps en millisecondes

  • ByteArrayOutputStreamTest: 194
  • NioStream: 198
  • Java9ISTransferTo: 201
  • Java9ISReadAllBytes: 205
  • BufferedInputStreamVsByteArrayOutputStream: 314
  • ApacheStringWriter2: 574
  • GuavaCharStreams: 589
  • ScannerReaderNoNextTest: 614
  • ScannerReader: 633
  • ApacheStringWriter: 1544
  • StreamApi: erreur
  • ParallelStreamApi: erreur
  • BufferReaderTest: erreur
  • InputStreamAndStringBuilder: erreur

Code source de référence

import com.google.common.io.CharStreams;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * Created by Ilya Gazman on 2/13/18.
 */
public class InputStreamToString {


    private static final String UTF_8 = "UTF-8";

    public static void main(String... args) {
        log("App started");
        byte[] bytes = new byte[1024 * 1024];
        new Random().nextBytes(bytes);
        log("Stream is ready\n");

        try {
            test(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void test(byte[] bytes) throws IOException {
        List<Stringify> tests = Arrays.asList(
                new ApacheStringWriter(),
                new ApacheStringWriter2(),
                new NioStream(),
                new ScannerReader(),
                new ScannerReaderNoNextTest(),
                new GuavaCharStreams(),
                new StreamApi(),
                new ParallelStreamApi(),
                new ByteArrayOutputStreamTest(),
                new BufferReaderTest(),
                new BufferedInputStreamVsByteArrayOutputStream(),
                new InputStreamAndStringBuilder(),
                new Java9ISTransferTo(),
                new Java9ISReadAllBytes()
        );

        String solution = new String(bytes, "UTF-8");

        for (Stringify test : tests) {
            try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
                String s = test.inputStreamToString(inputStream);
                if (!s.equals(solution)) {
                    log(test.name() + ": Error");
                    continue;
                }
            }
            long startTime = System.currentTimeMillis();
            for (int i = 0; i < 20; i++) {
                try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
                    test.inputStreamToString(inputStream);
                }
            }
            log(test.name() + ": " + (System.currentTimeMillis() - startTime));
        }
    }

    private static void log(String message) {
        System.out.println(message);
    }

    interface Stringify {
        String inputStreamToString(InputStream inputStream) throws IOException;

        default String name() {
            return this.getClass().getSimpleName();
        }
    }

    static class ApacheStringWriter implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            StringWriter writer = new StringWriter();
            IOUtils.copy(inputStream, writer, UTF_8);
            return writer.toString();
        }
    }

    static class ApacheStringWriter2 implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return IOUtils.toString(inputStream, UTF_8);
        }
    }

    static class NioStream implements Stringify {

        @Override
        public String inputStreamToString(InputStream in) throws IOException {
            ReadableByteChannel channel = Channels.newChannel(in);
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 16);
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            WritableByteChannel outChannel = Channels.newChannel(bout);
            while (channel.read(byteBuffer) > 0 || byteBuffer.position() > 0) {
                byteBuffer.flip();  //make buffer ready for write
                outChannel.write(byteBuffer);
                byteBuffer.compact(); //make buffer ready for reading
            }
            channel.close();
            outChannel.close();
            return bout.toString(UTF_8);
        }
    }

    static class ScannerReader implements Stringify {

        @Override
        public String inputStreamToString(InputStream is) throws IOException {
            java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
            return s.hasNext() ? s.next() : "";
        }
    }

    static class ScannerReaderNoNextTest implements Stringify {

        @Override
        public String inputStreamToString(InputStream is) throws IOException {
            java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
            return s.next();
        }
    }

    static class GuavaCharStreams implements Stringify {

        @Override
        public String inputStreamToString(InputStream is) throws IOException {
            return CharStreams.toString(new InputStreamReader(
                    is, UTF_8));
        }
    }

    static class StreamApi implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return new BufferedReader(new InputStreamReader(inputStream))
                    .lines().collect(Collectors.joining("\n"));
        }
    }

    static class ParallelStreamApi implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return new BufferedReader(new InputStreamReader(inputStream)).lines()
                    .parallel().collect(Collectors.joining("\n"));
        }
    }

    static class ByteArrayOutputStreamTest implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            try(ByteArrayOutputStream result = new ByteArrayOutputStream()) {
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) != -1) {
                    result.write(buffer, 0, length);
                }

                return result.toString(UTF_8);
            }
        }
    }

    static class BufferReaderTest implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            String newLine = System.getProperty("line.separator");
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder result = new StringBuilder(UTF_8);
            String line;
            boolean flag = false;
            while ((line = reader.readLine()) != null) {
                result.append(flag ? newLine : "").append(line);
                flag = true;
            }
            return result.toString();
        }
    }

    static class BufferedInputStreamVsByteArrayOutputStream implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            BufferedInputStream bis = new BufferedInputStream(inputStream);
            ByteArrayOutputStream buf = new ByteArrayOutputStream();
            int result = bis.read();
            while (result != -1) {
                buf.write((byte) result);
                result = bis.read();
            }

            return buf.toString(UTF_8);
        }
    }

    static class InputStreamAndStringBuilder implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            int ch;
            StringBuilder sb = new StringBuilder(UTF_8);
            while ((ch = inputStream.read()) != -1)
                sb.append((char) ch);
            return sb.toString();
        }
    }

    static class Java9ISTransferTo implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            inputStream.transferTo(bos);
            return bos.toString(UTF_8);
        }
    }

    static class Java9ISReadAllBytes implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return new String(inputStream.readAllBytes(), UTF_8);
        }
    }

}

Faire des benchmarks en Java n'est pas facile (surtout à cause de JIT). Après avoir lu le code source de Benchmark, je suis convaincu que ces valeurs ci-dessus ne sont pas précises et que tout le monde devrait être prudent en les croyant.
Dalibor

@Dalibor, vous devriez probablement fournir plus de raisons pour votre réclamation plutôt qu'un simple lien.
Ilya Gazman

Je pense que c'est un fait bien connu qu'il n'est pas facile de faire sa propre référence. Pour ceux qui ne le savent pas, il y a un lien;)
Dalibor

@Dalibor Je ne suis peut-être pas le meilleur, mais j'ai une bonne compréhension des benchmarks Java, donc à moins que vous ne puissiez signaler un problème spécifique, vous êtes simplement trompeur et je ne poursuivrai pas la conversation avec vous dans ces conditions.
Ilya Gazman

Surtout, je suis d'accord avec Dalibor. Vous dites que vous avez une "bonne compréhension des benchmarks Java", mais vous semblez avoir implémenté l'approche la plus naïve tout en ignorant apparemment les problèmes bien connus de cette approche. Pour commencer, lisez chaque article sur cette question: stackoverflow.com/questions/504103/…
DavidS

41

J'utiliserais quelques astuces Java 8.

public static String streamToString(final InputStream inputStream) throws Exception {
    // buffering optional
    try
    (
        final BufferedReader br
           = new BufferedReader(new InputStreamReader(inputStream))
    ) {
        // parallel optional
        return br.lines().parallel().collect(Collectors.joining("\n"));
    } catch (final IOException e) {
        throw new RuntimeException(e);
        // whatever.
    }
}

Essentiellement les mêmes que certaines autres réponses, sauf plus succinctes.


5
Serait-ce return nulljamais appelé? Soit le br.lines...retour, soit une exception est levée.
Holloway

3
@Khaled A Khunaifer: oui, presque sûr ... peut-être devriez-vous jeter un œil ici: docs.oracle.com/javase/tutorial/essential/exceptions/… . Ce que vous avez modifié à tort est une instruction "essayez avec des ressources".
2015

11
Pourquoi appelez-vous parallel()sur le flux?
robinst

4
Cela ne résulterait pas en une copie honnête des données si le flux source utilisait des fins de ligne Windows car tout \r\nfinirait par être converti en \n...
Lucas

2
Vous pouvez utiliser System.lineSeparator()pour utiliser la fin de ligne dépendante de la plate-forme appropriée.
Steve K

34

J'ai fait des tests de chronométrage car le temps compte, toujours.

J'ai essayé d'obtenir la réponse dans une chaîne de 3 manières différentes. (montré ci-dessous)
J'ai laissé de côté les blocs try / catch pour la lisibilité.

Pour donner le contexte, voici le code précédent pour les 3 approches:

   String response;
   String url = "www.blah.com/path?key=value";
   GetMethod method = new GetMethod(url);
   int status = client.executeMethod(method);

1)

 response = method.getResponseBodyAsString();

2)

InputStream resp = method.getResponseBodyAsStream();
InputStreamReader is=new InputStreamReader(resp);
BufferedReader br=new BufferedReader(is);
String read = null;
StringBuffer sb = new StringBuffer();
while((read = br.readLine()) != null) {
    sb.append(read);
}
response = sb.toString();

3)

InputStream iStream  = method.getResponseBodyAsStream();
StringWriter writer = new StringWriter();
IOUtils.copy(iStream, writer, "UTF-8");
response = writer.toString();

Ainsi, après avoir exécuté 500 tests sur chaque approche avec les mêmes données de demande / réponse, voici les chiffres. Encore une fois, ce sont mes conclusions et vos conclusions ne sont peut-être pas exactement les mêmes, mais j'ai écrit ceci pour donner une indication aux autres des différences d'efficacité de ces approches.

Classements:
Approche n ° 1
Approche n ° 3 - 2,6% plus lent que n ° 1
Approche n ° 2 - 4,3% plus lent que n ° 1

Chacune de ces approches est une solution appropriée pour récupérer une réponse et en créer une chaîne.


2
2) contient une erreur, il ajoute toujours "null" à la fin de la chaîne car vous effectuez toujours une étape de plus que nécessaire. Je pense que les performances seront les mêmes de toute façon. Cela devrait fonctionner: String read = null; StringBuffer sb = new StringBuffer (); while ((read = br.readLine ())! = null) {sb.append (read); }
LukeSolar

Il convient de noter que GetMethod fait partie de org.apache.commons.httpclient, et non Java standard
jk7

L'approche # 2 consommera le '\ n' si le fichier a plusieurs lignes, cela ne devrait pas être la réponse
Ninja

33

Solution Java pure utilisant Stream s, fonctionne depuis Java 8.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

// ...
public static String inputStreamToString(InputStream is) throws IOException {
    try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
        return br.lines().collect(Collectors.joining(System.lineSeparator()));
    }
}

Comme mentionné par Christoffer Hammarström ci - dessous autre réponse , il est plus sûr de spécifier explicitement le jeu de caractères . C'est-à-dire que le constructeur InputStreamReader peut être modifié comme suit:

new InputStreamReader(is, Charset.forName("UTF-8"))

11
Au lieu de le faire Charset.forName("UTF-8"), utilisez StandardCharsets.UTF_8(from java.nio.charset).
robinst

26

Voici la réponse de Sampath plus ou moins, nettoyée un peu et représentée comme une fonction:

String streamToString(InputStream in) throws IOException {
  StringBuilder out = new StringBuilder();
  BufferedReader br = new BufferedReader(new InputStreamReader(in));
  for(String line = br.readLine(); line != null; line = br.readLine()) 
    out.append(line);
  br.close();
  return out.toString();
}


21

Si vous ne pouvez pas utiliser Commons IO (FileUtils / IOUtils / CopyUtils), voici un exemple utilisant un BufferedReader pour lire le fichier ligne par ligne:

public class StringFromFile {
    public static void main(String[] args) /*throws UnsupportedEncodingException*/ {
        InputStream is = StringFromFile.class.getResourceAsStream("file.txt");
        BufferedReader br = new BufferedReader(new InputStreamReader(is/*, "UTF-8"*/));
        final int CHARS_PER_PAGE = 5000; //counting spaces
        StringBuilder builder = new StringBuilder(CHARS_PER_PAGE);
        try {
            for(String line=br.readLine(); line!=null; line=br.readLine()) {
                builder.append(line);
                builder.append('\n');
            }
        } 
        catch (IOException ignore) { }

        String text = builder.toString();
        System.out.println(text);
    }
}

Ou si vous voulez une vitesse brute, je proposerais une variation de ce que Paul de Vrieze a suggéré (ce qui évite d'utiliser un StringWriter (qui utilise un StringBuffer en interne):

public class StringFromFileFast {
    public static void main(String[] args) /*throws UnsupportedEncodingException*/ {
        InputStream is = StringFromFileFast.class.getResourceAsStream("file.txt");
        InputStreamReader input = new InputStreamReader(is/*, "UTF-8"*/);
        final int CHARS_PER_PAGE = 5000; //counting spaces
        final char[] buffer = new char[CHARS_PER_PAGE];
        StringBuilder output = new StringBuilder(CHARS_PER_PAGE);
        try {
            for(int read = input.read(buffer, 0, buffer.length);
                    read != -1;
                    read = input.read(buffer, 0, buffer.length)) {
                output.append(buffer, 0, read);
            }
        } catch (IOException ignore) { }

        String text = output.toString();
        System.out.println(text);
    }
}

Afin de faire fonctionner votre code, j'ai dû utiliser this.getClass (). GetClassLoader (). GetResourceAsStream () (en utilisant Eclipse avec un projet
maven

19

Celui-ci est sympa car:

  • Il gère en toute sécurité le Charset.
  • Vous contrôlez la taille du tampon de lecture.
  • Vous pouvez provisionner la longueur du générateur et il ne doit pas nécessairement être une valeur exacte.
  • Est exempt de dépendances de bibliothèque.
  • Est pour Java 7 ou supérieur.

Comment faire?

public static String convertStreamToString(InputStream is) throws IOException {
   StringBuilder sb = new StringBuilder(2048); // Define a size if you have an idea of it.
   char[] read = new char[128]; // Your buffer size.
   try (InputStreamReader ir = new InputStreamReader(is, StandardCharsets.UTF_8)) {
     for (int i; -1 != (i = ir.read(read)); sb.append(read, 0, i));
   }
   return sb.toString();
}

Pour JDK 9

public static String inputStreamString(InputStream inputStream) throws IOException {
    try (inputStream) {
        return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
    }
}

1
Le catch (Throwable)ne devrait pas vraiment être vide s'il s'agit d'un code de production.
Christian Hujer

1
que mettre dans cette déclaration catch-throwable?
alex

Bien que l'utilisation d'UTF-8 soit généralement raisonnable, vous ne devez pas supposer que les caractères sont codés de cette façon.
Martin

18

Ceci est une réponse adaptée du org.apache.commons.io.IOUtils code source , pour ceux qui veulent avoir l'implémentation d'apache mais ne veulent pas la bibliothèque entière.

private static final int BUFFER_SIZE = 4 * 1024;

public static String inputStreamToString(InputStream inputStream, String charsetName)
        throws IOException {
    StringBuilder builder = new StringBuilder();
    InputStreamReader reader = new InputStreamReader(inputStream, charsetName);
    char[] buffer = new char[BUFFER_SIZE];
    int length;
    while ((length = reader.read(buffer)) != -1) {
        builder.append(buffer, 0, length);
    }
    return builder.toString();
}

18

Assurez-vous de fermer les flux à la fin si vous utilisez des lecteurs de flux

private String readStream(InputStream iStream) throws IOException {
    //build a Stream Reader, it can read char by char
    InputStreamReader iStreamReader = new InputStreamReader(iStream);
    //build a buffered Reader, so that i can read whole line at once
    BufferedReader bReader = new BufferedReader(iStreamReader);
    String line = null;
    StringBuilder builder = new StringBuilder();
    while((line = bReader.readLine()) != null) {  //Read till end
        builder.append(line);
        builder.append("\n"); // append new line to preserve lines
    }
    bReader.close();         //close all opened stuff
    iStreamReader.close();
    //iStream.close(); //EDIT: Let the creator of the stream close it!
                       // some readers may auto close the inner stream
    return builder.toString();
}

EDIT: Sur JDK 7+, vous pouvez utiliser la construction try-with-resources.

/**
 * Reads the stream into a string
 * @param iStream the input stream
 * @return the string read from the stream
 * @throws IOException when an IO error occurs
 */
private String readStream(InputStream iStream) throws IOException {

    //Buffered reader allows us to read line by line
    try (BufferedReader bReader =
                 new BufferedReader(new InputStreamReader(iStream))){
        StringBuilder builder = new StringBuilder();
        String line;
        while((line = bReader.readLine()) != null) {  //Read till end
            builder.append(line);
            builder.append("\n"); // append new line to preserve lines
        }
        return builder.toString();
    }
}

2
Vous avez raison sur la fermeture des flux, cependant, la responsabilité de la fermeture des flux incombe généralement au constructeur du flux (terminez ce que vous commencez). Donc, iStreamdevrait plutôt être fermé par l'appelant parce que l'appelant a créé iStream. En outre, la fermeture des flux doit être effectuée dans un finallybloc, ou mieux dans une instruction Java 7 try-with-resources. Dans votre code, lorsque readLine()jette IOException, ou builder.append()jetteOutOfMemoryError , les flux resteraient ouverts.
Christian Hujer

16

Un autre, pour tous les utilisateurs de Spring:

import java.nio.charset.StandardCharsets;
import org.springframework.util.FileCopyUtils;

public String convertStreamToString(InputStream is) throws IOException { 
    return new String(FileCopyUtils.copyToByteArray(is), StandardCharsets.UTF_8);
}

Les méthodes utilitaires dans org.springframework.util.StreamUtilssont similaires à celles de FileCopyUtils, mais elles laissent le flux ouvert une fois terminé.


16

Utilisez le java.io.InputStream.transferTo (OutputStream) pris en charge dans Java 9 et le ByteArrayOutputStream.toString (String) qui prend le nom du jeu de caractères:

public static String gobble(InputStream in, String charsetName) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    in.transferTo(bos);
    return bos.toString(charsetName);
}

Qu'avez-vous passé pour le nom de jeu de caractères dans votre cas?
virsha

1
@virsha Vous devez déterminer cela à partir de la source qui a fourni InputStream. N'oubliez pas qu'il n'est pas logique d'avoir une chaîne sans savoir quel encodage elle utilise.
jmehrens

15

Voici la méthode complète pour convertir InputStreamen Stringsans utiliser de bibliothèque tierce. Utiliser StringBuilderpour un environnement à thread unique sinon utiliser StringBuffer.

public static String getString( InputStream is) throws IOException {
    int ch;
    StringBuilder sb = new StringBuilder();
    while((ch = is.read()) != -1)
        sb.append((char)ch);
    return sb.toString();
}

3
Dans cette méthode, aucun codage n'est appliqué. Supposons donc que les données reçues de InputStream soient encodées en UTF-8, la sortie sera incorrecte. Pour résoudre ce problème, vous pouvez utiliser in = new InputStreamReader(inputStream)et (char)in.read().
Frédéric Leitenberger

2
et mémoire-inefficace aussi bien; Je crois que j'ai essayé d'utiliser cela auparavant sur une grande entrée et StringBuilder a manqué de mémoire
gengkev

1
Il existe une autre réponse similaire qui utilise un tampon char [] et est plus efficace et prend soin de charset.
Guillaume Perrot

14

Voici comment le faire en utilisant uniquement le JDK en utilisant des tampons de tableau d'octets. C'est en fait ainsi que fonctionnent les IOUtils.copy()méthodes communes-io . Vous pouvez remplacer byte[]par char[]si vous copiez à partir d'un Readerau lieu d'un InputStream.

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

...

InputStream is = ....
ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
byte[] buffer = new byte[8192];
int count = 0;
try {
  while ((count = is.read(buffer)) != -1) {
    baos.write(buffer, 0, count);
  }
}
finally {
  try {
    is.close();
  }
  catch (Exception ignore) {
  }
}

String charset = "UTF-8";
String inputStreamAsString = baos.toString(charset);

1
Veuillez décrire ce que vous essayez d'accomplir.
Ragunath Jawahar

14

Les utilisateurs de Kotlin font simplement:

println(InputStreamReader(is).readText())

tandis que

readText()

est la méthode d'extension intégrée de la bibliothèque standard de Kotlin.


Ce n'est en fait pas tout à fait correct car cela ne ferme pas le flux. Je recommanderais is.bufferedReader().use { it.readText() }.
Max

9

Le moyen le plus simple dans JDK consiste à utiliser les extraits de code suivants.

String convertToString(InputStream in){
    String resource = new Scanner(in).useDelimiter("\\Z").next();
    return resource;
}

7

Voici ma solution basée sur Java 8 , qui utilise la nouvelle API Stream pour collecter toutes les lignes d'un InputStream:

public static String toString(InputStream inputStream) {
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(inputStream));
    return reader.lines().collect(Collectors.joining(
        System.getProperty("line.separator")));
}

1
Il semble que vous n'ayez pas lu toutes les réponses publiées auparavant. La version Stream API était déjà là au moins deux fois .
Tagir Valeev

J'ai examiné toutes les solutions, mais je n'ai trouvé aucune solution appropriée. Je trouve que deux lignes avec une brève description sont précises présentées. Le bloc try-catch de l'autre solution n'est par exemple jamais utilisé. Mais tu as raison. Avec autant de réponses, je suis passé en mode lecture rapide ... :-)
Christian Rädel

1
Vous ne lisez pas le fichier d'origine, vous convertissez toutes les fins de ligne du fichier en toutes les fins de ligne du système d'exploitation, modifiant éventuellement le contenu du fichier.
Christian Hujer

7

En termes de reduce, et concatil peut être exprimé en Java 8 comme:

String fromFile = new BufferedReader(new   
InputStreamReader(inputStream)).lines().reduce(String::concat).get();

1
Ce sera incroyablement lent.
Tagir Valeev

Intéressant, pourquoi? Pourriez-vous élaborer?
libnull-dev

1
ne savez-vous pas pourquoi concaténer des chaînes en boucle au lieu d'utiliser StringBuilder est une mauvaise idée?
Tagir Valeev

Tu as raison. StringBuilderpourrait être plus efficace. Je vais vérifier, mais mon but était de montrer une approche plus fonctionnelle avec immuable String.
libnull-dev

Les approches fonctionnelles sont cool mais généralement très inefficaces.
Lluis Martinez

4

Réponse JDK 7/8 qui ferme le flux et lève toujours une exception IOException:

StringBuilder build = new StringBuilder();
byte[] buf = new byte[1024];
int length;
try (InputStream is = getInputStream()) {
  while ((length = is.read(buf)) != -1) {
    build.append(new String(buf, 0, length));
  }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.