這幾天一直百思不得其解,從前利用Here Document來傳值給C++/Fortran程式都能夠輕易完成;昨天寫了一個小小的轉換格式的程式想搭配shell script完成任務卻出現了NoSuchElementException 的尷尬問題,怎麼嘗試都不成功。
所謂Here Document,意思就是在子process裡傳入參數。在父process還在運行的時候,預先把參數值寫好,等到運行到子process的時候,不必等待使用者提示即可傳值。舉個例子:
假設someprog是一個C++/Fortran程式,過程中會提示,輸入兩個字串兩個數值:
#!/bin/sh /usr/local/bin/someprog << END vespa.env.out vespa.ent.OUT 23.5 2.0 END
這種在script裡就先把要傳入的參數先寫好的方法,便是常被使用的Here Document。然而,今天我也寫了一個落落長的java互動式程式,卻拋出了NoSuchElementException 的例外。為了檢查到底是哪裡出問題,我寫了一個更簡單的程式來代替:
假設這個程式稱為TestString:
import java.util.*; public class TestString{ public static void main(String[] args){ System.out.println("1st enter:"); String tmpstr = new Scanner(System.in).nextLine(); System.out.println("1st enter is "+tmpstr+". Now enter 2nd :"); tmpstr = new Scanner(System.in).nextLine(); System.out.println("2nd enter is "+tmpstr); } }
編譯他並包成jar檔:
jar -cmvf menifest.mf TestString.class當然,你的menifest.mf在預設套件的情況下可能會長這樣:
Main-Class: TestString
由於java程式是byte-code而非native-code,因此需要jvm去驗證並啟動他;想要模擬C++程式直接呼叫binary這樣的方法,只需要一個小小的script來解決:首先建立一個跟java程式同檔名的文字檔,以此例即為TestString,其內容如下:
#!/bin/bash #Program TestString [ -e `which java` ] && java -jar /home/maxsolar/bin/TestString.jar exit 0;
當然別忘記把這個script加上可執行屬性,並放在你的PATH範圍裡。
執行以下的script來試試看!
#!/bin/bash TestString << END string1 string2 END
結果卻出現了下列錯誤訊息:
1st enter:
1st enter is string1. Now enter 2nd :
Exception in thread "main" java.util.NoSuchElementException: No line found
at java.util.Scanner.nextLine(Scanner.java:1516)
at TestString.main(TestString.java: 9)
對還不太能掌握java的我,實在不清楚究竟是為了麼值會傳不進去。困擾我一整天之後,決定到台灣最大的JavaWorld@TW論壇去爬文,卻沒有跟我的情況有類似的問題。後來在版上發問,沒想到很快的就有高手來解決我的問題:
Scanner 在某些操作上會有 buffering 的行為,如果要透過 Scanner 來處理 standard input 裡的數據,應只使用一個 Scanner instance。
import java.util.*;
public class TestString {
public static void main(String[] args) {
System.out.println("1st enter:");
final Scanner scanner = new Scanner(System.in);
String tmpstr = scanner.nextLine();
System.out.println("1st enter is " + tmpstr + ". Now enter 2nd :");
tmpstr = scanner.nextLine();
System.out.println("2nd enter is " + tmpstr);
}
}
如此一來,我使用的Scanner物件將會是唯一。先前我所使用的是anonymous的方法產生物件並直接傳到tmpstr這個字串物件接收,但是根據Dunkan前輩的建議,要使用一個唯一的Scanner物件來接收我的標準輸入,才能擺脫被buffered在記憶體的問題。
非常感謝Dunkan前輩精確且迅速的解答,因此把這篇作成筆記,好讓自己跟需要的Java與linux同好可以有機會參考。
沒有留言:
張貼留言