JAVA从键盘输入数据时,一般可采用BufferedReader类或者Scanner类。由于Scanner类的方法更加灵活多样,得到了更多的应用。
最近刷题遇到String数组输入的情况,发现Scanner方法nextLine()、next()、nextInt()的一些区别。要求先输入一个正整数n,然后输入n个字符串,代码如下:
import java.util.Scanner;
public class MyCode2{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n=0;
while(sc.hasNext()){
n = sc.nextInt();
String[] s = new String[n];
int count=0;
while(count<n){
s[count] = sc.nextLine();
System.out.println("s["+count+"]:"+s[count]);
count++;
}
}
}
}
运行结果如下( 每次输入完都以回车键结束):
如果输入n后,先空格然后输入s[0],则得到以下结果:
注意:第一个输入的字符串是“ abc”,有一个空格。
输入时,先调用nextInt()函数输入n,然后回车,再调用nextLine()函数依次输入String,结果第一个字符串自动被赋值,即第一次调用nextLine()函数返回的值并不是从键盘输入的值,而是读入的回车。
若将数组s定义为int类型数组,输入都是调用nextInt()函数,则不存在此问题,运行结果如下:
查询JAVA SE 8.0 API文档,说明如下:
A Scanner breaks its input into tokens using a delimiter pattern, which by default matches whitespace. The resulting tokens may then be converted into values of different types using the various next methods.
可知,nextLine()扫描当前行,遇到回车后返回回车前的字符串,不包括末尾的换行符。nextInt()将输入的下一个标记返回为一个int数据,而标记指分隔符(相邻空格或者回车)之间的字符串(不包括分隔符)。
查询JDK源码,应该是nextLine()中clearCaches()清空了缓冲区,而nextInt()/next()/nextFloat()等不会清空缓存,只是读取了相邻分隔符之间的字符串,将分隔符仍然留在缓冲区。
因此,在前面的程序里,先调用nextInt()再调用nextLine()函数,当输入过程为“3->回车->....”,第一个回车后程序不等你输入而直接输出“s[0]:”,因为nextLine()首先读到的是上次输入遗留在缓冲区里的第一个回车,然后直接返回空。当输入过程为“3->空格->abc->回车->dfg....”,nextLine()返回的是“空格abc”。
为了防止这类问题出现,可以在第一次调用nextLine()输入字符串前调用一次nextLine()清除缓存,即
import java.util.Scanner;
public class MyCode2{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n=0;
while(sc.hasNext()){
n = sc.nextInt();
String[] s = new String[n];
int count=0;
sc.nextLine();
while(count<n){
s[count] = sc.nextLine();
System.out.println("s["+count+"]:"+s[count]);
count++;
}
}
}
}
next()与nextInt()相似,也是返回相邻分隔符之间的字符串,因此无法返回带空格的句子。
还有一点需要注意的是,next()、nextInt()等输入的时候必须是完整的标记,即缓冲区必须有“分隔符+数据+分隔符”,才能把数据返回,若数据为空,则一直等待输入,即只按空格或回车next()、nextInt()无法返回空数据。而nextLine()只要缓冲区有回车,即可以返回空数据。
JDK相关源码如下:
/**
* Advances this scanner past the current line and returns the input
* that was skipped.
*
* This method returns the rest of the current line, excluding any line
* separator at the end. The position is set to the beginning of the next
* line.
*
* <p>Since this method continues to search through the input looking
* for a line separator, it may buffer all of the input searching for
* the line to skip if no line separators are present.
*
* @return the line that was skipped
* @throws NoSuchElementException if no line was found
* @throws IllegalStateException if this scanner is closed
*/
public String nextLine() {
if (hasNextPattern == linePattern())
return getCachedResult();
clearCaches();
String result = findWithinHorizon(linePattern, 0);
if (result == null)
throw new NoSuchElementException("No line found");
MatchResult mr = this.match();
String lineSep = mr.group(1);
if (lineSep != null)
result = result.substring(0, result.length() - lineSep.length());
if (result == null)
throw new NoSuchElementException();
else
return result;
}
/**
* Scans the next token of the input as an <tt>int</tt>.
*
* <p> An invocation of this method of the form
* <tt>nextInt()</tt> behaves in exactly the same way as the
* invocation <tt>nextInt(radix)</tt>, where <code>radix</code>
* is the default radix of this scanner.
*
* @return the <tt>int</tt> scanned from the input
* @throws InputMismatchException
* if the next token does not match the <i>Integer</i>
* regular expression, or is out of range
* @throws NoSuchElementException if input is exhausted
* @throws IllegalStateException if this scanner is closed
*/
public int nextInt() {
return nextInt(defaultRadix);
}
/**
* Scans the next token of the input as an <tt>int</tt>.
* This method will throw <code>InputMismatchException</code>
* if the next token cannot be translated into a valid int value as
* described below. If the translation is successful, the scanner advances
* past the input that matched.
*
* <p> If the next token matches the <a
* href="#Integer-regex"><i>Integer</i></a> regular expression defined
* above then the token is converted into an <tt>int</tt> value as if by
* removing all locale specific prefixes, group separators, and locale
* specific suffixes, then mapping non-ASCII digits into ASCII
* digits via {@link Character#digit Character.digit}, prepending a
* negative sign (-) if the locale specific negative prefixes and suffixes
* were present, and passing the resulting string to
* {@link Integer#parseInt(String, int) Integer.parseInt} with the
* specified radix.
*
* @param radix the radix used to interpret the token as an int value
* @return the <tt>int</tt> scanned from the input
* @throws InputMismatchException
* if the next token does not match the <i>Integer</i>
* regular expression, or is out of range
* @throws NoSuchElementException if input is exhausted
* @throws IllegalStateException if this scanner is closed
*/
public int nextInt(int radix) {
// Check cached result
if ((typeCache != null) && (typeCache instanceof Integer)
&& this.radix == radix) {
int val = ((Integer)typeCache).intValue();
useTypeCache();
return val;
}
setRadix(radix);
clearCaches();
// Search for next int
try {
String s = next(integerPattern());
if (matcher.group(SIMPLE_GROUP_INDEX) == null)
s = processIntegerToken(s);
return Integer.parseInt(s, radix);
} catch (NumberFormatException nfe) {
position = matcher.start(); // don't skip bad token
throw new InputMismatchException(nfe.getMessage());
}
}