I'm building a simple terminal emulator using the pty4j library. My program has a print() method that renders the text to a canvas using the GraphicsContext.fillText() method from javafx. I connect the emulator to an instance of cmd and read ouptput from a buffered reader. Now sadly when it recieves text it also includes ANSI-escape characters (see image). However If i print the ouput to the IDE or system console it works fine.
I tried using the readLine() method from the BufferedReader and then applying a regex, but because not all input recieved from the terminal is terminated by a \n it blocks on the last line.
Thread terminalReaderThread = new Thread() {
public void run() {
try {
int c;
while (terminal.isRunning() && (c = terminal.getReader().read()) != -1) {
if(c != 0){
print(Character.toString((char)c));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
terminalReaderThread.start();
Is there an effective way to filter these escape codes from the inputStream?
The answers I received to my question (Safely ignoring unknown ANSI, ESC/P, ESC/POS sequences, know the length) should also answer yours.
If you read the standard (ECMA-48 https://www.ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf) you will see that the sequences always start from the ESCAPE character and always end with a FINAL BYTE which has a value from a defined range.
With this information it is enough to detect start and end of each sequence. (regex for example) (also the newline character (and other C0 codes) is not allowed inside the escape sequence so you will never have an escape sequence not entirely located inside one line)
Related
I'm printing a single string from a server which contains multiple '\n' in it and would like to clear the screen before each new message. However, the following code causes the screen to be cleared after each line in the single string.
while (true) {
String s = server.readLine();
if (s == null) {
throw new NullPointerException();
}
ConsoleCleaner.clean();
System.out.println(s.toString());
}
Again, s is one single string with multiple '\n' inside which leads to one line being printed and the screen cleared each time.
I'm assuming that server is a BufferedReader here, since you haven't specified otherwise. And for the purpose of BufferedReader.readLine(), there's no such thing as "a single string with multiple \n". When the method encounters the first \n, that's the output of readLine().
You could avoid this issue by keeping track of the non-whitespace length of the last message printed, and only clearing the screen when it's non-zero.
Could you adapt this example to reach your requirement?
Read all lines with BufferedReader
Perhaps like this:
String s;
while ((line = server.readLine()) != null) {
s += line + (System.getProperty("line.separator"));
}
ConsoleCleaner.clean();
System.out.println(s.toString());
Not tried this...
I'm trying to create a small program that allows you to search for a word in a text file, and then the program should print out the whole line the text is in.
Example:
test.txt
don't mind this text
don't mind this either
and then when you let the program search for the word "text", it should print out "Don't mind this text".
What's the best way to do this?
This is what I have;
public boolean findFileInCache(){
try (BufferedReader br = new BufferedReader(new FileReader("direct.txt")))
{
while ((name = br.readLine()) != null)
{
Process p = Runtime.getRuntime().exec(name);
}
}
catch (IOException e1) { }
return true;
}
Use BufferedReader to read the file line by line using the BufferedReader.readLine() method.
For each line, check if the word is in it using a regular expression, or by splitting the line into a String[] (using String.split()), and iterating the entries in the resulting array to check if any of them is the desired word. If the desired word is there - print the entire line.
If you chose the 2nd suggestion, don't forget to check equality of two strings by using equals() and NOT by using ==
There are a couple of things you need to do:
Learn the basics by going through an introductory Java book, or course notes, from the beginning, making sure you understand each step as it comes.
Read the Javadocs of likely classes, to find methods that could be useful for the task.
You have already found two core pieces of the solution:
You are getting a line at a time using BufferedReader.readLine()
You are doing it in a while loop, so you handle one line at a time
Now, you need to work out how to deal with each line. Although you didn't include the type, name is a String. It would be better as:
while ((String name = br.readLine()) != null) {
... do something with `line`
}
If your code compiled without String there, it means you declared name as a global. Don't do that, until you know what you're doing.
Breaking things into methods is good; so let's make "do something with line" use a method now:
while ((String name = br.readLine()) != null) {
if(matches(line,"text")) {
System.out.println(line);
}
}
Now you need to write matches():
private boolean matches(String line, String word) {
boolean result = // work out whether it's a match
return result;
}
So, how do you write the guts of matches()?
Well, start by looking at the methods available in String. It has methods like contains() and split(). Some of those methods return other types, like arrays. Your teaching material and reference materials tell you how to look in arrays. The answers are there.
How can I know at run time if the file in a specified folder is text rendering or not? (i.e files like csv, html, etc that can be displayed as text)
I do not want to do this via extension matching (by checking for .txt, .html extensions etc).
Suppose if there is a jpg file, I deliberately rename the extension to .txt and still the java code should be able to detect that this file (although with .txt extn) cannot be rendered as text.
How can I achieve this in java?
You could guess the type by scanning the file and usinng Character.html#isISOControl to check whether there are non printable character included.
Binary files usually include headers which often contain control chars see this list of File Signatures most of them would be detected by isISOControl.
Implement a heuristic matcher which scans files for known signatures.
One classic example is the file command: http://en.wikipedia.org/wiki/File_(command) and the libmagic library.
There are several variants in Java, such as Tika: http://tika.apache.org/
I don't think there is a 100% foolproof way to do this, since it's a matter of opinion what counts as "can be displayed as text" ... but if you're okay with restricting it to English text, you could examine the bytes of the file, and if most or all of the byte values are in the range of 32 through 126 (decimal unsigned), then it is likely vanilla ASCII text.
This is going call for some kind of statistical pattern matching. You could, for example, if you were working with English only, check how many "foreign" characters appear in the first 100 characters. That should give you a pretty good idea of whether this is a text document or not. If you run into too many characters that are not a..zA..Z0..9[punctutation], then you can guess it is not text. Working with English-language files, and languages that can be expressed using mostly the ASCII list of characters, you should be relatively safe.
This of course goes out the window the moment you start working with foreign languages where some of the characters might appear to be special characters, but only to someone who does not speak the language.
The other alternative is to use file markers (like in Java a class file starts with a specific header) and compare the values in the file to a library of headers. It's cumbersome and error-prone as well, as you might not have the file on record and could therefore think it's a text file when it is not.
The use of a Character#isISOControl is a good thing. You should take the encoding in consideration too (p.ex.UTF-8). Here my function:
/**
* Test is a file is a text file. It is the case only if it has no well-known control characters.
* (see {#link Character#isISOControl(int)})
* #param file
* #return
* #throws IOException
*/
public static boolean isTextFile (final File file) throws IOException
{
BufferedInputStream is = null;
try
{
final BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-16"));
boolean isText;
int read;
do
{
read = in.read();
isText = read == -1;
isText |= read == 13; // newline
isText |= read == 10; // newline
isText |= read == 9; // tab
isText |= !Character.isISOControl(read);
}
while (isText && read != -1);
return isText;
}
finally {
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
throw new Error(e);
}
}
}
}
You can maintain a list of acceptable Mime Types and then get Mime Type of file you are reading. If it matches good to go.
import javax.activation.MimetypesFileTypeMap;
import java.io.File;
class GetMimeType {
public static void main(String args[]) {
File f = new File("gumby.gif");
System.out.println("Mime Type of " + f.getName() + " is " +
new MimetypesFileTypeMap().getContentType(f));
// expected output :
// "Mime Type of gumby.gif is image/gif"
}
}
http://www.rgagnon.com/javadetails/java-0487.html
Sorry if this sounds too simple. I'm very new to Java.
Here is some simple code I was using to examine hasNextLine(). When I run it, I can't make it stop. I thought if you didn't write any input and pressed Enter, you would escape the while loop.
Can someone explain to me how hasNextLine() works in this situation?
import java.util.*;
public class StringRaw {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()) {
String str = sc.nextLine();
}
System.out.print("YOU'VE GOT THROUGH");
}
}
When reading from System.in, you are reading from the keyboard, by default, and that is an infinite input stream... it has as many lines as the user cares to type. I think sending the control sequence for EOF might work, such as CTL-Z (or is it CTL-D?).
Looking at my good-ol' ASCII chart... CTL-C is an ETX and CTL-D is an EOT; either of those should work to terminate a text stream. CTL-Z is a SUB which should not work (but it might, since controls are historically interpreted highly subjectively).
CTRL-D is the end of character or byte stream for UNIX/Linux and CTRL-Z is the end of character or byte stream for Windows (a historical artifact from the earliest days of Microsoft DOS).
With the question code as written, an empty line won't exit the loop because hasNextLine() won't evaluate to false. It will have a line terminator in the input byte stream.
System.in is a byte stream from standard input, normally the console. Ending the byte stream will therefore stop the loop. Although nextLine() doesn't block waiting for input, hasNextLine() does. The only way the code terminates, as designed, is with CTRL-Z in Windows or CTRL-D in UNIX/Linux, which ends the byte stream, causes hasNextLine() not to block waiting for input and to return a boolean false which terminates the while loop.
If you want it to terminate with an empty line input you can check for non-empty lines as part of the loop continuation condition. The following code demonstrates how to change the basic question design that uses hasNextLine() and nextLine() to one that terminates if it gets an empty line or an end of input character (i.e. CTRL-Z in Windows or CTRL-D in UNIX/Linux). The additional code in the while condition uses a feature of assignment operators wherein they can be evaluated like an expression to return the value that was assigned. Since it is a String object, the String.equals() method can be used with the evaluation.
Other additional code just adds some printed output to make what is going on obvious.
// HasNextLineEndDemo.java
import java.util.*;
public class HasNextLineEndDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// this code is a bit gee-whiz
// the assignment expression gets assigned sc.nextLine()
// only if there is one because of the &&
// if hasNextLine() is false, everything after the &&
// gets ignored
// in addition, the assignment operator itself, if
// executed, returns, just like a method return,
// whatever was assigned to str which,
// as a String object, can be tested to see if it is empty
// using the String.equals() method
int i = 1; // input line counter
String str = " "; // have to seed this to other than ""
System.out.printf("Input line %d: ", i); // prompt user
while (sc.hasNextLine() && !(str = sc.nextLine()).equals("")) {
System.out.printf("Line %d: ", i);
System.out.println("'" + str + "'");
System.out.printf("Input line %d: ", ++i);
} // end while
System.out.println("\nYOU'VE GOT THROUGH");
} // end main
} // end class HasNextLineEndDemo
Hit Ctrl + D to terminate input from stdin. (Windows: Ctrl + Z) or provide input from a command:
echo -e "abc\ndef" | java Program
I had a similar problem with a socket input stream. Most solutions I found would still block the execution. It turns out there is a not-blocking check you can do with InputStream.available().
So in this case the following should work:
int x = System.in.available();
if (x!=0) {
//Your code
}
As per my understanding , if you take an example of result set object from JDBC or any iterator then in these cases you have a finite set of things and the iterators each time check whether end of the set has been reached.
However in the above case , their is no way of knowing the end of user input i.e. hasNextLine() has no way of knowing when user wants to terminate, and hence it goes on infinitely.
Best way is to put additional condition on the for loop that checks for some condition inside for loop that fails in the future.
In the above post #Jim 's answer illustrates this.
In fact using hasNextLine() as loop terminator for console input should be discouraged because it will never return false.
I want to prompt the user to begin entering characters and I want them to be able to enter characters as long as they want until they hit control+d to exit.
For example, they can type a string of numbers like:
1234567
and as soon as they decide to hit control+d the line that they entered will be displayed (so without having to hit return)
I am thinking I'll need a buffered reader or something. Any suggestions?
What rlibby said is spot on: the CTL-D will cause the terminal to flush buffered input to the JVM. However, the keypress event itself is captured and acted on by the terminal and not passed through.
Fortunately, though, it's easy to detect. If the user hits CTL-D on a line of its own, there is no input to flush...which to the JVM is indistinguishable from EOF. Accordingly, System.in.read() will return -1 per the contract of InputStream. If you've wrapped System.in with a BufferedReader, readLine() will return null.
This is my main loop for an interactive command line tool I just wrote:
BufferedReader systemIn = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
String line;
while((line = systemIn.readLine()) != null) {
// my program loop.
}
One thing worth pointing out is that if the user hits CTL-D after inputting characters (but before hitting return), you'll get those characters. I don't believe there's a way to detect CTL-D when it's not on a line of its own.
DISCLAIMER: I have no idea how this applies to Windows.
http://download.oracle.com/javase/6/docs/api/java/io/BufferedInputStream.html#read%28%29
public class InputTest {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
StringBuilder out = new StringBuilder();
while (true) {
try {
int c = in.read();
if (c != 4) // ASCII 4 04 EOT (end of transmission) ctrl D, I may be wrong here
out.append (c);
else
break;
} catch (IOException e) {
System.err.println ("Error reading input");
}
}
System.out.println(out.toString());
}
}
You are confusing the roles of your Java program and of the terminal. The terminal buffers input and occasionally supplies it to the Java program, especially after line feeds. Ctrl+D also causes the terminal to make buffered input available, or, if nothing is buffered, to terminate the input. Asking for a program to do something on Ctrl+D is essentially asking it to do something when it reads all available input (and more may become available later). Buffering input on the Java side is going to make things more complicated, rather than less.
Just adding another option for people like me who turn up 9 or so years after the question was asked. If you are using java.util.Scanner, using a String input = in.next(); will drop a NoSuchElementException when Ctrl+d is used. In this case, you can use a try-catch block to catch that particular exception and end execution of your program (either by breaking or changing your iteration value to stop the loop).
import java.util.Scanner;
while (some condition) {
try {
(take your input using the in.next() function here)
...
} catch (java.util.NoSuchElementException e) {
(some condition) = false;
//OR break;
}
}
give this code a try. it actually worked for me
// press ctrl+Z(windows) and ctrl+D(mac, linux) for input termination
StringBuilder out = new StringBuilder();
String text = null;
Scanner scanner = new Scanner( System.in );
while( scanner.hasNextLine() )
{
text = new String( scanner.nextLine() );
out.append( text );
}
scanner.close();
System.out.println( out );
System.out.println( "program terminated" );
In a GUI, you can use a KeyListener.