コンソールからJavaFXTextAreaへの出力用のUTF-8エンコーディング

Dec 22 2020

コンソールの出力をJavaFXTextAreaにリダイレクトしたいので、ここでの提案に従います。JavaFX:コンソール出力をSceneBuilderで作成されたTextAreaにリダイレクトします。

PrintStream()で文字セットをUTF-8に設定しようとしましたが、見栄えがよくありません。文字セットをUTF-16に設定すると少し改善されますが、それでも判読できません。

Eclipse IDEでは、コンソールで想定されるテキスト出力は正常になります。

KHA khởi đầu phiên giao dịch sáng nay ở mức 23600 điểm, khối lượng giao dịch trong ngày đạt 765 cổ phiếu, tương đương khoảng 18054000 đồng.

Controller.java

public class Controller {
    @FXML
    private Button button;

    public Button getButton() {
        return button;
    }

    @FXML
    private TextArea textArea;

    public TextArea getTextArea() {
        return textArea;
    }

    private PrintStream printStream;

    public PrintStream getPrintStream() {
        return printStream;
    }

    public void initialize() {
        textArea.setWrapText(true);
        printStream = new PrintStream(new UITextOutput(textArea), true, StandardCharsets.UTF_8);
    } // Encoding set to UTF-8

    public class UITextOutput extends OutputStream {
        private TextArea text;

        public UITextOutput(TextArea text) {
            this.text = text;
        }

        public void appendText(String valueOf) {
            Platform.runLater(() -> text.appendText(valueOf));
        }

        public void write(int b) throws IOException {
            appendText(String.valueOf((char) b));
        }
    }
}

UI.java

public class UI extends Application {
    @Override
    public void start(Stage stage) {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Sample.fxml"));
            Parent root = loader.load();
            Controller control = loader.getController();

            stage.setTitle("Title");
            stage.setScene(new Scene(root));
            stage.show();

            control.getButton().setOnAction(new EventHandler<ActionEvent>() {
                public void handle(ActionEvent event) {
                    try {
                        System.setOut(control.getPrintStream());
                        System.setErr(control.getPrintStream());
                        System.out.println(
                                "KHA khởi đầu phiên giao dịch sáng nay ở mức 23600 điểm, khối lượng giao dịch trong ngày đạt 765 cổ phiếu, tương đương khoảng 18054000 đồng.");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.BorderPane?>


<BorderPane prefHeight="339.0" prefWidth="468.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/11.0.1" fx:controller="main.Controller">
   <center>
      <TextArea fx:id="textArea" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
   </center>
   <right>
      <Button fx:id="button" mnemonicParsing="false" onAction="#getButton" text="Button" BorderPane.alignment="CENTER" />
   </right>
</BorderPane>

私はまだJavaに慣れていないので、PrintStreamまたはOutputStreamがどのように正確に機能するかについてはよく知りません。私の無知を許してください。

すべての提案をいただければ幸いです。

回答

1 Slaw Dec 23 2020 at 03:29

私はあなたの問題がこのコードによって引き起こされていると信じています:

public void write(int b) throws IOException {
    appendText(String.valueOf((char) b));
}

これは、個々のバイトを文字に変換することです。つまり、各文字が1バイトで表されていることを前提としています。それは必ずしも真実ではありません。UTF-8などの一部のエンコーディングでは、複数のバイトを使用して1つの文字を表す場合があります。256文字以上を表現できるようにしたい場合は、そうする必要があります。

着信バイトを適切にデコードする必要があります。これを自分でやろうとするよりも、のようなものを使用する方法を見つける方がよいでしょうBufferedReader。幸いにもそれがで可能だPipedInputStreamPipedOutputStream。例えば:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;

import static java.nio.charset.StandardCharsets.UTF_8;

public class Main extends Application {

  @Override
  public void start(Stage primaryStage) {
    TextArea area = new TextArea();
    area.setWrapText(true);

    redirectStandardOut(area);

    primaryStage.setScene(new Scene(area, 800, 600));
    primaryStage.show();

    System.out.println(
        "KHA khởi đầu phiên giao dịch sáng nay ở mức 23600 điểm, khối lượng giao dịch trong ngày đạt 765 cổ phiếu, tương đương khoảng 18054000 đồng.");
  }

  private void redirectStandardOut(TextArea area) {
    try {
      PipedInputStream in = new PipedInputStream();
      System.setOut(new PrintStream(new PipedOutputStream(in), true, UTF_8));

      Thread thread = new Thread(new StreamReader(in, area));
      thread.setDaemon(true);
      thread.start();
    } catch (IOException ex) {
      throw new UncheckedIOException(ex);
    }
  }

  private static class StreamReader implements Runnable {

    private final StringBuilder buffer = new StringBuilder();
    private boolean notify = true;

    private final BufferedReader reader;
    private final TextArea textArea;

    StreamReader(InputStream input, TextArea textArea) {
      this.reader = new BufferedReader(new InputStreamReader(input, UTF_8));
      this.textArea = textArea;
    }

    @Override
    public void run() {
      try (reader) {
        int charAsInt;
        while ((charAsInt = reader.read()) != -1) {
          synchronized (buffer) {
            buffer.append((char) charAsInt);
            if (notify) {
              notify = false;
              Platform.runLater(this::appendTextToTextArea);
            }
          }
        }
      } catch (IOException ex) {
        throw new UncheckedIOException(ex);
      }
    }

    private void appendTextToTextArea() {
      synchronized (buffer) {
        textArea.appendText(buffer.toString());
        buffer.delete(0, buffer.length());
        notify = true;
      }
    }
  }
}

上記の使用はbufferJavaFXアプリケーションスレッドがタスクで溢れるのを回避するための試みです。

あなたが考慮に入れる必要がある他のいくつかの事柄:

  • 文字列リテラルを使用しているため、ソースファイルをUTF-8で保存し、コードを-encoding UTF-8。でコンパイルしていることを確認してください。
  • で使用するフォントが、必要TextAreaなすべての文字を表すことができることを確認してください。
  • でアプリケーションを実行する必要がある可能性もありますが-Dfile.encoding=UTF-8、よくわかりません。私はしませんでした、そしてそれはまだ私のために働きました。
Nickitiki Dec 22 2020 at 15:47

デフォルトのJVMエンコーディングをUTF-8に設定してみてください。

java -Dfile.encoding=UTF-8 -jar YourJarfile.jar

詳細については、次のスレッドを参照してください。デフォルトのJava文字エンコードの設定

ファイルをエクスポートしたくない場合は、Eclipseの[設定]> [一般]> [ワークスペース]に移動し、テキストファイルのエンコードをUTF-8(または必要なエンコード)に設定します。

さらにいくつかの詳細があります:Eclipseでデフォルトのテキストファイルエンコーディングを変更する方法