Yesterday I lamented that far too many Java APIs put useful data into a stream but fail to offer me an accessor method to obtain the data as a String. Today I know how to get that data as a String.
The secret is the ByteArrayOutputStream class. You "print" to this stream, then call toString() on it to get the String. Or you can call toByteArray() if you need that for some reason.
This looks like a painful extra step to my normal way of thinking, but I can see that a case might somehow be made that it is more flexible design, since the mechanism exists to take any method that outputs to streams and obtain the data in a String. I guess having methods that are inappropriately coupled to streams seems convenient when it's so inconvenient to access streams.
All I wanted to do yesterday was send the little chunk of XML I'd manipulated to log4j so I could see it in a test run and make sure it was right without having to write it to a file and go open the file to look at it. I think logging is a perfect example of why there ought to be accessors like this for every piece of data, rather than methods that force you to output.
Minimize side effects!
The problem with doing things like this is that you are completely ignoring the encoding. Encoding is one of the reason why there are parallel IO hierarchies in Java: InputStream/OutputStream vs Reader/Writer. The latter understand characters, the former understand bytes. Unfortunately, every time you go near a Reader/Writer, you need to specify a character encoding. As a rule, I try to prefer Reader/Writer where I can.
Classes to look at:
To be honest, converting a stream to a String is a really useful thing to wrap up into a static utility method.
Re:Not Quite Right
lachoy on 2008-02-11T03:10:48
The commons io library has implementations for useful stuff like this -- see IOUtils#toString() for a stream (with default encoding or with a specified one).Re:Not Quite Right
jdavidb on 2008-02-11T14:20:22
Thanks for the pointer!
Re:Not Quite Right
jdavidb on 2008-02-11T14:18:16
Thank you for the useful information!
For the record, ByteArrayOutputStream.toString() does offer a version which specifies the character encoding as a parameter. But that's definitely something I wasn't thinking about (especially since I just wanted it for logging). My personal wiring is probably far too close to Paul Graham's recent unforgivable sin in making Arc ASCII-only for the taste of people around here, or anyone steeped in Java. It's something I'd prefer to leave as Somebody Else's Problem as much as possible. I love having Unicode around and knowing it buys me a lot; I'd just rather not have to ever think about it, and encodings is one place where that implementation is exposed.
Of course, I'm positive that my employer wouldn't want me to ignore encodings or any other internationalization issue if I was handing strings back to production output, so I definitely need to understand this distinction between Reader/Writer and InputStream/Output Stream. Again, thank you for the useful information.
To be honest, converting a stream to a String is a really useful thing to wrap up into a static utility method.Yeah, and it'd be really useful for somebody to clue the designers of the APIs into the fact that coupling accessing a value to outputting the value with side-effect methods is a bad design choice, too.
:) But yes, I can see that would be good to have. Re:Not Quite Right
Dom2 on 2008-02-12T07:32:24
As to thinking about encodings, I'm completely in agreement with you — everything should use UTF-8!
…Scheme:
(let ((str (with-output-to-string
(write exprs))))
(do-something-to str))
Re:Reminds me of…
jdavidb on 2008-02-11T19:08:07
Or IO::Scalar in Perl.
:) Re:Reminds me of…
Dom2 on 2008-02-12T21:27:52
You no longer need IO::Scalar since perl 5.8. Plain old open will now accept a scalar ref:my $str
open my $fh, '>', \$str;Re:Reminds me of…
jdavidb on 2008-02-13T17:51:51
Cool! Thank you!