質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
標準入力

標準入力(stdin)は、プログラムが標準的に用いるデータ入力元。リダイレクトしない限り、プログラムを起動した端末のキーボードが標準入力になります。UNIX系OSやC言語に実装されて普及した概念ですが、他のOSや言語も含めた総称としても使われます。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

Q&A

解決済

3回答

2581閲覧

PowershellでJavaの標準入力を自動化したい

AkihiroKanda

総合スコア13

標準入力

標準入力(stdin)は、プログラムが標準的に用いるデータ入力元。リダイレクトしない限り、プログラムを起動した端末のキーボードが標準入力になります。UNIX系OSやC言語に実装されて普及した概念ですが、他のOSや言語も含めた総称としても使われます。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

0グッド

0クリップ

投稿2019/04/23 22:15

PowershellでJavaの標準入力を自動化したい

現在,javaの採点業務をしており,キーボード入力を自動化するPowerShellのスクリプトを作成したいと考えています.
javaのプログラムでは以下のようにキーボード入力を受け付けています.

import java.io.*; import java.util.Arrays; class kadai01{ public static void main(String[] args) throws IOException{ int[] inputNum = new int[3]; System.out.println("数字を3個入力してください。"); for(int i = 0; i < 3; i++){ System.out.print((i+1)+"個目\t: "); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); inputNum[i] = Integer.parseInt(br.readLine()); System.out.println(inputNum[i]); } } }

このJavaのプログラムに対してキーボード入力を自動化するために以下のようにPowershellのコマンドを入力したのですが,エラーがでてうまくいきません.
入力が1回の場合だとうまくいくのですが,複数回のキーボード入力に対して自動化する方法がわかりません.

> 13,13,13 | java kadai01 数字を3個入力してください。 1個目 : 13 2個目 : Exception in thread "main" java.lang.NumberFormatException: null at java.lang.Integer.parseInt(Unknown Source) at java.lang.Integer.parseInt(Unknown Source) at kadai01.main(kadai01.java:11)

試したこと

すべて上記と同じようなエラーがでた.

Get-Content input.txt | java kadai01

@(13,13,13) | java kadai01

echo 13,13,13 | java kadai01

補足情報(Powershellのバージョン)

Version : 5.1.17763.316

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/04/23 22:39

標準入力に流し込む方法が間違えてるんじゃないの?
guest

回答3

0

これで良いのか正直微妙ですが……

バッファが全部読み込まれてしまうのなら、タイムアウトされるまで待てばいいじゃない、という事で適当にウェイトを追加してみたら、私の環境では上手いこと動作しました。

ps1

1# java と クラスを指定 2[string]$java = 'java.exe' 3[string]$class = 'kadai01' 4 5# .NET の機能を直接使用する。 6[System.Diagnostics.ProcessStartInfo]$procInfo = 7 New-Object -TypeName System.Diagnostics.ProcessStartInfo -Property @{ 8 FileName = $java 9 Arguments = $class 10 WorkingDirectory = $PWD 11 12 # 標準入出力を PowerShell 側で操作する。 13 UseShellExecute = $false 14 RedirectStandardInput = $true 15 RedirectStandardOutput = $true 16 RedirectStandardError = $true 17 } 18 19[System.Diagnostics.Process]$proc = 20 [System.Diagnostics.Process]::Start($procInfo) 21 22try { 23 11, 22, 33 | 24 % { 25 # 標準入力に書き込んで 26 $proc.StandardInput.WriteLine($_) 27 # 適度にウェイトする 28 Start-Sleep -Seconds 1 29 } 30 31 32 if (-not $proc.StandardOutput.EndOfStream) { 33 $proc.StandardOutput.ReadToEnd() 34 } 35 36 if (-not $proc.StandardError.EndOfStream) { 37 Write-Error $proc.StandardError.ReadToEnd() 38 } 39} finally { 40 $proc.Dispose() 41}

投稿2019/04/26 13:02

imihito

総合スコア2166

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

AkihiroKanda

2019/04/26 17:42

私の環境でもうまくいきました. ありがとうございます!
guest

0

ベストアンサー

入力する度にBufferedReaderインスタンスを生成しているのが問題です。bufferedというのは「ベースストリームからバッファーを介して入出力を行う」ということなのですが、本件の例で言えば「要求以上のデータが自動的にバッファーへ読み込まれる」ことを意味します。

キーボードから入力する場合はENTERを打ち込んだタイミングでSystem.inからのreadメソッドが戻ってきます。つまり最初の1行分のデータのみが読み込まれ2行目以降はまだ読み込まれないわけです。

しかしリダイレクションによりSystem.inがファイルからの入力ストリームに割り当てられていると、BufferedReaderは初回のreadline時点でSystem.inからバッファーのサイズ分だけ全て読み込んでしまいます(※1)。

さて、ご質問のコードでは1行入力したらBufferedReaderインスタンスを作り直しているため、1回目のreadlineでせっかくバッファーに読み込まれた後続行の入力は失われてしまいます。既に後続行は読み込み済みなのでベースストリームのSystem.inはEOF状態となってしまっています。そのため新たにBufferedReaderのインスタンスを生成しても読み込めるものが既になくなっており、結果がnull(EOF=End Of Fileを表す)となるわけです。(※2)

