Obtenez un OutputStream dans une chaîne

580

Quelle est la meilleure façon de diriger la sortie d'un java.io.OutputStream vers une chaîne en Java?

Disons que j'ai la méthode:

  writeToStream(Object o, OutputStream out)

Qui écrit certaines données de l'objet dans le flux donné. Cependant, je veux obtenir cette sortie dans une chaîne aussi facilement que possible.

J'envisage d'écrire un cours comme celui-ci (non testé):

class StringOutputStream extends OutputStream {

  StringBuilder mBuf;

  public void write(int byte) throws IOException {
    mBuf.append((char) byte);
  }

  public String getString() {
    return mBuf.toString();
  }
}

Mais y a-t-il une meilleure façon? Je veux seulement faire un test!

Adrian Mouat
la source
6
Avez-vous seulement des octets ASCII? N'avez-vous pas besoin de Codepage?
Horcrux7
Dans ce cas, oui. Cependant, bon point - je n'y avais pas pensé.
Adrian Mouat

Réponses:

607

J'utiliserais un ByteArrayOutputStream. Et à la fin, vous pouvez appeler:

new String( baos.toByteArray(), codepage );

ou mieux:

baos.toString( codepage );

Pour le Stringconstructeur, le codepagepeut être un Stringou une instance de java.nio.charset.Charset . Une valeur possible est java.nio.charset.StandardCharsets.UTF_8 .

La méthode toString()accepte uniquement un Stringcomme codepageparamètre (stand Java 8).

Horcrux7
la source
8
ByteArrayOutputStream n'a pas de méthode toArray (); il doit cependant toByteArray (). Pouvez-vous corriger la réponse? Aussi, pourquoi ne pas utiliser baos.toString (String charsetName) qui serait légèrement plus simple.
Jonik
35
Un bytearray est juste des données binaires. Comme le texte (unicode) peut être codé en binaire de différentes manières, le ByteArrayOutputStream doit savoir quel codage a été utilisé pour coder les octets, afin qu'il puisse utiliser le même codage pour décoder les octets en une chaîne à nouveau. Il n'est pas sage d'utiliser simplement toString sans argument car vous ignorez simplement le problème au lieu de le résoudre; Java utilisera le codage de la plateforme qui pourrait être correct ... ou non. C'est aléatoire au fond. Vous devez savoir quel encodage a été utilisé pour écrire le texte en octets et transmettre cet encodage à toString.
Stijn de Witt,
10
Juste une clarification sur la page de code référencée ici: en Java, vous pouvez utiliser Charset.defaultCharset () ou Charset.forName ("charset spécifique"); Ce qui a fonctionné pour moi, c'est: new String (baos.toByteArray (), Charset.defaultCharset ());
Wallace Brown
7
L'utilisation de @WallaceBrown defaultCharsetn'est pas mieux que d'ignorer complètement le jeu de caractères - vous devez savoir ce que c'est avant d'utilisertoString
artbristol
4
StandardCharsets.UTF_8est un Charset, pas un String. De plus, le paramètre est appelé charsetName, non codepage.
OrangeDog
46

J'aime la bibliothèque IO Apache Commons. Jetez un œil à sa version de ByteArrayOutputStream , qui a également une toString(String enc)méthode toByteArray(). L'utilisation de composants existants et fiables comme le projet Commons permet à votre code d'être plus petit et plus facile à étendre et à réutiliser.

Joe Liversedge
la source
10
Épargnez-vous un an de votre vie et lisez toutes les API courantes afin que lorsque vous rencontrez un problème, vous puissiez libérer une solution entièrement testée et détenue par la communauté.
Bob Herrmann
15
Hmm, je suis un utilisateur passionné d'Apache Commons, mais dans ce cas, je ne vois pas pourquoi vous devriez utiliser ByteArrayOutputStream de Commons IO au lieu du propre java.io.ByteArrayOutputStream de JDK. Ce dernier fournit également les méthodes toString (String charsetName) et toByteArray (). Envie d'élaborer?
Jonik
1
Oui, étant donné que le contexte d'origine était une meilleure façon de diffuser et d'extraire du contenu, j'ai inclus l'exemple Commons IO car il comprenait une méthode `` write (InputStream) '' pour un mécanisme alors indéfini / discutable pour remplir le OutputStream. J'irais aussi avec le JDK.
Joe Liversedge
23

Cela a bien fonctionné

OutputStream output = new OutputStream() {
    private StringBuilder string = new StringBuilder();

    @Override
    public void write(int b) throws IOException {
        this.string.append((char) b );
    }

    //Netbeans IDE automatically overrides this toString()
    public String toString() {
        return this.string.toString();
    }
};

appel de méthode = >> marshaller.marshal( (Object) toWrite , (OutputStream) output);

puis pour imprimer la chaîne ou la récupérer simplement référencer le flux "de sortie" lui-même. Par exemple, pour imprimer la chaîne sur console = >> System.out.println(output);

FYI: mon appel de méthode marshaller.marshal(Object,Outputstream) est pour travailler avec XML. Cela n'a aucun rapport avec ce sujet.

C'est très gaspillant pour un usage de production, il y a beaucoup trop de conversion et c'est un peu lâche. Cela vient d'être codé pour vous prouver qu'il est totalement possible de créer un OuputStream personnalisé et de sortir une chaîne. Mais allez simplement dans Horcrux7 et tout va bien avec seulement deux appels de méthode.

Et le monde vit un autre jour ...

SP
la source
9
Le simple fait de lancer un octet sur char ne fonctionnera que sur ascii. Utilisez ByteArrayOutputStream comme Horcrux7
Dave Ray
2
D'accord avec Dave Ray. Vous ne pouvez pas supposer que votre octet est un caractère ASCII. Vous devez interpréter les octets à l'aide d'un codage. Utilisez byteArrayOutputStream.toString ("UTF-8") ou une nouvelle chaîne (byteArrayOutputStream.toByteArray (), "UTF-8").
Martin Dow
16

Voici ce que j'ai fini par faire:

Obj.writeToStream(toWrite, os);
try {
    String out = new String(os.toByteArray(), "UTF-8");
    assertTrue(out.contains("testString"));
} catch (UnsupportedEncondingException e) {
    fail("Caught exception: " + e.getMessage());
}

Où os est un ByteArrayOutputStream.

Adrian Mouat
la source
2
@JavaJigs J'ai clarifié cela au bas de ma réponse il y a près de 5 ans :)
Adrian Mouat
19
Pensez à remplacer "UTF-8"par StandardCharsets.UTF_8.
james.garriss