Get an OutputStream into a String
Asked Answered
A

6

674

What's the best way to pipe the output from an java.io.OutputStream to a String in Java?

Say I have the method:

  writeToStream(Object o, OutputStream out)

Which writes certain data from the object to the given stream. However, I want to get this output into a String as easily as possible.

I'm considering writing a class like this (untested):

class StringOutputStream extends OutputStream {

  StringBuilder mBuf;

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

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

But is there a better way? I only want to run a test!

Autocrat answered 19/10, 2008 at 20:3 Comment(0)
M
697

I would use a ByteArrayOutputStream. And on finish you can call:

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

or better:

baos.toString( codepage );

For the String constructor, the codepage can be a String or an instance of java.nio.charset.Charset. A possible value is java.nio.charset.StandardCharsets.UTF_8.

The method toString() accepts only a String as a codepage parameter (stand Java 8).

Morula answered 19/10, 2008 at 20:15 Comment(13)
ByteArrayOutputStream has no toArray() method; it does have toByteArray() though. Can you fix the answer? Also, why not use baos.toString(String charsetName) which would be slightly simpler.Landtag
Thanks for the tip with the toString(charset). I have never look on the toString because I have expect a ByteArrayOutputStream@123456. I have also correct the mistake with toByteArray().Morula
Also note that some codepages are not installed unless you explicitly ask for them with a custom install.Hospodar
can return simply baos.toString(), what is the coepage thing for?Depriest
A bytearray is just binary data. As (unicode) text can be encoded binary in many different ways, the ByteArrayOutputStream needs to know what encoding was used to encode the bytes, so it can use the same encoding to decode the bytes to a string again. Simply using toString without an argument is not wise as you just ignore the problem instead of tackling it; Java will use the platform encoding which could be correct...or not. It's random basically. You need to find out what encoding was used to write the text to bytes and pass that encoding to toString.Spessartite
Just a clarification on the codepage referenced here: in Java you can use Charset.defaultCharset() or Charset.forName("specific charset"); What worked for me was: new String(baos.toByteArray(), Charset.defaultCharset());Centuple
@WallaceBrown using defaultCharset is no better than ignoring the charset altogether - you need to find out what it is before you use toStringSampan
@artbristol: While your warnings about defaultCharset are technically true, it seems that for this case (a unit test) it won't matter - the string will be encoded and decoded on the same machine, during the same execution of the program. There's really no risk here that different codepages will be used.Olsewski
@TomasLycken yeah, you're right, if it's both encoded and decoded on the same machine that's fine. Although what if the unit test compares the result to some fixed String?Sampan
@TomasLycken only if the writer also uses defaultCharset.Osugi
StandardCharsets.UTF_8 is a Charset, not a String. Moreover the parameter is called charsetName, not codepage.Plonk
Are there any limitations to baos.toString() compared to baos.toByteArray()? I have used first on some old project but for some reason I was not able to generate SHA-1 hash digest from it but was working fine for second one still I have no clue why maybe something in the reading file.Heartbroken
@Heartbroken You can generate a hash only from a byte array and not from a string. If you have a string then you must convert it first with a charset to a byte array. The result can differ with an different CharSet.Morula
E
55

I like the Apache Commons IO library. Take a look at its version of ByteArrayOutputStream, which has a toString(String enc) method as well as toByteArray(). Using existing and trusted components like the Commons project lets your code be smaller and easier to extend and repurpose.

Erst answered 19/10, 2008 at 20:23 Comment(3)
Save yourself a year of your life and read through all the common's APIs so when you encounter a problem, you can unleash a fully tested and community owned solution.Earl
Hmm, I'm an avid Apache Commons user, but in this case I fail to see why you should use Commons IO's ByteArrayOutputStream instead of JDK's own java.io.ByteArrayOutputStream. The latter also provides toString(String charsetName) and toByteArray() methods. Care to elaborate?Landtag
Yeah, since the original context was a better way to stream and extract content, I included the Commons IO example since it included a 'write(InputStream)' method for a then-undefined/questionable mechanism for populating the OutputStream. I'd go with the JDK, too.Erst
V
30

This worked nicely

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

method call =>> marshaller.marshal( (Object) toWrite , (OutputStream) output);

then to print the string or get it just reference the "output" stream itself As an example, to print the string out to console =>> System.out.println(output);

FYI: my method call marshaller.marshal(Object,Outputstream) is for working with XML. It is irrelevant to this topic.

This is highly wasteful for productional use, there is a way too many conversion and it is a bit loose. This was just coded to prove to you that it is totally possible to create a custom OuputStream and output a string. But just go Horcrux7 way and all is good with merely two method calls.

And the world lives on another day....

Venesection answered 20/6, 2009 at 20:13 Comment(2)
Just casting a byte to char will only work on ascii. Use ByteArrayOutputStream like Horcrux7Findlay
Agreed with Dave Ray. You can't assume that your byte is an ASCII character. You need to interpret the bytes using an encoding. Use byteArrayOutputStream.toString("UTF-8") or new String(byteArrayOutputStream.toByteArray(), "UTF-8").Weidman
A
19

Here's what I ended up doing:

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

Where os is a ByteArrayOutputStream.

Autocrat answered 3/11, 2008 at 21:4 Comment(2)
@JavaJigs I clarified this at the bottom of my answer nearly 5 years ago :)Autocrat
Consider replacing "UTF-8" with StandardCharsets.UTF_8.Pyrene
D
10
baos.toString(StandardCharsets.UTF_8);

Converts the buffer's contents into a string by decoding the bytes using the named charset.

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

Diplomate answered 19/5, 2020 at 5:48 Comment(0)
E
-2

Here's what I did (don't use this in production, this is not great! But it makes fixing multiple errors easier.)

  • Create a list that holds Exceptions.

  • Create a logger to log exceptions.

  • Use the code below:

    private static void exceptionChecker() throws Exception { if(exceptionList.isEmpty()) return; //nothing to do :) great news

      //create lock for multithreading
      synchronized (System.err){
          //create new error stream
          ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
          PrintStream errorOut = new PrintStream(byteArrayOutputStream);
    
          //save standard err  out
          PrintStream standardErrOut = System.err;
    
          try{
              //set new error stream
              System.setErr(errorOut);
    
              exceptionList.forEach(exception -> {
                  exception.printStackTrace();
                  System.err.println("<---------->");
              });
    
    
          } finally {
              //reset everything back to normal
              System.setErr(standardErrOut);
    
              //Log all the exceptions
              exceptionLogger.warning(byteArrayOutputStream.toString());
    
              //throw final generic exception
              throw new Exception();
          }
      }}
    

This isn't great as you are throwing an error in the finally block and it locks on the error stream, but it works for dev purposes.

Elgon answered 6/9, 2022 at 8:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.