콘솔에서 JavaFX TextArea 로의 출력을위한 UTF-8 인코딩

Dec 22 2020

콘솔의 출력을 JavaFX TextArea로 리디렉션하고 여기에 제안 사항을 따릅니다. JavaFX : SceneBuilder에서 생성 된 TextArea로 콘솔 출력 리디렉션

PrintStream ()에서 charset을 UTF-8로 설정하려고했지만 잘 보이지 않습니다 . charset을 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));
}

이것은 각 개별 바이트를 문자로 변환합니다. 즉, 각 문자가 단일 바이트로 표시된다고 가정합니다. 반드시 사실은 아닙니다. UTF-8과 같은 일부 인코딩 은 단일 문자를 나타 내기 위해 여러 바이트를 사용할 수 있습니다. 256 자 이상을 나타낼 수 있어야합니다.

들어오는 바이트를 올바르게 디코딩해야합니다. 직접 시도하는 것보다 .NET과 같은 것을 사용하는 방법을 찾는 것이 좋습니다 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;
      }
    }
  }
}

buffer위 의 사용은 JavaFX 응용 프로그램 스레드 에 작업 이 넘치지 않도록하기위한 것 입니다.

고려해야 할 몇 가지 다른 사항 :

  • 문자열 리터럴을 사용하고 있으므로 소스 파일을 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에서 기본 텍스트 파일 인코딩을 변경하는 방법