java - 创建正确处理两字节字符的自定义 OutputStream

标签 java utf-8

基本上,我想将控制台输出重定向到 javafx TextArea。我现在正在使用此代码。

public static void init() {
    //GUI code. TextArea variable is named textArea
    Console console = new Console(textArea);
    PrintStream ps = new PrintStream(console, true);
    System.setOut(ps);
    System.setErr(ps);
}

public static class Console extends OutputStream {

    private TextArea output;
    private PrintStream out;

    public Console(TextArea ta) {
        this.output = ta;
        out = System.out;
    }

    @Override
    public void write(int i) throws IOException {
        Platform.runLater(() ->  {
            output.appendText(String.valueOf((char) i));
            out.print(String.valueOf((char) i));

        });


    }

但是,有一个问题。 ASCII 字符工作得很好(因为它们都只使用一个字节)。但是,当我尝试打印需要两个字节进行编码的符号(例如西里尔字符)时,它们显然无法正确打印,我得到类似的东西

java.io.IOException: ￐ᄀ￐ᄌ￑チ￑ツ￐ᄉ￐ᄐ￐ᄉ ￐ᄑ￐ᄉ ￑テ￐ᄡ￐ᄚ￐ᄉ￑ツ￑チ￑マ ￐ᄑ￐ᄚ￐ᄍ￑ツ￐ᄌ ￑テ￐ᄎ￐ᄚ￐ᄋ￐ᄚ￐ᄑ￐ᄑ￑ヒ￐ᄍ ￐﾿￑テ￑ツ￑フ

有什么办法可以解决这个问题吗?也许使用不同的方法?

更新

这是我最后想到的。思考是否可以以某种方式优化它。

public static class Console extends OutputStream {

        private TextArea output;
        private PrintStream out;
        private ArrayList<Byte> bytes = new ArrayList<>();

        public Console(TextArea ta) {
            this.output = ta;
            out = System.out;
        }

        @Override
        public void write(int i) throws IOException {
            Platform.runLater(() ->  {
                bytes.add((byte)i);

                byte[] array = new byte[bytes.size()];
                int q = 0;
                for (Byte current : bytes) {
                    array[q] = current;
                    q++;
                }
                try {
                    output.setText(new String(array, "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                out.write(i);
            });


        }

    }

更新2

经过一些优化,这就是我最终得到的代码。

//Cut all the imports
public class LogScreen {

    private static TextArea textArea = new TextArea();
    private static List<Byte> bytes = new ArrayList<>();
    //And some other unnecessary variables

    public static void show() {
        update();
        logStage.showAndWait();

    }

    public static void init() {
        //Cut window initialization
        PrintStream ps = new PrintStream(new Console(), true);
        System.setOut(ps);
        System.setErr(ps);
    }

    public static void update() {
        byte[] array = new byte[bytes.size()];
        int q = 0;
        for (Byte current : bytes) {
            array[q] = current;
            q++;
        }
        try {
            textArea.setText(new String(array, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public static class Console extends OutputStream {
        private PrintStream out;

        public Console() {
            out = System.out;
        }

        @Override
        public void write(int i) throws IOException {
            Platform.runLater(() ->  {
                bytes.add((byte)i);
                out.write(i);
                if (logStage.isShowing()) {
                    update();
                }
            });
        }

        @Override
        public void write(byte[] i) {
            Platform.runLater(() -> {
                for (byte b : i) {
                    bytes.add(b);
                    out.write(b);
                }
                if (logStage.isShowing()) {
                    update();
                }
            });
        }
    }
}

最佳答案

它不能这样工作。

来自 OutputStream.write(int) 的 javadoc

Writes the specified byte to this output stream. The general contract for write is that one
byte is written to the output stream. The byte to be written is the eight low-order bits
of the argument b. The 24 high-order bits of b are ignored.

这意味着对于由两个字节组成的任何字符(例如 П - 0xD0、0x9F),此方法将被调用两次。但是,如果您使用 PrintStream.print(String.valueOf((char) i)) ,您将根据默认编码创建基于这些单字节的字符。这会导致您的示例出现乱码输出。

您应该使用out.write(int),而不是使用out.print(..),它在字节级别而不是字符上写入。

以下代码片段将预期输出打印到控制台。

static class Console extends OutputStream {
    private PrintStream out;

    public Console() {
        out = System.out;
    }

    @Override
    public void write(int i) throws IOException {
        out.write(i);
    }
}

public static void main(String[] args) throws Exception {
    Console console = new Console();
    PrintStream ps = new PrintStream(console, true);
    System.setOut(ps);
    System.setErr(ps);
    ps.println("Привет, мир!");
}

关于java - 创建正确处理两字节字符的自定义 OutputStream,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33628536/

相关文章:

java - hibernate 禁用缓存

java - Files.write() 无法正常工作,而 BufferedWriter 似乎工作正常

java - Netbeans 9 - 打印 Unicode 字符

python - 当 AJAX 在 Django 中保存(并重新填充)我的表单时,为什么我会收到 '

java - 特定对象列表的内存使用情况

java - 我可以使用已经编写的 Jmh 微基准来预热生产代码吗

java - 从静态上下文引用非静态变量

c++ - wcout 没有按要求输出

c# - 如何将 utf8 字符串转换为 utf8 字节数组?

python - Windows 文件名在 Linux 中显示损坏的字符