I need to dialogue with an external c++ console program (read output and write input). I read from the application with a Thread (and it works), but when it needs input, it works only the first time, then the stream probably remains empty, and it doesn't receive the second input (and external program closes).
The application i'm using is a simple .exe wrote in c++ that:
print "Insert first input"
scan input1
print input1
print "Insert second input"
scan input2
print input2
Main class:
import java.io.*;
import java.util.Scanner;
public class ExampleCom {
public static Communication com = new Communication();
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
String s;
com.read();
while(true)
{
s = in.nextLine();
com.write(s);
}
}
Communication class:
public class Communication
{
Process p;
OutputStream writer;
public InputStream reader = null;
Read r; //Class that with a loop read all exe input
Communication()
{
try{
p = Runtime.getRuntime ().exec ("C:\\esempio.exe");
writer = p.getOutputStream();
reader = p.getInputStream();
}catch(Exception e){}
}
public void read()
{
r = new Read();
Thread threadRead = new Thread(r);
threadRead.start();
}
public void write(String s)
{
try{
writer.write(s.getBytes());
writer.flush();
writer.close();
}catch(Exception e){}
}
}
How can I send my string (like "writer.write('hello')") when the external application needs it?
The problem is that in your write() method, you have the line
writer.close();
which means that after calling it the first time, you are closing the input stream to your C++. As far as it is concerned, it sees the "end of file" marker after your first input.
What you should do is put the close() in a separate method, and call that method only when you are done working with that process.
Now, as your target program expects text input and will only interpret the input if it gets an end-of-line (as per your answer to the question in my comment), you should supply that end-of-line to it.
Instead of doing raw byte-writes, I think a better approach would be to use a PrintWriter for that output stream, and use as naturally as you use System.out.println(). It can also save you on the flush() part.
You are interpreting it incorrectly when you see that your program is not reading the input until you close(). It's not waiting - it sends it as soon as you call flush(). But the C++ waits for either an end-of-file or an end-of-line, and since you are not giving it an end-of-line, then only close(), that sends it end-of-file, causes it to accept the input. But then you can no longer send any further data.
So the solution is, first, to define your writer as a PrintWriter. Instead of
OutputStream writer;
Use
PrintWriter writer;
And instead of
writer = p.getOutputStream();
Use
writer = new PrintWriter(p.getOutputStream(), true);
The true there will give you auto-flush whenever you use the println() command.
Now, your write method should be:
public void write(String s)
{
writer.println(s);
}
Note that a PrintWriter doesn't produce exceptions, so if you care about errors, you have to check for them using checkError().
And of course, have the close() in a separate method, as I mentioned before.
Because the write() method might throw an IOException, it is advisable to call the close() method inside a finally block.Place the writer.close() method outside the try clause:
finally {
if(writer != null) {
writer.close();
}
Related
A part of my application writes data to a .csv file in the following way:
public class ExampleWriter {
public static final int COUNT = 10_000;
public static final String FILE = "test.csv";
public static void main(String[] args) throws Exception {
try (OutputStream os = new FileOutputStream(FILE)){
os.write(239);
os.write(187);
os.write(191);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
for (int i = 0; i < COUNT; i++) {
writer.write(Integer.toString(i));
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(checkLineCount(COUNT, new File(FILE)));
}
public static String checkLineCount(int expectedLineCount, File file) throws Exception {
BufferedReader expectedReader = new BufferedReader(new FileReader(file));
try {
int lineCount = 0;
while (expectedReader.readLine() != null) {
lineCount++;
}
if (expectedLineCount == lineCount) {
return "correct";
} else {
return "incorrect";
}
}
finally {
expectedReader.close();
}
}
}
The file will be opened in excel and all kind of languages are present in the data. The os.write parts are for prefixing the file with a byte order mark as to enable all kinds of characters.
Somehow the amount of lines in the file do not match the count in the loop and I can not figure out how. Any help on what I am doing wrong here would be greatly appreciated.
You simply need to flush and close your output stream (forcing fsync) before opening the file for input and counting. Try adding:
writer.flush();
writer.close();
inside your try-block. after the for-loop in the main method.
(As a side note).
Note that using a BOM is optional, and (in many cases) reduces the portability of your files (because not all consuming app's are able to handle it well). It does not guarantee that the file has the advertised character encoding. So i would recommend to remove the BOM. When using Excel, just select the file and and choose UTF-8 as encoding.
You are not flushing the stream,Refer oracle docs for more info
which says that
Flushes this output stream and forces any buffered output bytes to be
written out. The general contract of flush is that calling it is an
indication that, if any bytes previously written have been buffered by
the implementation of the output stream, such bytes should immediately
be written to their intended destination. If the intended destination
of this stream is an abstraction provided by the underlying operating
system, for example a file, then flushing the stream guarantees only
that bytes previously written to the stream are passed to the
operating system for writing; it does not guarantee that they are
actually written to a physical device such as a disk drive.
The flush method of OutputStream does nothing.
You need to flush as well as close the stream. There are 2 ways
manually call close() and flush().
use try with resource
As I can see from your code that you have already implemented try with resource and also BufferedReader class also implements Closeable, Flushable so use code as per below
public static void main(String[] args) throws Exception {
try (OutputStream os = new FileOutputStream(FILE); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))){
os.write(239);
os.write(187);
os.write(191);
for (int i = 0; i < COUNT; i++) {
writer.write(Integer.toString(i));
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(checkLineCount(COUNT, new File(FILE)));
}
When COUNT is 1, the code in main() will write a file with two lines, a line with data plus an empty line afterwards. Then you call checkLineCount(COUNT, file) expecting that it will return 1 but it returns 2 because the file has actually two lines.
Therefore if you want the counter to match you must not write a new line after the last line.
(As another side note).
Notice that writing CSV-files the way you are doing is really bad practice. CSV is not so easy as it may look at first sight! So, unless you really know what you are doing (so being aware of all CSV quirks), use a library!
I'm trying to write a program that gets a users input that is then written to an output file called userStrings.txt. I'm also trying to stop the processing once the user inputs 'done', but I'm not sure how to accomplish this.
Here is my code:
import java.io.*;
import java.util.Scanner;
public class Murray_A04Q2 {
public static void main(String[] args) throws IOException {
// Name of the file
String fileName = "userStrings.txt";
Scanner scan = new Scanner(System.in);
try {
// FileReader reading the text files in the default encoding.
FileWriter fileWriter = new FileWriter("userStrings.txt");
// Wrapping FileReader in BufferedReader.
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write("A string");
bufferedWriter.write("Another string");
bufferedWriter.write("Yet more text...");
System.out.println("Enter something, DONE to quit: ");
String input = scan.nextLine();
// Closing file
bufferedWriter.close();
}
catch (IOException ex){
System.out.println("Error writing to file " + "userStrings.txt" + "");
}
} // End of method header
} // End of class header
In order to write to a file, do I still use System.out.println? and is the bufferedWriter.write even necessary? I'm just trying to understand the I/O and writing to files better.
Thanks!
In order to write to a file, do I still use System.out.println?
No. That writes to standard output, not to your file.
If you are using println then you need to wrap your BufferedWriter with a PrintWriter. (Look at the javadocs for the System class where the out field is documented.)
and is the bufferedWriter.write even necessary?
If you are going to write directly to the BufferedWriter then yes, it is necessary, though you probably need to an appropriate "end of line" sequence. And that's where it gets a bit messy because different platforms have different native "end of line" sequences. (If you use PrintWriter, the println method picks the right one to use for the execution platform.)
I'm also trying to stop the processing once the user inputs 'done', but I'm not sure how to accomplish this.
Hint: read about the Scanner class and System.in.
Right under you take input from the console, run a while loop to test that input is not equal to "done". Inside the while loop, add input to your file and get the next line of input.
while(!input.toLowerCase().Equals("done"))
{
bufferedWriter.write(input);
input = scan.nextLine();
}
This is my method to write to a file, this method belongs to a class
public void write(FileOutputStream fo) throws IOException
{
PrintWriter out=new PrintWriter(fo);
for (int i = 0; i < size; i++) {
String formatted="%3d";
for (int j=0;j<size;j++)
{
out.append(String.format(formatted,arr[i][j]));
}
out.append(System.getProperty("line.separator"));
}
out.append(System.getProperty("line.separator"));
out.close();
}
I want to write my results into many different files, corresponding to input files read by appropriate created object, so I pass the to-be-written file as a parameter to this method
It doesn't append at all
After spending hours searching for solution of this problem, I only got results of appending using PrintWriter with a specified path of file, for example:
PrintWriter out=new PrintWriter(new FileWriter("output1.txt",true));
If I use
PrintWriter out=new PrintWriter(new FileWriter(fo,true));
I encounter error
It doesn't append when write method of my 2 instances of that object is called
public void writeSolution() throws IOException
{
fo=new FileOutputStream("output\\output1.txt",true);
start.write(fo);
goal.write(fo);
fo.close();
}
Please help me solve this problem
When you close a PrintWriter, the close() method also closes the underlying stream/writer.
In every call to your write method, you open your PrintWriter and then you close it at the end. This means the stream is closed. Next time you open a PrintWriter to it, it is a closed file. You can't write to a closed file.
A PrintWriter does not throw exceptions when you do something like this, so it silently does nothing. And you are not calling its checkError() method, so you don't know that there was an error there.
What you should do is declare the PrintWriter as a field (so that it maintains its state between calls to write(), open it before you start calling the write() method, and close it after you finish calling the write() method for the last time. And remember to check errors.
I'm writing a program that executes another Java program by using the Class and Method classes to invoke the main method. This other program then tries to read from System.in. In order to pass arguments to the program, I set System.in to a PipedInputStream that is connected to a PipedOutputStream. I pass the arguments the other program requests to the PipedOutputStream, then invoke the main method.
However, as soon as the method is invoked, the program deadlocks. Why is that? Theoretically, the other program should have access to the arguments, since they're already available in the PipedInputStream.
I can't change the way the other program reads the input, so this solution wouldn't work.
Here some example code:
The part where I assign the PipedStreams
PipedInputStream inputStream = new PipedInputStream();
PipedStringOutputStream stringStream = new PipedStringOutputStream(); // custom class
try {
stringStream.connect(inputStream);
} catch (IOException e2) {
e2.printStackTrace();
}
System.setIn(inputStream);
The part where I invoke the method:
Class main = classes.get(className);
try {
Method m = main.getMethod("main", String[].class);
// write all parameters to System.in
String[] params = getParams(); // custom method, works (params is not empty)
for(int j = 0; j < params.length; j++) {
stringStream.write(params[j]);
}
params = null;
m.invoke(null, (Object) params); // this is were the program stops
} catch(Exception e) {}
The PipedStringOutputStream class:
public class PipedStringOutputStream extends PipedOutputStream {
public void write(String output) throws IOException {
this.write((output + "\n").getBytes());
flush();
}
}
My test program that reads from System.in:
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
System.out.println(sc.nextLine());
}
}
So what is the problem? Do I have to start the Streams in Threads? Why doesn't the other program read the input from the PipedInputStream?
The javadoc of PipedInputStream explicitely says:
Typically, data is read from a PipedInputStream object by one thread and data is written to the corresponding PipedOutputStream by some other thread. Attempting to use both objects from a single thread is not recommended, as it may deadlock the thread.
(emphasis mine)
Write your input to a byte array using a ByteArrayOutputStream. Then construct a ByteArrayInputStream from this byte array, and set System.in to this ByteArrayInputStream.
I have a function "a()" that calls another function "b()" that writes to stdout. I cannot modify "b()", but I want to be able to read what "b" is writing and write back to stdout for "b" to read, meaning:
public void a() {
// start a thread that listens to stdout.
// the thread should print a name to stdout after "b" print "Please enter your name"
b();
}
public void b() { // I cannot modify this function
System.out.println("Welcome! The time is " + System.currentTimeMillis());
System.out.println("Please enter your name");
String name = ...
// ... b reads here the name that the thread from function a() will write
// ...
System.out.println("This is the name that was entered: " + name);
}
I thought about starting "b" in a new process but I wasn't sure how unless I wrap "b" in a main function and run it using a command line - I'd be happy for suggestions.
If it's not a process, I'm not sure how to implement the thread that will be activated by "a()".
I tried using:
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = stdin.readLine()) != null) {
...
}
but it doesn't catch what "b" is writing.
Thanks for the help
You can run b() in another process but you don't need to do so.
System.out is a PrintStream. If you read the javadoc carefully you will notice System.setOut method. With it you can replace System.out with another PrintStream.
Example (not tested):
PrintStream originalOut = System.out; // To get it back later
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream newOut = new PrintStream(baos);
System.setOut(newOut);
b();
System.out.flush();
System.setOut(originalOut); // So you can print again
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
// Now you can read from bais what b() wrote to System.out
This solution has the problem of not being thread safe. If any other thread write to System.out when it is 'changed' the output will get redirected too. To get rid of this problem I think you need to run b() on another JVM or use a PrintStream that split (deMux) the output depending on the thread or context.
Unfortunately there is not easy way of doing this in Java. The biggest problem is that System.out and System.in are two separate files, created from FileDescriptor.out and FileDescriptor.in respectively. They are not connected in any way, and therefore you can't write to System.out and expect to see it in System.in.
Your options are:
Run b() in external process somehow. Yes, you'll need to put it in a class with main() function and do lots of complicated process setup, like getting the path to java.exe and setting up classpaths etc. The good part is that writing to and reading from the process will work as you expect.
Create two custom Input and Output streams that can duplicate all traffic to another in/out stream, as well as sending it to System.{in,out}, and set them using System.set{In,Out}. This way you can monitor those streams without affecting other code that might by using System.{in,out}.
As an example of the custom OutputStream mentioned in 2, try something like this:
class CopyOutputStream extends OutputStream {
private final OutputStream str1;
private final OutputStream str2;
public CopyOutputStream(OutputStream str1, OutputStream str2) {
this.str1 = str1;
this.str2 = str2;
}
#Override
public void write(int b) throws IOException {
str1.write(b);
str2.write(b);
}
// ...
#Override
public void close() throws IOException {
try {
str1.close();
} finally {
str2.close();
}
}
}
// then in a() do
public void a(){
//Create a pipe to capture data written to System.out
final PipedInputStream pipeIn = new PipedInputStream();
final PipedOutputStream pipeOut = new PipedOutputStream(pipeIn);
OutputStream out = new CopyOutputStream(System.out, pipeOut);
//From now on everything written to System.out will be sent to
// System.out first and then copied to pipeOut and will be available
// to read from pipeIn.
System.setOut(new PrintStream(out));
// In another thread: read data from System.out
BufferedReader reader = new BufferedReader(new InputStreamReader(pipeIn));
String name = reader.readLine();
}
Unfortunately you will have to repeat the above process for System.in, which means more crazy code, but I don't think it will get easier than this.
If you are ready for some really crazy action, maybe you can get hold of some java library (most likely with native code), that can give you the Process object of the currently running JVM, and then use get{Input,Output}Stream() methods to do the job.
How about setting System.out with System.setOut