I want to execute this java program using python
import java.util.Scanner;
public class Input {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Enter a number = ");
int n = sc.nextInt();
System.out.println("Number entered = " n);
}
}
I am taking a scanner input and do not want to take arguments using (String[] args).
I tried using
import subprocess
myInput = '1'
subprocess.run( [ 'java', 'Input.java' ], input=myInput.encode() )
for this python code, the output in terminal is
Enter a number = Number entered = 1
but the output I want is
Enter a number = 1
Number entered = 1
This question is similar to input to C executable python subprocess and Capturing INPUT and output with subprocess (Python running java) but there is no working solution. Is there an appropriate solution available now?
CodePudding user response:
problem is there is no platform independent method to do it, so if you are on linux you can set the process stdout to non-blocking and safely read from it, but on windows this is not possible, so there are 3 workarounds.
- modify the java file to print "\n" after the question and receiver answer on different line, the python process can
readline
from stdout. - know how many letters you want to read beforehand as if you are working with a socket.
- use threads
this solution implements the second option, by writing both stdin and stdout to another buffer for temporary storage.
import subprocess
import io
out_buff = io.StringIO()
with subprocess.Popen(['java', 'Input.java'],
stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,
text=True) as proc:
out_buff.write(proc.stdout.read(17))
in_val = "1\n"
out_buff.write(in_val)
proc.stdin.write(in_val)
proc.stdin.flush() # required
out_buff.write(proc.stdout.read())
print(out_buff.getvalue())
Enter a number = 1
Number entered = 1
obviously if you are expecting a certain keyword before you answer, you can keep reading letter by letter from stdout until you know you have to input something, but if you wanted to read more than you actually can then your application will block. an option to avoid blocking is to use threads and this answer shows how to do so, but it gets complicated.
Edit: to show the third option is possible using only 1 extra thread, but you have to put in delays
import subprocess
import io
import threading
import time
def read_from_stdout(buffer,handle):
try:
while True:
buffer.write(handle.read(1))
except ValueError as e: # when reading from closed file
pass
out_buff = io.StringIO()
with subprocess.Popen(['java', 'Input.java'],
stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,
text=True) as proc:
thread = threading.Thread(target=read_from_stdout,args=(out_buff,proc.stdout))
thread.start()
time.sleep(0.1) # wait for stdout to be read
in_val = "1\n"
out_buff.write(in_val)
proc.stdin.write(in_val)
proc.stdin.flush() # required
time.sleep(0.1) # wait for stdout to be read
print(out_buff.getvalue())