扫描仪在使用next()或nextFoo()之后跳过nextLine()吗?


683

我正在使用这些Scanner方法nextInt()nextLine()读取输入。

看起来像这样:

System.out.println("Enter numerical value");    
int option;
option = input.nextInt(); // Read numerical value from input
System.out.println("Enter 1st string"); 
String string1 = input.nextLine(); // Read 1st string (this is skipped)
System.out.println("Enter 2nd string");
String string2 = input.nextLine(); // Read 2nd string (this appears right after reading numerical value)

问题在于输入数值后,第一个将input.nextLine()被跳过,第二个将input.nextLine()被执行,因此我的输出如下所示:

Enter numerical value
3   // This is my input
Enter 1st string    // The program is supposed to stop here and wait for my input, but is skipped
Enter 2nd string    // ...and this line is executed and waits for my input

我测试了我的应用程序,看起来问题出在使用中input.nextInt()。如果我删除它,然后这两个string1 = input.nextLine()string2 = input.nextLine()执行,我希望他们能。


5
或者,您可能像我一样,并使用BufferedReader :)我不在乎它是否过时,它一直有效并且将始终为我工作。另外,BufferedReader的知识在其他地方也有应用。我根本不喜欢Scanner。
Cruncher

Answers:


829

那是因为该Scanner.nextInt方法不会在您按“ Enter”键创建的输入中读取换行符,因此Scanner.nextLine在读取该换行符后对return的调用。

当您使用Scanner.nextLineafter Scanner.next()或任何Scanner.nextFoo方法(nextLine本身除外)时,您将遇到类似的行为。

解决方法:

  • 要么把一个Scanner.nextLine电话后,每Scanner.nextIntScanner.nextFoo消耗该行包括休息换行符

    int option = input.nextInt();
    input.nextLine();  // Consume newline left-over
    String str1 = input.nextLine();
  • 或者,甚至更好的方法是通读输入,Scanner.nextLine然后将输入转换为所需的正确格式。例如,您可以使用Integer.parseInt(String)方法转换为整数。

    int option = 0;
    try {
        option = Integer.parseInt(input.nextLine());
    } catch (NumberFormatException e) {
        e.printStackTrace();
    }
    String str1 = input.nextLine();

4
@blekione。您必须使用try-catch,因为Integer.parseIntNumberFormatException无效参数传递给该参数时将引发。稍后您将了解异常。对于EG:- Integer.parseInt("abc")。您不希望将“ abc”转换为int吗?
罗希特·贾因

3
@blekione。因此,在上述情况下,您的代码将在此时停止,您将无法继续执行。使用异常处理,您可以处理这种情况。
罗希特·贾因

4
后者在多大程度上更好?AFAIK Scanner#nextInt()通过允许组逗号和区域设置前缀和后缀,可以更轻松地找到正确的整数。Integer#parseInt()仅允许数字和小数点以及可选的符号。
Mordechai 2015年

5
我个人更喜欢Scanner#hasNextFoo事前检查而不是尝试捕获,但这也可以。
MildlyMilquetoast

1
使用ParseDouble有一个新问题,那就是nextDouble使用十进制(。或,)的区域配置,但是Parsedouble总是接收美国十进制(。)。
olmerg

209

问题出在 input.nextInt()方法上-它只读取int值。因此,当您继续阅读input.nextLine()时,会收到“ \ n”回车键。因此,要跳过此步骤,您必须添加input.nextLine()。希望现在应该弄清楚。

像这样尝试:

System.out.print("Insert a number: ");
int number = input.nextInt();
input.nextLine(); // This line you have to add (It consumes the \n character)
System.out.print("Text1: ");
String text1 = input.nextLine();
System.out.print("Text2: ");
String text2 = input.nextLine();

umm似乎是一种解决方案,使跳过的行不可用nextLine,但我仍然需要对此行为进行解释
Eng.Fouad 2011年


问题:2012年10月27日16:37问。2011年8月14日在12:24回答。这怎么可能?
Apoorv Patne

79

这是因为当您输入数字然后按时Enterinput.nextInt()仅消耗该数字,而不消耗“行尾”。什么时候input.nextLine()执行时,它会消耗来自第一输入缓冲器中的“行结束”静止。

相反,请input.nextLine()在之后立即使用input.nextInt()


7
@Victor这不是错误。它按指定方式工作。您可能会争辩说,应该有一种简单的方法来告诉api在目标输入之后也消耗任何空格。
波希米亚

我知道了。谢谢@Bohemian,那正是我所争论的。我认为应该对此进行更改,也许您可​​以指出我在下一个JCP中建议此“问题”的位置。
维克多

