【问题标题】:(Java) Character 'é' is displayed wrong in JTextArea(Java) 字符 'é' 在 JTextArea 中显示错误
【发布时间】:2021-05-02 09:48:57
【问题描述】:

第一次发帖,有个问题。

我正在使用 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) 谢谢!

【问题讨论】:

  • 显示正确,但不是这样 抱歉,我不明白那个说法。除此之外,不要解释你的代码应该做什么。而是给出一个真正的minimal reproducible example,在你的情况下,只是一个带有 textarea 和那个文本的 jframe,也许是你得到的截图。并更好地解释您认为错误的地方。
  • 它应该是 Marie-héléne 但显示:Marie-h←l│ne 如果我用文本而不是 k 更改它,它会显示:Marie-h?l?ne
  • 不要将此类信息放入 cmets,请相应地编辑您的问题。
  • 你在OutputStream 中做了一些奇怪的事情,得到了奇怪的结果。这是预期的行为。 b 是默认字符编码中的字节值。您将其解释为char,这是错误的,然后您调用getBytes(),它使用默认字符编码将错误解释的字符转换为字节数组,然后,您使用new String(o,"Cp1252") 解释字节数组使用显式编码。也许它匹配默认编码,也许不匹配。

标签: java string swing encoding jtextarea


【解决方案1】:

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

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

使用相同(系统默认)编码将其转换回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 字符,并在 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));
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 2015-06-24
    • 2010-10-17
    • 1970-01-01
    • 2012-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多