Uzyskaj OutputStream w ciąg

580

Jaki jest najlepszy sposób na przesłanie danych wyjściowych z pliku java.io.OutputStream do ciągu znaków w Javie?

Powiedz, że mam metodę:

  writeToStream(Object o, OutputStream out)

Który zapisuje pewne dane z obiektu do podanego strumienia. Chcę jednak, aby dane wyjściowe zostały przetworzone na ciąg znaków tak łatwo, jak to możliwe.

Zastanawiam się nad napisaniem takiej klasy (nieprzetestowane):

class StringOutputStream extends OutputStream {

  StringBuilder mBuf;

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

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

Ale czy jest lepszy sposób? Chcę tylko przeprowadzić test!

Adrian Mouat
źródło
6
Czy masz tylko bajty ASCII? Czy nie potrzebujesz strony kodowej?
Horcrux7
W takim przypadku tak. Jednak dobra uwaga - nie myślałem o tym.
Adrian Mouat,

Odpowiedzi:

607

Chciałbym użyć ByteArrayOutputStream. Na koniec możesz zadzwonić:

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

albo lepiej:

baos.toString( codepage );

Dla Stringkonstruktora codepagemoże to być Stringinstancja lub java.nio.charset.Charset . Możliwą wartością jest java.nio.charset.StandardCharsets.UTF_8 .

Sposób toString()akceptuje tylko Stringjako codepageparametr (stand Java 8).

Horkruks 7
źródło
8
ByteArrayOutputStream nie ma metody toArray (); ma jednak funkcję toByteArray (). Czy potrafisz naprawić odpowiedź? Ponadto, dlaczego nie użyć baos.toString (String charsetName), co byłoby nieco prostsze.
Jonik
35
Bytearray to tylko dane binarne. Ponieważ (Unicode) tekst może być kodowany binarnie na wiele różnych sposobów, ByteArrayOutputStream musi wiedzieć, jakie kodowanie zostało użyte do zakodowania bajtów, więc może użyć tego samego kodowania do ponownego odkodowania bajtów na ciąg znaków. Proste użycie toString bez argumentu nie jest mądre, ponieważ po prostu ignorujesz problem zamiast go rozwiązać; Java użyje kodowania platformy, które może być poprawne ... lub nie. Zasadniczo jest losowy. Musisz dowiedzieć się, jakiego kodowania użyto do zapisania tekstu w bajtach i przekazać to kodowanie do ToString.
Stijn de Witt,
10
Tylko wyjaśnienie na stronie kodowej, o której tu mowa: w Javie można użyć Charset.defaultCharset () lub Charset.forName („określony zestaw znaków”); Dla mnie zadziałały: new String (baos.toByteArray (), Charset.defaultCharset ());
Wallace Brown,
7
@ Korzystanie z WallaceBrown defaultCharsetnie jest lepsze niż całkowite zignorowanie zestawu znaków - musisz dowiedzieć się, co to jest przed użyciemtoString
artbristol
4
StandardCharsets.UTF_8jest, a Charsetnie String. Ponadto parametr jest wywoływany charsetName, a nie codepage.
OrangeDog
46

Lubię bibliotekę Apache Commons IO. Spójrz na jego wersję ByteArrayOutputStream , która ma również toString(String enc)metodę toByteArray(). Korzystanie z istniejących i zaufanych komponentów, takich jak projekt Commons, pozwala na zmniejszenie kodu i łatwiejsze rozszerzanie i ponowne przeznaczanie.

Joe Liversedge
źródło
10
Zaoszczędź sobie roku swojego życia i zapoznaj się ze wszystkimi interfejsami API tego wspólnego, aby w razie problemów można było wypróbować w pełni przetestowane rozwiązanie będące własnością społeczności.
Bob Herrmann
15
Hmm, jestem zapalonym użytkownikiem Apache Commons, ale w tym przypadku nie rozumiem, dlaczego warto użyć ByteArrayOutputStream Commons IO zamiast własnego java.io.ByteArrayOutputStream JDK. Ten ostatni zapewnia także metody toString (String charsetName) i toByteArray (). Możesz rozwinąć temat?
Jonik
1
Tak, ponieważ pierwotny kontekst był lepszym sposobem przesyłania strumieniowego i wyodrębniania treści, dołączyłem przykład Commons IO, ponieważ zawiera on metodę „write (InputStream)” dla niezdefiniowanego wówczas / wątpliwego mechanizmu wypełniania OutputStream. Ja też bym poszedł z JDK.
Joe Liversedge
23

To działało ładnie

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

wywołanie metody = >> marshaller.marshal( (Object) toWrite , (OutputStream) output);

następnie, aby wydrukować ciąg lub uzyskać go, po prostu odwołaj się do samego strumienia „wyjściowego” Jako przykład, wydrukuj ciąg do konsoli = >> System.out.println(output);

FYI: moje wywołanie metody marshaller.marshal(Object,Outputstream)służy do pracy z XML. Nie ma znaczenia dla tego tematu.

Jest to bardzo marnotrawne do użytku produkcyjnego, jest zbyt wiele konwersji i jest nieco luźne. Zostało to po prostu zakodowane, aby udowodnić, że można całkowicie utworzyć niestandardowy OuputStream i wyprowadzić ciąg. Ale po prostu idź Horcrux7 i wszystko jest dobrze z zaledwie dwoma wywołaniami metod.

A świat żyje w innym dniu ...

SM
źródło
9
Samo przesłanie bajtu na char działa tylko na ascii. Użyj ByteArrayOutputStream jak Horcrux7
Dave Ray
2
Uzgodniono z Dave'em Rayem. Nie możesz założyć, że twój bajt jest znakiem ASCII. Musisz interpretować bajty za pomocą kodowania. Użyj byteArrayOutputStream.toString („UTF-8”) lub nowego ciągu (byteArrayOutputStream.toByteArray (), „UTF-8”).
Martin Dow
16

Oto co skończyłem:

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

Gdzie os jest a ByteArrayOutputStream.

Adrian Mouat
źródło
2
@JavaJigs Wyjaśniłem to na dole mojej odpowiedzi prawie 5 lat temu :)
Adrian Mouat
19
Rozważyć wymianę "UTF-8"z StandardCharsets.UTF_8.
james.garriss,