Codificação UTF-8 para saída do Console para JavaFX TextArea
Desejo redirecionar a saída do Console para JavaFX TextArea e sigo uma sugestão aqui: JavaFX: Redirecionar a saída do console para TextArea criada no SceneBuilder
Tentei definir o conjunto de caracteres como UTF-8 em PrintStream (), mas não parece muito bem . Definir o conjunto de caracteres como UTF-16 melhora um pouco, mas ainda é ilegível .
No Eclipse IDE, a suposta saída de texto no Console resulta bem:
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>
Ainda sou novo em Java, então não estou familiarizado como exatamente o PrintStream ou o OutputStream funcionam. Por favor, desculpe minha ignorância.
Cada sugestão é apreciada.
Respostas
Acredito que seu problema seja causado por este código:
public void write(int b) throws IOException {
appendText(String.valueOf((char) b));
}
Isso converte cada byte individual em um caractere. Em outras palavras, presume-se que cada caractere seja representado por um único byte. Isso não é necessariamente verdade. Algumas codificações, como UTF-8 , podem usar vários bytes para representar um único caractere. Eles têm que fazê-lo se quiserem representar mais de 256 caracteres.
Você precisará decodificar corretamente os bytes de entrada. Em vez de tentar fazer isso sozinho, seria melhor encontrar uma maneira de usar algo como BufferedReader
. Felizmente, isso é possível com PipedInputStream
e PipedOutputStream
. Por exemplo:
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;
}
}
}
}
O uso do buffer
acima é uma tentativa de evitar sobrecarregar o JavaFX Application Thread com tarefas.
Algumas outras coisas que você precisa levar em consideração:
- Como você está usando uma string literal, certifique-se de salvar o arquivo de origem com UTF-8 e compilar o código com
-encoding UTF-8
. - Certifique-se de que a fonte usada com o
TextArea
pode representar todos os caracteres que você deseja. - É possível que você também precise executar o aplicativo,
-Dfile.encoding=UTF-8
mas não tenho certeza. Eu não fiz e ainda funcionou para mim.
Tente definir sua codificação JVM padrão para UTF-8.
java -Dfile.encoding=UTF-8 -jar YourJarfile.jar
Para obter mais detalhes, consulte este tópico: Configurando a codificação de caracteres Java padrão
Se você não quiser exportar seu arquivo, vá para Preferências do Eclipse > Geral> Espaço de trabalho e defina a codificação do arquivo de texto para UTF-8 (ou a codificação que você gostaria de ter).
Existem mais alguns detalhes: Como alterar a codificação do arquivo de texto padrão no Eclipse