Kodowanie UTF-8 dla danych wyjściowych z konsoli do JavaFX TextArea
Chcę przekierować dane wyjściowe w konsoli do JavaFX TextArea i postępuję zgodnie z sugestią tutaj: JavaFX: Przekieruj dane wyjściowe konsoli do TextArea, który jest tworzony w SceneBuilder
Próbowałem ustawić kodowanie na UTF-8 w PrintStream (), ale nie wygląda to tak dobrze . Ustawienie zestawu znaków na UTF-16 nieco go poprawia, ale nadal jest nieczytelne .
W Eclipse IDE, przypuszczalny tekst wyjściowy w Console wygląda dobrze:
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>
Wciąż jestem nowy w Javie, więc nie wiem, jak dokładnie działa PrintStream lub OutputStream. Proszę wybaczyć moją ignorancję.
Każda sugestia jest mile widziana.
Odpowiedzi
Uważam, że przyczyną problemu jest ten kod:
public void write(int b) throws IOException {
appendText(String.valueOf((char) b));
}
To jest konwersja każdego pojedynczego bajtu na znak. Innymi słowy, zakłada, że każdy znak jest reprezentowany przez jeden bajt. To niekoniecznie prawda. Niektóre kodowania, takie jak UTF-8 , mogą używać wielu bajtów do reprezentowania pojedynczego znaku. Muszą, jeśli chcą mieć możliwość reprezentowania więcej niż 256 znaków.
Musisz poprawnie zdekodować przychodzące bajty. Zamiast próbować to zrobić samemu, lepiej byłoby znaleźć sposób na użycie czegoś takiego BufferedReader
. Na szczęście jest to możliwe dzięki PipedInputStream
i PipedOutputStream
. Na przykład:
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;
}
}
}
}
Użycie buffer
powyższego jest próbą uniknięcia zalewania wątku aplikacji JavaFX zadaniami.
Kilka innych rzeczy, które musisz wziąć pod uwagę:
- Ponieważ używasz literału ciągu, upewnij się, że zarówno zapisujesz plik źródłowy w UTF-8, jak i kompilujesz kod z
-encoding UTF-8
. - Upewnij się, że czcionka, której używasz z,
TextArea
może reprezentować wszystkie znaki, które chcesz. - Jest to możliwe, trzeba także uruchomić aplikację z
-Dfile.encoding=UTF-8
, ale nie jestem pewien. Nie zrobiłem i nadal mi to działało.
Spróbuj ustawić domyślne kodowanie JVM na UTF-8.
java -Dfile.encoding=UTF-8 -jar YourJarfile.jar
Aby uzyskać więcej informacji, zajrzyj do tego wątku: Ustawianie domyślnego kodowania znaków w języku Java
Jeśli nie chcesz eksportować pliku, przejdź do Preferencji Eclipse > Ogólne> Przestrzeń robocza i ustaw kodowanie pliku tekstowego na UTF-8 (lub kodowanie, które chcesz mieć).
Jest jeszcze kilka szczegółów: Jak zmienić domyślne kodowanie pliku tekstowego w Eclipse