It’s not quite clear what your multi-step conversion is trying to achieve.
The write(int)
method will receive a single byte
, for some reasons passed as int
. Since the byte originates from a PrintStream
constructed as new PrintStream(out)
, it will be encoded in the system’s default encoding.
One way to convert it back to a String
using the same (system default) encoding would be
byte[] array = { (byte) b };
String text = new String(array);
However, there are some things to keep in mind
The system default encoding might not support all unicode characters. Hence, the code above is correct, but still may cause data loss. You should therefore not rely on the system default encoding, but use an explicit encoding capable of handling all characters, e.g. UTF-8, for both sides.
This approach can’t handle multi-byte encodings as when trying to convert a single byte of a multi byte sequence back to a string you’ll end up at invalid or wrong characters. A portable program can not assume to know whether the system default encoding is multi-byte or not. When following the advice of point 1, you’ll always end up at a multi-byte encoding. You need a solution that can accumulate multiple bytes before converting them to a string. That will even improve the efficiency.
Here is a complete solution:
public class PrintToTextArea extends ByteArrayOutputStream implements Runnable
{
public static PrintStream create(JTextArea ta)
{
try
{
return new PrintStream(new PrintToTextArea(ta), true, "UTF-8");
}
catch(UnsupportedEncodingException ex)
{
throw new AssertionError("UTF-8 should always be supported", ex);
}
}
private final JTextArea target;
private PrintToTextArea(JTextArea ta)
{
super(100);
target = ta;
}
@Override
public void flush()
{
if(EventQueue.isDispatchThread()) run(); else EventQueue.invokeLater(this);
}
@Override
public synchronized void run()
{
target.append(new String(buf, 0, count, StandardCharsets.UTF_8));
count = 0;
}
}
It uses UTF-8 on both sides, to handle all unicode characters, and gathers bytes in the buffer of a ByteArrayOutputStream
, to create a new String
and append it when flush
is called, which happens automatically on newlines or when flush
has been called explicitly.
You can try it with something like
public static void main(String[] args) {
try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); }
catch(ReflectiveOperationException | UnsupportedLookAndFeelException ex) {}
JFrame f = new JFrame();
JTextArea ta = new JTextArea(40, 60);
f.setContentPane(new JScrollPane(ta));
PrintStream ps = PrintToTextArea.create(ta);
System.setOut(ps);
System.setErr(ps);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
Thread.dumpStack();
System.out.println("è");
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
System.out.println("??ü");
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
System.out.println("u263a ud83dude80");
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…