BufferedReaderインスタンスを生成したらアプリケーション終了時までそのインスタンスを使い続けてください。BufferedReaderに限らず一般的に同じベースストリームから何度もストリームインスタンスを生成しなおすのは避けるべきと思います。


※1: これは一般的なI/Oテクニック(Buffered I/O)でして、OSに対するI/O要求は極端な話1バイト読もうが4KB読もうが大した違いがなくどうせなら先に一度に読み込んでしまえば早くなるというアイデアです。大雑把に言えば10行に対して数バイトずつちまちまI/O要求を出すのに比べ10行分を一度にI/O要求を出した方が大雑把に10倍速くなるのです。大抵の場合はもっと極端に差が出ます。

※2: 原因推測の過程

例外のメッセージの意味を把握できるようになるともう少し原因に早くせまることができたかも知れません。

Exception in thread "main" java.lang.NumberFormatException: null

この例外メッセージから「parseIntに対しておかしな文字列を与えたのが原因で例外が起きた。そのおかしな文字列とはnullであった」ことがわかります。それがわかれば「readline()からnullが返される時はどんな時か?それはEOFに到達したときである」と推測できます。
そこで「Buffered I/Oとはそもそもどういうものであるか」という点が把握できていればなぜEOFに達するかも理解できます。

デバッグの作業は、実行時に得られる情報(エラーメッセージやデバッグプリントなどから得られる)とメソッドの仕様などの知識を突き合わせ何が起きているかを推測する演繹過程の積み上げで進めます。

・メッセージを読み意味を把握する
・メソッドの仕様を調べそれを把握する
・ライブラリーの仕様のベースになっている一般的アイデアを知る
・仕様・起きた現象から考えられる原因を列挙し可能性を検討する

こうしたことは初心者のころはなかなかうまく進められません。やりたくても「必要な前提知識が不足しすぎていて思うように進められない」と思います。「様々な前提知識を広く知る」には時間がかかるので上記はいっぺんにできるようにはならないのですが、それでも少しずつ上記に挙げたようなことを心がけるのがよいと思います。

投稿2019/04/24 01:06

KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

AkihiroKanda

2019/04/24 06:33

丁寧に説明いただきありがとうございます. BufferedReaderの一文をfor文の外に出すことで,解決しました. ありがとうございます. もう一つ追加で質問させていただきたいのですが,私は現在Javaの採点業務を委託されており,Javaのプログラムのほうに関しては,変更することができません. ですので,powershellのスクリプトで,どうにかして標準入力を自動化させたいのですが,何か良い方法はありませんか?
KSwordOfHaste

2019/04/24 07:01

コメント欄で質問の追加はお勧めできません・・・ が、簡単にお答えするなら「できそうな気がするが少々試さないとわからない」です。「ファイルから入力しても行単位でJava側のreadが完了するように」すればよいので、そのような入力パイプをJavaへ繋げればよいのですが・・・powershell書いたことないのでほとんど試せてませんがとりあえず funcion dump() {  foreach ($line in input.txt) { echo $line } } dump | java kadai01 とやってもダメでした。もうちょっと細かな制御が可能な普通の汎用言語なら可能かも知れません。
AkihiroKanda

2019/04/26 11:35

いろいろ試していただきありがとうございます. 何とかできないか自分でも解決策をもう少しやってみます.
guest

0

まあ PowerShell で 観点で外れるけど テスト用の実行クラスを作ってしまえばいいかと

package teratail.q186093; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.util.Scanner; public class Kadai1Macro { public static void main(String[] args) throws IOException { // args[0] 起動する Java のクラスファイルを想定 // String className = args[0]; String className = "teratail.q186093.Kadai1"; // args[1] 起動する Java のクラスファイルを含むクラスパスを想定 // String classPath = args[1]; String classPath = Kadai1Macro.class.getResource("/").getFile(); // args[2] 標準入力をする回数を指定 // Integer count = Integer.parseInt(args[2]); Integer count = Integer.parseInt("3"); // args[3] 標準入力をする文字列を複数指定 Integer[] inputs = new Integer[count]; for (int i = 0; i < count; i++) { // inputs[i] = Integer.parseInt(args[3 + i]); inputs[i] = i + 3; } Runtime r = Runtime.getRuntime(); Process p = exec(r, "java", "-cp", classPath, className); try (InputStream is = p.getErrorStream(); Scanner in = new Scanner(is)) { if (is.available() > 0) { while (in.hasNextLine()) { System.out.println(in.nextLine()); } } } // 改行 try (InputStream is = p.getInputStream(); Scanner in = new Scanner(is)) { String line = in.nextLine(); System.out.println(line); try (OutputStream os = p.getOutputStream(); PrintWriter out = new PrintWriter(os);) { for (int i = 0; i < count; i++) { out.println(inputs[i]); out.flush(); line = in.nextLine(); System.out.println(line); } } } } static Process exec(Runtime r, String... args) throws IOException { return r.exec(args); } }

対象クラス

package teratail.q186093; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class Kadai1 { public static void main(String[] args) throws IOException { int[] inputNum = new int[3]; System.out.println("数字を3個入力してください。"); for (int i = 0; i < 3; i++) { System.out.print((i + 1) + "個目\t: "); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); inputNum[i] = Integer.parseInt(br.readLine()); System.out.println(inputNum[i]); } } }

投稿2019/04/27 01:57

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問