Obtenga un OutputStream en una cadena

580

¿Cuál es la mejor manera de canalizar la salida de un java.io.OutputStream a una cadena en Java?

Digamos que tengo el método:

  writeToStream(Object o, OutputStream out)

Que escribe ciertos datos del objeto en la secuencia dada. Sin embargo, quiero obtener esta salida en una cadena lo más fácilmente posible.

Estoy considerando escribir una clase como esta (sin probar):

class StringOutputStream extends OutputStream {

  StringBuilder mBuf;

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

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

Pero hay una manera mejor? ¡Solo quiero hacer una prueba!

Adrian Mouat
fuente
66
¿Tiene solo bytes ASCII? ¿NO necesitas página de códigos?
Horcrux7
En este caso, si. Sin embargo, buen punto: no lo había pensado.
Adrian Mouat

Respuestas:

607

Yo usaría a ByteArrayOutputStream. Y al finalizar puedes llamar:

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

o mejor:

baos.toString( codepage );

Para el Stringconstructor, codepagepuede ser una Stringo una instancia de java.nio.charset.Charset . Un valor posible es java.nio.charset.StandardCharsets.UTF_8 .

El método toString()acepta solo a Stringcomo codepageparámetro (stand Java 8).

Horrocrux7
fuente
8
ByteArrayOutputStream no tiene el método toArray (); Sin embargo, tiene que byByteArray (). ¿Puedes arreglar la respuesta? Además, ¿por qué no usar baos.toString (String charsetName) que sería un poco más simple?
Jonik
35
Un bytearray es solo información binaria. Como el texto (unicode) se puede codificar en binario de muchas maneras diferentes, ByteArrayOutputStream necesita saber qué codificación se usó para codificar los bytes, por lo que puede usar la misma codificación para decodificar los bytes en una cadena nuevamente. Simplemente usar toString sin un argumento no es aconsejable ya que simplemente ignora el problema en lugar de abordarlo; Java usará la codificación de la plataforma que podría ser correcta ... o no. Es al azar básicamente. Debe averiguar qué codificación se usó para escribir el texto en bytes y pasar esa codificación a toString.
Stijn de Witt
10
Solo una aclaración sobre la página de códigos a la que se hace referencia aquí: en Java puede usar Charset.defaultCharset () o Charset.forName ("charset específico"); Lo que funcionó para mí fue: new String (baos.toByteArray (), Charset.defaultCharset ());
Wallace Brown el
77
El uso de @WallaceBrown defaultCharsetno es mejor que ignorar por completo el juego de caracteres: debe averiguar qué es antes de usarlotoString
artbristol
44
StandardCharsets.UTF_8es un Charset, no un String. Además, el parámetro se llama charsetName, no codepage.
OrangeDog
46

Me gusta la biblioteca Apache Commons IO. Eche un vistazo a su versión de ByteArrayOutputStream , que también tiene un toString(String enc)método toByteArray(). El uso de componentes existentes y confiables como el proyecto Commons permite que su código sea más pequeño y más fácil de extender y reutilizar.

Joe Liversedge
fuente
10
Ahórrese un año de su vida y lea todas las API de Common para que cuando encuentre un problema, pueda desatar una solución totalmente probada y de propiedad comunitaria.
Bob Herrmann
15
Hmm, soy un ávido usuario de Apache Commons, pero en este caso no entiendo por qué deberías usar ByteArrayOutputStream de Commons IO en lugar de java.io.ByteArrayOutputStream de JDK. Este último también proporciona los métodos toString (String charsetName) y toByteArray (). ¿Cuidado para elaborar?
Jonik
1
Sí, dado que el contexto original era una mejor manera de transmitir y extraer contenido, incluí el ejemplo de IO de Commons ya que incluía un método 'write (InputStream)' para un mecanismo entonces indefinido / cuestionable para poblar OutputStream. Yo también iría con el JDK.
Joe Liversedge
23

Esto funcionó bien

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();
    }
};

llamada al método = >> marshaller.marshal( (Object) toWrite , (OutputStream) output);

luego para imprimir la cadena u obtenerla, simplemente haga referencia a la secuencia de "salida" en sí misma Como ejemplo, para imprimir la cadena en la consola = >> System.out.println(output);

FYI: mi llamada al método marshaller.marshal(Object,Outputstream)es para trabajar con XML. Es irrelevante para este tema.

Esto es muy derrochador para uso productivo, hay demasiadas conversiones y es un poco flojo. Esto fue codificado para demostrarle que es totalmente posible crear un OuputStream personalizado y generar una cadena. Pero simplemente siga Horcrux7 y todo está bien con solo dos llamadas a métodos.

Y el mundo vive otro día ...

em
fuente
99
Solo lanzar un byte a char solo funcionará en ascii. Use ByteArrayOutputStream como Horcrux7
Dave Ray
2
De acuerdo con Dave Ray. No puede suponer que su byte es un carácter ASCII. Necesita interpretar los bytes usando una codificación. Utilice byteArrayOutputStream.toString ("UTF-8") o una nueva cadena (byteArrayOutputStream.toByteArray (), "UTF-8").
Martin Dow
16

Esto es lo que terminé haciendo:

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());
}

Donde os es a ByteArrayOutputStream.

Adrian Mouat
fuente
2
@JavaJigs Aclaré esto al final de mi respuesta hace casi 5 años :)
Adrian Mouat
19
Considere reemplazar "UTF-8"con StandardCharsets.UTF_8.
james.garriss
0
baos.toString(StandardCharsets.UTF_8);

Convierte el contenido del búfer en una cadena decodificando los bytes usando el juego de caracteres con nombre.

Java 14 - https://docs.oracle.com/

jschnasse
fuente