HOWTO · Java
修復 Java Scanner NextLine Skips 錯誤
本文討論了 Java Scanner NextLine 跳過的原因以及如何修復它
本頁內容
當 Scanner.nextLine() 看起來在讀取數字後跳過姓名,常見原因是混用了讀取 token 與讀取整行的方法。Scanner.nextInt() 會讀取整數 token,但會將該行剩餘內容留給下一次讀取。接著呼叫 Scanner.nextLine() 時,取得的是這段剩餘內容(通常是空字串),而不是包含姓名的下一行。可以使用 Integer.parseInt() 將數字所在行讀成整行後再轉換,或先明確讀完目前行的剩餘內容,再讀取姓名。
為什麼 Scanner.nextLine() 看起來會跳過輸入
這個可執行範例使用兩行輸入:42 和 Ada Lovelace。nextInt() 會取得數字,緊接著呼叫的 nextLine() 則會傳回數字所在行的剩餘內容。輸出中的 Name: [] 顯示了傳回值;姓名所在行仍未讀取。
執行以下範例:
範例中的程式碼與輸出標籤維持英文。輸出顯示讀取到整數 42,而姓名欄位為空,因為姓名所在行尚未讀取。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import java.util.Scanner;
public class ScannerSkipExample {
public static void main(String[] args) {
Scanner scanner = new Scanner("42\nAda Lovelace\n");
int number = scanner.nextInt();
String name = scanner.nextLine();
System.out.println("Integer: " + number);
System.out.println("Name: [" + name + "]");
}
}
Integer: 42
Name: []
Scanner 如何讀取 token 與行
Scanner 同時提供以 token 為單位和以行為單位的讀取方法。next() 會讀取下一個 token,而 nextInt() 會讀取下一個 token 並將其轉換成整數。這兩種方法都不會讀取該 token 後方的整行剩餘內容。nextLine() 會傳回目前行的剩餘字元,並越過該行分隔符。如果 token 後面緊接著行分隔符,剩餘內容就沒有可見字元,因此 nextLine() 會傳回空字串。
這不是 nextLine() 會跳過輸入的特殊規則,而是程式預期讀取後續一行時,實際上要求讀取目前行的剩餘內容。同樣的情況也可能出現在其他讀取 token 的方法之後,例如 nextDouble()。Java Scanner 文件說明了這些輸入方法。
透過讀取整行修正被跳過的輸入
如果數字在一行、姓名在下一行,就將兩者都以整行方式讀取。只對數字所在行使用 Integer.parseInt() 進行轉換。此範例使用相同的輸入值,並保留姓名中的空格。
執行以下範例:
範例中的程式碼與輸出標籤維持英文。輸出顯示整數 42,以及包含空格的完整姓名 Ada Lovelace。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import java.util.Scanner;
public class ScannerWholeLineExample {
public static void main(String[] args) {
Scanner scanner = new Scanner("42\nAda Lovelace\n");
int number = Integer.parseInt(scanner.nextLine());
String name = scanner.nextLine();
System.out.println("Integer: " + number);
System.out.println("Name: " + name);
}
}
Integer: 42
Name: Ada Lovelace
兩次讀取都會讀完整行,因此第二次讀取能取得包含空格的完整姓名。如果第一行不是有效整數,Integer.parseInt() 會擲出 NumberFormatException;如果程式應該復原而非停止,請加入輸入驗證和重試迴圈。當應用程式將每個回答都視為一整行時,這種做法尤其簡單直接。
透過讀完目前行的剩餘內容修正被跳過的輸入
如果程式已使用 nextInt(),就在它之後立即呼叫一次 nextLine(),讀完數字所在行的剩餘內容。接著再呼叫一次,讀取預期的整行內容。第一次呼叫是刻意安排的;第二次才會讀取姓名。
執行以下範例:
範例中的程式碼與輸出標籤維持英文。輸出顯示整數 42,以及包含空格的完整姓名 Ada Lovelace。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import java.util.Scanner;
public class ScannerConsumeLineExample {
public static void main(String[] args) {
Scanner scanner = new Scanner("42\nAda Lovelace\n");
int number = scanner.nextInt();
scanner.nextLine();
String name = scanner.nextLine();
System.out.println("Integer: " + number);
System.out.println("Name: " + name);
}
}
Integer: 42
Name: Ada Lovelace
當使用 token 讀取方式解析數字很方便,而且該輸入行剩下的內容可以捨棄時,適合採用這種模式。如果數字後面可能還有有用文字,第一次呼叫 nextLine() 也會一併讀掉那些文字;此時請改為讀取整行,再解析其中的數字部分。
trim() 不會讀取待處理行的剩餘內容
trim() 會作用在已經讀取的 String 上。它會移除該字串開頭與結尾、值小於或等於空格字元的字元;它不會從 System.in 讀取資料、不會讀取待處理行的剩餘內容,也不會改變下一次 Scanner 呼叫會讀取哪一行。因此,在 nextInt() 後立即呼叫 scanner.nextLine().trim(),仍然只會修剪數字所在行的剩餘內容,通常結果是空字串;它不會取得下一行的姓名。
請先採用上述其中一種修正輸入順序的方式。只有在應用程式需要移除實際讀取那一行首尾的空白時,才在之後使用 trim()。診斷輸入行為時,請讓程式碼與結果以可選取的文字呈現,方便讀者直接比對呼叫順序。