@victor您可以通过“ 报告错误”或“请求功能”页面请求(并了解如何向其贡献代码)。
波希米亚


48

关于的这个问题似乎有很多问题java.util.Scanner。我认为一种更具可读性/惯用性的解决方案是调用,scanner.skip("[\r\n]+")以在调用之后删除任何换行符nextInt()

编辑:如下@PatrickParker所述,如果用户在数字后输入任何空格,这将导致无限循环。请参阅他们的答案以获取与跳过一起使用的更好模式:https : //stackoverflow.com/a/42471816/143585



我知道我们要如何删除缓冲区中的数据,但是在这种情况下,请为我提供帮助:stackoverflow.com/questions/33585314/having-issues-with-scanner
Khuong 2015年

1
仅供参考,这将导致无限循环,因为用户在数字输入后键入任何制表符或空格。在这里查看我的答案以获得更好的跳过:stackoverflow.com/a/42471816/7098259
Patrick Parker

@PatrickParker:确实确实会导致无限循环!谢谢,编辑了答案以链接到您的答案。
Denis Tulskiy '17

33

这样做是因为input.nextInt();不捕获换行符。您可以像在input.nextLine();下面建议的那样添加其他建议。
另外,您也可以使用C#样式,将nextLine解析为整数,如下所示:

int number = Integer.parseInt(input.nextLine()); 

这样做同样有效,并且可以节省一行代码。



1
您必须在这里使用try catch。如果输入不是数字,将会发生什么。这里需要处理NumberFormatException。
阿伦杰夫

假设您为此尝试的每一行上只有一个int令牌。
cellepo

25

TL; DR scanner.skip("\\R")在每次scanner.newLine()调用前使用,在以下时间执行:

  • scanner.next()
  • scanner.next*TYPE*() 方法。

