我有一个在控制台上工作的程序,我想为它制作一个自定义的控制台。目前的命令行界面可以用一个方法来启动,这个方法需要一个InputStream和PrintStream作为参数,我有两个文本区域(JTextArea),一个是输入,另一个是输出。
我有两个文本区域(JTextArea),其中一个用于输入,另一个用于输出。我已经扩展了InputStream和OutputStreams来为我的起始方法提供流。
public class ConsoleInputStream extends InputStream implements KeyListener {
private BlockingDeque<Integer> mBuffer = new LinkedBlockingDeque<>();
private JTextArea mJTextArea;
public ConsoleInputStream(JTextArea JTextArea) {
mJTextArea = JTextArea;
mJTextArea.addKeyListener(this);
}
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyPressed(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyChar();
char c = (char) key;
mBuffer.add(key);
}
@Override
public int read() {
try {
char c = (char) (int) mBuffer.take();
if(c == '\n')
mJTextArea.setText("");
return c;
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
@Override
public int read(byte[] b, int off, int len) {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len && available() > 0 ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException e) {
}
return i;
}
}
而输出的方法:
public class ConsoleOutputStream extends OutputStream {
private JTextArea mJTextArea;
public ConsoleOutputStream(JTextArea JTextArea) {
mJTextArea = JTextArea;
}
@Override
public void write(int b) throws IOException {
mJTextArea.append(String.valueOf((char) b));
}
}
启动程序:
CommandInterface.get().start(ui.getConsoleIn(), new PrintStream(ui.getConsoleOut()));
(ui是一个扩展了JFrame的类的实例, getConsoleIn()和getConsoleOut()返回一个ConsoleInputStream和ConsoleOutputStream的实例)
在里面我使用扫描仪来读取输入流。
public void start(InputStream inputStream, PrintStream outputStream){
Scanner scanner = new Scanner(inputStream, "UTF-8");
while (true){
String[] input = scanner.nextLine().split(" ");
if(input[0].equals("exit"))
break;
Command command = mCommands.get(input[0]);
if(command == null){
displayErrorMessage("No such command", outputStream);
continue;
}
List<String> flags = new LinkedList<>();
List<String> params = new LinkedList<>();
for(String s : Arrays.copyOfRange(input, 1, input.length)){
if(s.charAt(0) == '/')
flags.add(s.substring(1));
else
params.add(s);
}
command.execute(outputStream, flags, params);
}
}
这很好,直到我尝试使用本地字符: ś ć ó ż ź 等。
我尝试了很多不同的解决方案,但都没有成功。然后我试着自己想办法。每当我读取一个字符时,我也把它打印到标准输出(我的IDE),我知道它可以正确显示这些字符。我发现它们被正确地读取了,但是它们之间有三个字符(UTF-8 65535)(不是以常规模式而是成对地),原因我不清楚。我也试过。
Scanner scanner = new Scanner(System.in);
while (true){
ui.getConsoleOut().write(scanner.nextLine().getBytes(StandardCharsets.UTF_8));
}
用不同的字符集,但不能让它们正确显示。
如何正确显示这些(和其他UTF-8)字符?
我不知道你是否还做错了什么,但我知道你至少需要解决这个问题。
read
还有 write
方法不能与 人物,他们与 字节数. 一个字符!=一个字节。
我说的是这些。
public int read() {
try {
char c = (char) (int) mBuffer.take();
if(c == '\n')
mJTextArea.setText("");
return c;
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
public void write(int b) throws IOException {
mJTextArea.append(String.valueOf((char) b));
}
你需要把 char
变成 byte
数组,并使用扫描器能够理解的编码。然后将这些字节中的每一个都变成无符号的英特字,而不是将每个字符作为一个单一的字节来处理。
public void keyReleased(KeyEvent e) {
int key = e.getKeyChar();
char c = (char) key;
if(c == '\n')
mJTextArea.setText("");
byte[] byteArray = Character.toString(c).getBytes(StandardCharset.UTF_8);
for (byte b : byteArray) {
mBuffer.add(Byte.toUnsignedInt(b));
}
}
public int read() {
try {
byte b = (int) mBuffer.take();
return b;
} catch (InterruptedException e) {
e.printStackTrace();
}
return -1;
}
对于 write
你也不能把每个字节当作一个单独的字符。处理这个问题的一个方法是将 PrintStream
直接。见解决方案2中的 本回答 为例。
我需要做的事情。扫地僧忽略未定义的字符(有些键如ALT或CTRL没有与之相关联的字符,所以结果是char 65535 - char undefined -)
@Override
public void keyReleased(KeyEvent e) {
char c = e.getKeyChar();
if(c == '\n')
mJTextArea.setText("");
if(c == KeyEvent.CHAR_UNDEFINED)
return;
byte[] byteArray = Character.toString(c).getBytes(StandardCharsets.UTF_8);
for (byte b : byteArray) {
mBuffer.add(Byte.toUnsignedInt(b));
}
}