Pengkodean UTF-8 untuk keluaran dari Konsol ke JavaFX TextArea

Dec 22 2020

Saya ingin mengarahkan output di Console ke JavaFX TextArea, dan saya mengikuti saran di sini: JavaFX: Alihkan output konsol ke TextArea yang dibuat di SceneBuilder

Saya mencoba mengatur charset ke UTF-8 di PrintStream (), tetapi tidak terlihat begitu baik . Menyetel charset ke UTF-16 sedikit meningkatkannya, tetapi masih tidak terbaca .

Di Eclipse IDE, keluaran teks yang seharusnya di Konsol ternyata baik-baik saja:

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>

Saya masih baru mengenal Java jadi saya tidak terbiasa dengan cara kerja PrintStream atau OutputStream. Mohon maafkan ketidaktahuan saya.

Setiap saran dihargai.

Jawaban

1 Slaw Dec 23 2020 at 03:29

Saya yakin masalah Anda disebabkan oleh kode ini:

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

Ini mengubah setiap byte individu menjadi karakter. Dengan kata lain, ini mengasumsikan setiap karakter diwakili oleh satu byte. Itu belum tentu benar. Beberapa pengkodean, seperti UTF-8 , dapat menggunakan beberapa byte untuk mewakili satu karakter. Mereka harus melakukannya jika mereka ingin dapat mewakili lebih dari 256 karakter.

Anda harus mendekode byte yang masuk dengan benar. Daripada mencoba melakukan ini sendiri, akan lebih baik mencari cara untuk menggunakan sesuatu seperti BufferedReader. Untungnya itu mungkin dengan PipedInputStreamdan PipedOutputStream. Sebagai contoh:

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

Penggunaan di bufferatas adalah upaya untuk menghindari membanjiri Thread Aplikasi JavaFX dengan tugas.

Beberapa hal lain yang perlu Anda pertimbangkan:

  • Karena Anda menggunakan literal string, pastikan Anda menyimpan file sumber dengan UTF-8 dan mengompilasi kode dengan -encoding UTF-8.
  • Pastikan font yang Anda gunakan dengan TextAreacan mewakili semua karakter yang Anda inginkan.
  • Ini mungkin Anda juga perlu menjalankan aplikasi dengan -Dfile.encoding=UTF-8tapi aku tidak yakin. Saya tidak melakukannya dan itu masih berhasil untuk saya.
Nickitiki Dec 22 2020 at 15:47

Coba setel pengkodean JVM default Anda ke UTF-8.

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

Untuk lebih jelasnya lihat utas ini: Mengatur pengkodean karakter Java default

Jika Anda tidak ingin mengekspor file Anda, masuk ke Eclipse Preferences> General> Workspace dan setel pengkodean file Teks ke UTF-8 (atau pengkodean yang Anda inginkan).

Ada beberapa detail lainnya: Cara mengubah pengkodean file teks default di Eclipse