Java Scanner的hasNext()方法


Java Scanner的hasNext()方法

在編程筆試(或者某些場景)中,可能存在這樣的需求:程序被要求接收不確定數量的一些字符串或者是數字,然后對接收的數據進行相關的處理。

假設這樣一個場景,程序被要求接收不定數量的一些整數,然后計算這些數字的和。來看下面這個程序:

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int sum = 0;
        while(s.hasNextInt()) {
            sum += s.nextInt();
        }
        System.out.println(sum);
    }
}

我們的預期是:程序開始運行后,我們通過鍵盤在終端鍵入一行不定數量的整數,按下回車,程序緊接着打印出整數之和,然后結束運行。看上去似乎條例清晰,邏輯嚴密。不過程序實際運行的情況與我們的預期出現了一點偏差:在我們輸入數據並按下回車之后,光標移動到了下一行,不過程序並沒有打印出我們期待的結果,也沒有結束運行,它“停下”了,或者說,它似乎在等待用戶繼續輸入。如果此時你繼續輸入一行整數,然后按下回車,光標移動到下一行,情況不會發生變化。甚至你不輸入任何內容,直接按下回車,情況仍然不會發生變化。但是如果你輸入了一個浮點數或者是一個字符串,程序會緊接着打印出你想要的結果,然后結束運行。這很好理解,因為你輸入的不是整數,循環會因此結束。

再來看這樣一個場景,程序被要求接收不確定數量的一些字符串,然后將其逆序輸出。來看下面這個程序:

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        String str = "";
        while(s.hasNext()) {
            str = s.next() + " " + str;
        }
        System.out.println(str);
    }
}

我們的預期是:程序開始運行后,我們通過鍵盤在終端鍵入一行不定數量的字符串,按下回車,程序緊接着逆序打印出字符串,然后結束運行。然而這個程序實際運行的情況更加糟糕,似乎不論我們輸入任何類型、任何數量的內容,程序都不會從循環中跳出,就好像沒有什么條件能夠使s.hasNext()的結果為false

這種情況令人感到沮喪,看來我們必須調查一下到底哪里出現了問題。顯而易見,問題的出現肯定和Scanner類的hasNext()方法有關,那就讓我們首先來看一下 jdk 的開發者對這個方法的描述:

Returns true if this scanner has another token in its input.
This method may block while waiting for input to scan.
The scanner does not advance past any input.

重點看這一句:**This method may block while waiting for input to scan. **大意是此方法可能會在等待輸入時阻塞。這樣一解釋就好辦了,我們在循環體里面打印一下 hasNext() 方法的返回值看看:

public static void main(String[] args) {
    Scanner s = new Scanner(System.in);
    boolean b;
    while(b = s.hasNext()) {
        System.out.println(b);
        System.out.println("your input is " + s.next());
    }
    System.out.println(b);
}

程序執行的結果如下:

i am iron man // 這句是輸入,下面的都是輸出
true
your input is i
true
your input is am
true
your input is iron
true
your input is man
// 輸出換行到這兒以后程序並未從循環中跳出

可以看到,程序將輸入的最后一個單詞man打印出以后,既沒有跳出循環,打印出false;也沒有進入下一次循環,打印出trues.hasNext()在這兒發生了阻塞,程序在等待用戶輸入,而用戶不想再繼續輸入了。一方面,程序並不把空格、制表符以及回車換行這樣的不可見字符當作輸入,空白字符被當作輸入內容的分隔符,回車換行則被用於提交輸入;另一方面,似乎用戶輸入任何可見字符,s.hasNext()都會返回true。

真的,沒有辦法了嗎?


其實是有解決辦法的,而且不止一種,請看下面:


方案一(Windows系統適用):ctrl + Z

如果是在Windows系統的cmd窗口中運行此程序,按下ctrl+Z,窗口會出現^Z這樣的符號,按下回車,程序打印出false,然后結束運行。請注意,程序是正常結束的,所以說:並非用戶輸入任何可見字符,s.hasNext()都會返回true,將^Z作為輸入,hasNext()會返回false這個方案在cmd窗口中有效,而在IDEA自帶的控制台中無效,不過在IDEA中按下ctrl+D可以達到同樣的效果。

另外,有StackOverflow用戶指出,對於Linux/Unix/MacOS環境,ctrl+D可以達到同樣的效果,不過我沒有做過相關的測試,僅僅在這里提一下。


方案二:使用hasNext(String pattern)

可以使用hasNext()的重載方法hasNext(String pattern)通過特殊終止符結束循環,就像下面這樣:

public static void main(String[] args) {
    Scanner s = new Scanner(System.in);
    while(!s.hasNext("exit")) {
        System.out.println("your input is " + s.next());
    }
}

當輸入exit,循環結束。有點類似於在mysql命令行程序中輸入quit,或者是在cmd中輸入exit


方案三:使用break跳出循環

在循環體中通過特殊終止符觸發break,例如下面這樣:

public static void main(String[] args) {
    Scanner s = new Scanner(System.in);
    while(s.hasNext()) {
        String tmp = s.next();
        if(tmp.equals("quit")) break;
        System.out.println("your input is " + tmp);
    }
}

當輸入quit,循環終止。


方案四:重定向輸入流

前面說過,hasNext()方法可能會在等待輸入時阻塞,可能的意思就是說:存在某種情況,hasNext()不會發生阻塞。比如說下面介紹的這種方案:

既然問題是由鍵盤輸入帶來的,不如就把產生問題的源頭干掉,我們可以重定向標准輸入使得系統從文件而不是從鍵盤讀取輸入。操作很簡單,提前在一個文本文件中編輯好輸入內容,然后在cmd中執行程序,只需給執行命令增加一點內容,像下面這樣:

java Test < text.txt //text.txt中包含了待輸入的內容

按下回車,程序從text.txt中讀取內容,告別從鍵盤獲取輸入。

這個方案有一個優點是用戶不再需要手動從鍵盤輸入數據,尤其是當我們做測試的時候,這種方式能幫我們節省很多時間和精力,輸入量越大,這種優勢越為明顯。IDEA中可以配置輸入重定向,打開 Run Configurations 就可以找到,這里我不詳細描述了。

另外,一些朋友提到:在編程網站中提交代碼while(s.hasNext())可以自己跳出循環,而在本地 IDEA 中使用while(s.hasNext())沒有辦法退出循環,其實就是因為在線編程網站將輸入流重定向了。


總結四種方案:

方案號 辦法 備注
方案一 ctrl+Z Windows系統適用
方案二 使用hasNext(String pattern)
方案三 使用break跳出循環 最朴素的思路
方案四 重定向輸入流 節省時間和精力

參考資料:

[1] Robert Sedgewick and Kevin Wayne 著,謝路雲 譯.算法.北京:人民郵電出版社,2012:24.

[2] trutheality.StackOverflow.https://stackoverflow.com/questions/10490344/how-to-get-out-of-while-loop-in-java-with-scanner-method-hasnext-as-condition

[3] while(hasNextInt())為什么不會結束循環.百度知道.https://zhidao.baidu.com/question/135843130068219805.html

[4] java的Scanner類的hasNext()方法問題.百度知道.https://zhidao.baidu.com/question/505075549.html

[5] 如何在JAVA中使用SCANNER方法“HASNEXT”作為條件退出WHILE循環?.dovov編程網.http://www.dovov.com/javascannerhasnextwhile.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM