如何将文本区域变成 java 中的输入流?
How can I turn a text area into an input stream in java?
我正在尝试对我的一本书中的一些 C++ 代码进行 Java 移植,而这本书使用了一些 OS 特定的东西来为控制台中的文本着色 window.
我决定,因为没有简单的方法让它在 java 中跨平台工作,我可以制作一个 window,其中包含一个可以模拟控制台的文本区域。打印和着色文本很简单,但我不知道如何让控制台的输入流部分正常工作。
我希望 Console.getIn() 方法返回的对象能够像 System.in 一样工作。我当前的代码可以正常工作,但如果用作扫描仪的输入将会挂起。我在下面列出了我的实现,请让我知道您是否可以告诉我我的代码有什么问题,或者是否有更好的方法来执行此操作。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
/**
*
* @author William Matrix Peckham
*/
public class Console extends JTextPane {
DocOutputStream out;
PrintStream pout;
DocInputStream in;
JFrame frame;
StyledDocument doc;
public Console() {
super();
setPreferredSize(new Dimension(500, 500));
doc = this.getStyledDocument();
out = new DocOutputStream(doc,this);
pout=new PrintStream(out);
in = new DocInputStream();
this.addKeyListener(in);
setFGColor(Color.black);
setBGColor(Color.white);
frame = new JFrame("Console");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(this));
frame.pack();
frame.setVisible(true);
}
public InputStream getIn(){
return in;
}
public PrintStream getOut(){
return pout;
}
public void setFGColor(Color c){
StyleConstants.setForeground(out.cur, c);
}
public void setBGColor(Color c){
StyleConstants.setBackground(out.cur, c);
}
private static class DocOutputStream extends OutputStream {
StyledDocument doc;
MutableAttributeSet cur;
JTextPane pane;
public DocOutputStream(StyledDocument doc, JTextPane pane) {
this.doc = doc;
this.pane=pane;
cur=new SimpleAttributeSet();
}
@Override
public void write(int b) throws IOException {
try {
doc.insertString(doc.getLength(), (char)b+"", cur);
pane.setCaretPosition(doc.getLength());
} catch (BadLocationException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
}
}
private static class DocInputStream extends InputStream implements KeyListener {
ArrayBlockingQueue<Integer> queue;
public DocInputStream(){
queue=new ArrayBlockingQueue<Integer>(1024);
}
@Override
public int read() throws IOException {
Integer i=null;
try {
i = queue.take();
} catch (InterruptedException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
if(i!=null)
return i;
return -1;
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
int c = e.getKeyCode();
try {
queue.put(c);
} catch (InterruptedException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
}
}
}
编辑:请注意,在 read() 中,我尝试了 poll() 而不是 take(),这完全阻止了它的阻塞,但我认为它可能会阻止 Scanner 永远阻塞,这是真的,但它也停止了它根本无法获得任何实际输入。
也许 Message Console 可以满足您的要求。您可以使用 JTextArea 或 JTextPane 作为控制台组件。
我明白了。问题在于 Scanner 正在调用 InputStream.read(char[],int,int),它被实现为读取整个流或整个大小的缓冲区。扫描器试图填充 8000+ 字节的缓冲区,默认的 read(...) 实现仅在缓冲区已满或读取 -1 (EOF) 后才停止调用 read()。
这会导致扫描器永远阻塞,因为大多数控制台输入永远不会那么长。解决方案是覆盖缓冲读取。我需要的版本将阻塞第一个字节,如果没有更多字符,return,我认为 BufferedInputStream 已经通过在流上调用 available() 实现了这一点,所以我尝试包装我的 class在覆盖可用之后的一个中,但没有用。我的新实现使用 available() 和 EOF 作为停止条件。
现在的实现是这样的:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
/**
*
* @author William Matrix Peckham
*/
public class Console extends JTextPane {
DocOutputStream out;
PrintStream pout;
DocInputStream in;
JFrame frame;
StyledDocument doc;
public Console() {
super();
setPreferredSize(new Dimension(500, 500));
doc = this.getStyledDocument();
out = new DocOutputStream(doc,this);
pout=new PrintStream(out);
in = new DocInputStream();
this.addKeyListener(in);
setFGColor(Color.black);
setBGColor(Color.white);
frame = new JFrame("Console");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(this));
frame.pack();
frame.setVisible(true);
}
public InputStream getIn(){
return in;
}
public PrintStream getOut(){
return pout;
}
public void setFGColor(Color c){
StyleConstants.setForeground(out.cur, c);
}
public void setBGColor(Color c){
StyleConstants.setBackground(out.cur, c);
}
private static class DocOutputStream extends OutputStream {
StyledDocument doc;
MutableAttributeSet cur;
JTextPane pane;
public DocOutputStream(StyledDocument doc, JTextPane pane) {
this.doc = doc;
this.pane=pane;
cur=new SimpleAttributeSet();
}
@Override
public void write(int b) throws IOException {
try {
doc.insertString(doc.getLength(), (char)b+"", cur);
pane.setCaretPosition(doc.getLength());
} catch (BadLocationException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
}
}
private static class DocInputStream extends InputStream implements KeyListener {
ArrayBlockingQueue<Integer> queue;
public DocInputStream(){
queue=new ArrayBlockingQueue<Integer>(1024);
}
@Override
public int read() throws IOException {
Integer i=null;
try {
i = queue.take();
} catch (InterruptedException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
if(i!=null)
return i;
return -1;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
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 ee) {
}
return i;
}
@Override
public int available(){
return queue.size();
}
@Override
public void keyTyped(KeyEvent e) {
int c = e.getKeyChar();
try {
queue.put(c);
} catch (InterruptedException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
}
}
我正在尝试对我的一本书中的一些 C++ 代码进行 Java 移植,而这本书使用了一些 OS 特定的东西来为控制台中的文本着色 window.
我决定,因为没有简单的方法让它在 java 中跨平台工作,我可以制作一个 window,其中包含一个可以模拟控制台的文本区域。打印和着色文本很简单,但我不知道如何让控制台的输入流部分正常工作。
我希望 Console.getIn() 方法返回的对象能够像 System.in 一样工作。我当前的代码可以正常工作,但如果用作扫描仪的输入将会挂起。我在下面列出了我的实现,请让我知道您是否可以告诉我我的代码有什么问题,或者是否有更好的方法来执行此操作。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
/**
*
* @author William Matrix Peckham
*/
public class Console extends JTextPane {
DocOutputStream out;
PrintStream pout;
DocInputStream in;
JFrame frame;
StyledDocument doc;
public Console() {
super();
setPreferredSize(new Dimension(500, 500));
doc = this.getStyledDocument();
out = new DocOutputStream(doc,this);
pout=new PrintStream(out);
in = new DocInputStream();
this.addKeyListener(in);
setFGColor(Color.black);
setBGColor(Color.white);
frame = new JFrame("Console");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(this));
frame.pack();
frame.setVisible(true);
}
public InputStream getIn(){
return in;
}
public PrintStream getOut(){
return pout;
}
public void setFGColor(Color c){
StyleConstants.setForeground(out.cur, c);
}
public void setBGColor(Color c){
StyleConstants.setBackground(out.cur, c);
}
private static class DocOutputStream extends OutputStream {
StyledDocument doc;
MutableAttributeSet cur;
JTextPane pane;
public DocOutputStream(StyledDocument doc, JTextPane pane) {
this.doc = doc;
this.pane=pane;
cur=new SimpleAttributeSet();
}
@Override
public void write(int b) throws IOException {
try {
doc.insertString(doc.getLength(), (char)b+"", cur);
pane.setCaretPosition(doc.getLength());
} catch (BadLocationException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
}
}
private static class DocInputStream extends InputStream implements KeyListener {
ArrayBlockingQueue<Integer> queue;
public DocInputStream(){
queue=new ArrayBlockingQueue<Integer>(1024);
}
@Override
public int read() throws IOException {
Integer i=null;
try {
i = queue.take();
} catch (InterruptedException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
if(i!=null)
return i;
return -1;
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
int c = e.getKeyCode();
try {
queue.put(c);
} catch (InterruptedException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
}
}
}
编辑:请注意,在 read() 中,我尝试了 poll() 而不是 take(),这完全阻止了它的阻塞,但我认为它可能会阻止 Scanner 永远阻塞,这是真的,但它也停止了它根本无法获得任何实际输入。
也许 Message Console 可以满足您的要求。您可以使用 JTextArea 或 JTextPane 作为控制台组件。
我明白了。问题在于 Scanner 正在调用 InputStream.read(char[],int,int),它被实现为读取整个流或整个大小的缓冲区。扫描器试图填充 8000+ 字节的缓冲区,默认的 read(...) 实现仅在缓冲区已满或读取 -1 (EOF) 后才停止调用 read()。
这会导致扫描器永远阻塞,因为大多数控制台输入永远不会那么长。解决方案是覆盖缓冲读取。我需要的版本将阻塞第一个字节,如果没有更多字符,return,我认为 BufferedInputStream 已经通过在流上调用 available() 实现了这一点,所以我尝试包装我的 class在覆盖可用之后的一个中,但没有用。我的新实现使用 available() 和 EOF 作为停止条件。
现在的实现是这样的:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
/**
*
* @author William Matrix Peckham
*/
public class Console extends JTextPane {
DocOutputStream out;
PrintStream pout;
DocInputStream in;
JFrame frame;
StyledDocument doc;
public Console() {
super();
setPreferredSize(new Dimension(500, 500));
doc = this.getStyledDocument();
out = new DocOutputStream(doc,this);
pout=new PrintStream(out);
in = new DocInputStream();
this.addKeyListener(in);
setFGColor(Color.black);
setBGColor(Color.white);
frame = new JFrame("Console");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(this));
frame.pack();
frame.setVisible(true);
}
public InputStream getIn(){
return in;
}
public PrintStream getOut(){
return pout;
}
public void setFGColor(Color c){
StyleConstants.setForeground(out.cur, c);
}
public void setBGColor(Color c){
StyleConstants.setBackground(out.cur, c);
}
private static class DocOutputStream extends OutputStream {
StyledDocument doc;
MutableAttributeSet cur;
JTextPane pane;
public DocOutputStream(StyledDocument doc, JTextPane pane) {
this.doc = doc;
this.pane=pane;
cur=new SimpleAttributeSet();
}
@Override
public void write(int b) throws IOException {
try {
doc.insertString(doc.getLength(), (char)b+"", cur);
pane.setCaretPosition(doc.getLength());
} catch (BadLocationException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
}
}
private static class DocInputStream extends InputStream implements KeyListener {
ArrayBlockingQueue<Integer> queue;
public DocInputStream(){
queue=new ArrayBlockingQueue<Integer>(1024);
}
@Override
public int read() throws IOException {
Integer i=null;
try {
i = queue.take();
} catch (InterruptedException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
if(i!=null)
return i;
return -1;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
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 ee) {
}
return i;
}
@Override
public int available(){
return queue.size();
}
@Override
public void keyTyped(KeyEvent e) {
int c = e.getKeyChar();
try {
queue.put(c);
} catch (InterruptedException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
}
}