您需要知道的事情:

  • 代表几行的文本在行之间也包含不可打印的字符(我们称为行分隔符),例如

    • 回车符(CR-以String文字表示为 "\r"
    • 换行符(LF-以表示的字符串文字"\n"
  • 当您从控制台读取数据时,它允许用户键入其响应,完成后他需要某种方式确认该事实。为此,要求用户按下键盘上的“输入” /“返回”键。

    重要的是,此键除了确保将用户数据放入标准输入(由System.in表示为Scanner)外,还向其发送依赖于操作系统的行分隔符(例如Windows \r\n)。

    因此,当您向用户询问诸如的值age,并且用户键入42并按Enter键时,标准输入将包含"42\r\n"

问题

Scanner#nextInt(和其他方法)不允许Scanner 使用这些行分隔符。它将从中读取它们(否则Scanner怎么会知道来自用户的代表数值的数字不会比面对空格多?),这会将它们从标准输入中删除,但也会在内部缓存这些行分隔符Scanner#nextTypeSystem.inage。我们需要记住的是,所有Scanner方法始终从缓存的文本开始进行扫描。

现在Scanner#nextLine()简单地收集并返回所有字符,直到找到行分隔符(或流的末尾)为止。但是,由于从控制台读取数字后的行分隔符会立即在Scanner的缓存中找到,因此它返回空字符串,这意味着Scanner在这些行分隔符(或流的末尾)之前找不到任何字符。
BTW nextLine消耗了那些行分隔符。

因此,当您要先输入数字然后再查询整行,同时又要避免该空字符串成为时nextLine

  • 消耗nextIntScanners缓存 剩余的行分隔符
    • 打电话nextLine
    • 或IMO 更具可读性的方式是调用skip("\\R")skip("\r\n|\r|\n")让Scanner跳过由行分隔符匹配的部分(有关更多信息\Rhttps : //stackoverflow.com/a/31060125
  • 根本不使用nextInt(nor next,或任何nextTYPE方法)。而是使用逐行读取整个数据,nextLine并解析每行中的数字(假设一行仅包含一个数字)为适当的类型,例如intvia Integer.parseInt

顺便说一句:方法可以跳过定界符(默认情况下,所有空白,如制表符,行分隔符),包括扫描程序缓存的定界符,直到找到下一个非定界符值(令牌)为止。多亏了输入像代码Scanner#nextType"42\r\n\r\n321\r\n\r\n\r\nfoobar"

int num1 = sc.nextInt();
int num2 = sc.nextInt();
String name = sc.next();

将能够正确分配num1=42 num2=321 name=foobar


14

代替input.nextLine()使用input.next(),应该可以解决问题。

修改后的代码:

public static Scanner input = new Scanner(System.in);

public static void main(String[] args)
{
    System.out.print("Insert a number: ");
    int number = input.nextInt();
    System.out.print("Text1: ");
    String text1 = input.next();
    System.out.print("Text2: ");
    String text2 = input.next();
}


13

如果要同时读取字符串和整数,则一种解决方案是使用两个扫描仪:

Scanner stringScanner = new Scanner(System.in);
Scanner intScanner = new Scanner(System.in);

intScanner.nextInt();
String s = stringScanner.nextLine(); // unaffected by previous nextInt()
System.out.println(s);

intScanner.close();
stringScanner.close();

1
这是我一直在寻找的一段时间。明智之举!
利亚姆·威尔逊

1
@BdLearner scanner.nextLine()应该工作。
安德烈·瓦伦蒂

11

如果要快速扫描输入而又不混淆Scanner类nextLine()方法,请为其使用“自定义输入扫描器”。

代码:

class ScanReader {
/**
* @author Nikunj Khokhar
*/
    private byte[] buf = new byte[4 * 1024];
    private int index;
    private BufferedInputStream in;
    private int total;

    public ScanReader(InputStream inputStream) {
        in = new BufferedInputStream(inputStream);
    }

    private int scan() throws IOException {
        if (index >= total) {
            index = 0;
            total = in.read(buf);
            if (total <= 0) return -1;
        }
        return buf[index++];
    }
    public char scanChar(){
        int c=scan();
        while (isWhiteSpace(c))c=scan();
        return (char)c;
    }


    public int scanInt() throws IOException {
        int integer = 0;
        int n = scan();
        while (isWhiteSpace(n)) n = scan();
        int neg = 1;
        if (n == '-') {
            neg = -1;
            n = scan();
        }
        while (!isWhiteSpace(n)) {
            if (n >= '0' && n <= '9') {
                integer *= 10;
                integer += n - '0';
                n = scan();
            }
        }
        return neg * integer;
    }

    public String scanString() throws IOException {
        int c = scan();
        while (isWhiteSpace(c)) c = scan();
        StringBuilder res = new StringBuilder();
        do {
            res.appendCodePoint(c);
            c = scan();
        } while (!isWhiteSpace(c));
        return res.toString();
    }

    private boolean isWhiteSpace(int n) {
        if (n == ' ' || n == '\n' || n == '\r' || n == '\t' || n == -1) return true;
        else return false;
    }

    public long scanLong() throws IOException {
        long integer = 0;
        int n = scan();
        while (isWhiteSpace(n)) n = scan();
        int neg = 1;
        if (n == '-') {
            neg = -1;
            n = scan();
        }
        while (!isWhiteSpace(n)) {
            if (n >= '0' && n <= '9') {
                integer *= 10;
                integer += n - '0';
                n = scan();
            }
        }
        return neg * integer;
    }

    public void scanLong(long[] A) throws IOException {
        for (int i = 0; i < A.length; i++) A[i] = scanLong();
    }

    public void scanInt(int[] A) throws IOException {
        for (int i = 0; i < A.length; i++) A[i] = scanInt();
    }

    public double scanDouble() throws IOException {
        int c = scan();
        while (isWhiteSpace(c)) c = scan();
        int sgn = 1;
        if (c == '-') {
            sgn = -1;
            c = scan();
        }
        double res = 0;
        while (!isWhiteSpace(c) && c != '.') {
            if (c == 'e' || c == 'E') {
                return res * Math.pow(10, scanInt());
            }
            res *= 10;
            res += c - '0';
            c = scan();
        }
        if (c == '.') {
            c = scan();
            double m = 1;
            while (!isWhiteSpace(c)) {
                if (c == 'e' || c == 'E') {
                    return res * Math.pow(10, scanInt());
                }
                m /= 10;
                res += (c - '0') * m;
                c = scan();
            }
        }
        return res * sgn;
    }

}

优点 :

  • 扫描输入比BufferReader更快
  • 降低时间复杂度
  • 下一个输入刷新缓冲区

方法 :

  • scanChar()-扫描单个字符
  • scanInt()-扫描整数值
  • scanLong()-扫描Long值
  • scanString()-扫描字符串值
  • scanDouble()-扫描Double值
  • scanInt(int [] array)-扫描整个Array(Integer)
  • scanLong(long [] array)-扫描完整的Array(Long)

用法:

  1. 将给定代码复制到您的Java代码下方。
  2. 给定类的初始化对象

ScanReader sc = new ScanReader(System.in); 3.导入必要的类:

import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; 4.从您的主要方法中抛出IOException以处理异常5.使用提供的方法。6.享受

范例:

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
class Main{
    public static void main(String... as) throws IOException{
        ScanReader sc = new ScanReader(System.in);
        int a=sc.scanInt();
        System.out.println(a);
    }
}
class ScanReader....

10

为了避免此问题,请nextLine();在之后立即使用,nextInt();因为它有助于清除缓冲区。当您按时ENTERnextInt();不会捕获新行,因此,Scanner稍后将跳过代码。

Scanner scanner =  new Scanner(System.in);
int option = scanner.nextInt();
scanner.nextLine(); //clearing the buffer


5

我想我晚会很晚。

如前所述,input.nextLine()在获取int值后调用即可解决您的问题。您的代码无法正常运行的原因是,您的输入(您在其中输入int的内容)没有其他可存储的内容string1。我将为整个主题提供更多信息。

nextLine()视为Scanner类中nextFoo()方法中的奇数。让我们举个简单的例子。假设我们有两行代码,如下所示:

int firstNumber = input.nextInt();
int secondNumber = input.nextInt();

如果我们输入以下值(作为单行输入)

54234

我们firstNumbersecondNumber变量的值分别变为54和234。之所以这样工作,是因为当nextInt()方法接受值时,不会自动生成换行符(即\ n) 。它只是采用“下一个int”并继续前进。除nextLine()之外,其余的nextFoo()方法都是相同的。

nextLine()在获取值后立即生成换行符;这就是@RohitJain所说的新换行已“消耗”的意思。

最后,next()方法仅采用最近的String 而不生成新行;这使其成为在同一行中采用单独的字符串的首选方法。

希望对您有所帮助。


什么是“换行符”?
弓箭手

@Abcd新换行基本上意味着“从新行开始”。
塔斯林·奥塞尼

1
该“ 54 234”示例确实使事情变得清晰。
阿隆索阿尔特

3

使用2个扫描仪对象而不是一个

Scanner input = new Scanner(System.in);
System.out.println("Enter numerical value");    
int option;
Scanner input2 = new Scanner(System.in);
option = input2.nextInt();

2
public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int i = scan.nextInt();
        scan.nextLine();
        double d = scan.nextDouble();
        scan.nextLine();
        String s = scan.nextLine();

        System.out.println("String: " + s);
        System.out.println("Double: " + d);
        System.out.println("Int: " + i);
    }

如果您在nextInt()方法之后立即使用nextLine()方法,请记住nextInt()读取整数标记;因此,该整数输入行的最后一个换行符仍在输入缓冲区中排队,下一个nextLine()将读取整数行的其余部分(为空)。
Neeraj Gahlawat

2

如果我希望输入非空

public static Function<Scanner,String> scanLine = (scan -> {
    String s = scan.nextLine();
    return( s.length() == 0 ? scan.nextLine() : s );
  });


在以上示例中使用:

System.out.println("Enter numerical value");    
int option = input.nextInt(); // read numerical value from input

System.out.println("Enter 1st string"); 
String string1 = scanLine.apply( input ); // read 1st string
System.out.println("Enter 2nd string");
String string2 = scanLine.apply( input ); // read 2nd string

2

在我的一个用例中,有一种方案是读取一个字符串值,然后是几个整数值。我不得不使用for / while循环 ”来读取值。在这种情况下,以上建议均无效。

使用input.next()而不是input.nextLine()解决问题。希望这对处理类似情况的人可能有所帮助。


2

使用此代码可以解决您的问题。

System.out.println("Enter numerical value");    
int option;
option = input.nextInt(); // Read numerical value from input
input.nextLine();
System.out.println("Enter 1st string"); 
String string1 = input.nextLine(); // Read 1st string (this is skipped)
System.out.println("Enter 2nd string");
String string2 = input.nextLine(); // Read 2nd string (this appears right after reading numerical value)

2

由于nextXXX()方法不读newline,除了nextLine()newline在读取任何non-string值(int在这种情况下)之后,我们可以通过以下方法跳过scanner.skip()

Scanner sc = new Scanner(System.in);
int x = sc.nextInt();
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(x);
double y = sc.nextDouble();
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(y);
char z = sc.next().charAt(0);
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(z);
String hello = sc.nextLine();
System.out.println(hello);
float tt = sc.nextFloat();
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(tt);

0

为什么不为每个阅读使用新的扫描仪?像下面。使用这种方法,您将不会遇到问题。

int i = new Scanner(System.in).nextInt();

3
但是随后您必须关闭,Scanner以防止内存泄漏。浪费时间?
TheCoffeeCup

1
如果将Scanner包裹在某种方法中,GC会不会解决?像这样:String getInput(){返回新的Scanner(System.in).nextLine()};
Tobias Johansson

这是绝对不正确的。 nextInt()不管换行符是“ new” Scanner还是已经使用过的换行符,都不会使用。
达伍德·伊本·卡里姆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.