首页 > 解决方案 > (Java) 字符 'é' 在 JTextArea 中显示错误

问题描述

第一次发布东西,我有一个问题。

我正在使用 JFrame 和 JTextArea,将我的控制台输出重定向到 JTextArea。在控制台中它正确显示。这是重定向的代码:

public PrintStream redirectOut() {
    
    OutputStream out = new OutputStream() {
        @Override
        
        public void write(int b) throws IOException {
            String k = String.valueOf((char) b);
            byte[] o = k.getBytes();
            String text = new String(o,"Cp1252");
            output.append(k);
        }
    };
    PrintStream ps = new PrintStream(out);
    System.setOut(ps);
    System.setErr(ps);
    return ps;
}

如果我将字符“é”以相同的方式放入 TextArea 中,它会正确显示,但不是这样。这是为什么?有人有解决方案吗?(输出是我的 JTextArea) 谢谢!

标签: javastringswingencodingjtextarea

解决方案


目前尚不清楚您的多步转换试图实现什么。

write(int)方法将接收单个byte,由于某些原因传递为int。由于字节来自PrintStream构造的 as new PrintStream(out),它将以系统的默认编码进行编码。

将其转换回String使用相同(系统默认)编码的一种方法是

byte[] array = { (byte) b };
String text = new String(array);

但是,有一些事情要记住

  1. 系统默认编码可能不支持所有 unicode 字符。因此,上面的代码是正确的,但仍然可能导致数据丢失。因此,您不应依赖系统默认编码,而应使用能够处理所有字符的显式编码,例如 UTF-8,用于双方。

  2. 这种方法无法处理多字节编码,因为当尝试将多字节序列的单个字节转换回字符串时,您最终会得到无效或错误的字符。可移植程序不能假设知道系统默认编码是否为多字节。遵循第 1 点的建议时,您将始终以多字节编码告终。您需要一个可以在将多个字节转换为字符串之前累积多个字节的解决方案。这甚至会提高效率。

这是一个完整的解决方案:

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

它在两边都使用 UTF-8 来处理所有 unicode 字符,并在 a 的缓冲区中收集字节ByteArrayOutputStream,创建一个新的String并在调用时附加它flush,这会在换行符或flush显式调用时自动发生。

您可以尝试使用类似的东西

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   \ud83d\ude80");
  LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
}

推荐阅读