【问题标题】:Scanner is skipping nextLine() after using next() or nextFoo()?扫描仪在使用 next() 或 nextFoo() 后跳过 nextLine()?
【发布时间】:2021-11-11 00:28:07
【问题描述】:

我正在使用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() 都会按照我想要的方式执行。

【问题讨论】:

  • 或者你可以像我一样使用 BufferedReader :) 我不在乎它是否是老派,它一直有效,并且永远对我有效。此外,BufferedReader 的知识在其他地方也有应用。我只是不喜欢扫描仪。

标签: java io java.util.scanner


【解决方案1】:

问题在于 input.nextInt() 方法 - 它只读取 int 值。因此,当您使用 input.nextLine() 继续阅读时,您会收到 "\n" Enter 键。所以要跳过这个,你必须添加 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,但我仍然需要解释这种行为
  • 仅供参考:从stackoverflow.com/questions/7056749/…合并
  • 如果是这样的话,如果我们在Scanner.nextInt()之后调用Scanner.next(),为什么不会遇到同样的问题。为什么我在Scanner.next() 中没有得到一个空白字符串?
【解决方案2】:

这是因为当你输入一个数字然后按 Enter 时,input.nextInt() 只消耗数字,而不是“行尾”。当input.nextLine() 执行时,它会从第一个输入中消耗仍在缓冲区中的“行尾”。

而是在input.nextInt() 之后立即使用input.nextLine()

【讨论】:

  • @Victor 这不是错误。它按规定工作。您可能会争辩说,应该有一种简单的方法来告诉 api 也消耗目标输入之后的任何空格。
  • 我明白了。谢谢@Bohemian,这正是我所争论的。我认为这应该改变,也许你可以指出我在下一个 JCP 中建议这个“问题”的地方。
  • @victor 您可以通过Report a Bug or Request a Feature 页面请求(并了解如何贡献代码)功能。
  • 仅供参考:从stackoverflow.com/questions/7056749/…合并
  • 如果是这样的话,如果我们在Scanner.nextInt()之后使用Scanner.next()调用,为什么不会遇到同样的问题。为什么我在Scanner.next() 中没有得到一个空白字符串?
【解决方案3】:

这是因为Scanner.nextInt 方法不会读取您通过按“Enter”创建的输入中的换行符 字符,因此在读取该换行符。

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

解决方法:

  • 在每个Scanner.nextIntScanner.nextFoo 之后调用Scanner.nextLine 以消耗该行的其余部分,包括换行符

    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();
    

【讨论】:

  • @blekione。你必须使用try-catch,因为Integer.parseInt 在传递无效参数时会抛出NumberFormatException。稍后您将了解异常。例如:-Integer.parseInt("abc")。您不希望将“abc”转换为 int 对吗?
  • @blekione。因此,在上述情况下,您的代码将在此时停止,您将无法继续执行。使用异常处理,您可以处理此类情况。
  • 后者到底好到什么程度?通过允许组逗号和语言环境前缀和后缀,AFAIK Scanner#nextInt() 在查找正确的整数方面更加宽松。 Integer#parseInt() 只允许数字和小数点加上一个可选的符号。
  • 我个人更喜欢预先检查 Scanner#hasNextFoo 而不是 try-catch,但这也有效。
  • 使用 ParseDouble 有一个新问题,即 nextDouble 使用十进制的区域配置(. 或 ,)但 Parsedouble 总是接收美国十进制( . )。
【解决方案4】:

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

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

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

【讨论】:

  • 仅供参考:从stackoverflow.com/questions/7056749/…合并
  • 你必须在这里使用try catch。如果输入不是数字会发生什么。 NumberFormatException 需要在这里处理。
  • 这假定您尝试此操作的每一行上只有一个 int 标记。
  • 如果是这样的话,如果我们在Scanner.nextInt()之后使用Scanner.next()调用,为什么不会遇到同样的问题。为什么我在Scanner.next() 中没有得到一个空白字符串?
  • @Tushar 我希望我记得,自从我在 13 年写下那个答案后,我就没有接触过 Java
【解决方案5】:

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

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

【讨论】:

【解决方案6】:

而不是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();
}

【讨论】:

【解决方案7】:

为什么不每次阅读都使用新的扫描仪?如下所示。使用这种方法,您将不会遇到问题。

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

【讨论】:

  • 但是你必须关闭Scanner 以防止内存泄漏。浪费时间?
  • 如果 Scanner 被包装在一个方法中,GC 不会处理这个问题吗?比如:String getInput() {return new Scanner(System.in).nextLine()};
  • 这是绝对错误的。 nextInt() 不会使用换行符,无论它是在“新”Scanner 还是已使用的换行符中。
【解决方案8】:

TL;DR
如果您不确定之前的指令是scanner.next() 还是scanner.next*TypeName*(),请在scanner.nextLine() 之前使用scanner.skip("\\R?")

skip 在哪里使用正则表达式

  • \R代表行分隔符

  • ? 将使 \R 行分隔符序列可选 - 这将阻止 skip 方法

    (a) 等待匹配序列 - 如果像 System.in 这样的数据源仍然开放,但没有找到行分隔符
    (b) 在终止/结束数据源(如文件或字符串)的情况下抛出java.util.NoSuchElementException

你需要知道的事情:

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

  • 回车(CR - 在字符串文字中表示为"\r"

  • 换行(LF - 在字符串文字中表示为"\n"

  • 当您从控制台读取数据时,它允许用户输入他的响应,当他完成后,他需要以某种方式确认这一事实。为此,用户需要按键盘上的“enter”/“return”键。

重要的是,除了确保将用户数据放置到标准输入(由System.in 表示,由Scanner 读取)之外,此键还发送依赖于操作系统的行分隔符(如 Windows @ 987654337@) 之后。

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

问题

Scanner#nextInt(和其他Scanner#next<b><i>Type</i></b> 方法)不允许扫描器使用这些行分隔符。它将从System.in 读取它们(扫描器怎么会知道用户没有更多代表age 值的数字而不是面对空格?)这将从标准输入中删除它们,但它也会缓存这些行分隔符在内部。我们需要记住的是,所有的 Scanner 方法总是从缓存的文本开始扫描。

现在Scanner#nextLine() 只是收集并返回所有字符直到找到行分隔符(或流的结尾)。但由于从控制台读取数字后的行分隔符立即在 Scanner 的缓存中找到,因此它返回空字符串,这意味着 Scanner 无法在这些行分隔符(或流的结尾)之前找到任何字符。
BTW nextLine使用那些行分隔符。

解决方案

因此,当您想询问数字,然后询问整行,同时避免 nextLine 导致的空字符串时,要么

  • 使用扫描仪缓存中nextInt 留下的行分隔符
  • 致电nextLine,
  • 或 IMO 更具可读性方法是调用 skip("\\R")skip("\r\n|\r|\n") 让扫描仪跳过与行分隔符匹配的部分(有关 \R 的更多信息: https://stackoverflow.com/a/31060125)
  • 根本不要使用nextInt(也不要使用next,或任何nextTYPE 方法)。而是使用nextLine 逐行读取整个数据,并通过Integer.parseInt 将每一行中的数字(假设一行仅包含一个数字)解析为正确的类型,例如int

顺便说一句Scanner#next<b><i>Type</i></b> 方法可以跳过分隔符(默认情况下所有空格,如制表符、行分隔符),包括那些被扫描仪缓存的,直到它们找到下一个非分隔符值(令牌)。感谢"42\r\n\r\n321\r\n\r\n\r\nfoobar" 代码之类的输入

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

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

【讨论】:

    【解决方案9】:

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

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

    【讨论】:

      【解决方案10】:

      如果您想快速扫描输入而不被 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() - 扫描长值
      • scanString() - 扫描字符串值
      • scanDouble() - 扫描双精度值
      • scanInt(int[] array) - 扫描完整的 Array(Integer)
      • scanLong(long[] array) - 扫描完整的 Array(Long)

      用法:

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

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

      import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; 4. 从你的 main 方法中抛出 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....
      

      【讨论】:

        【解决方案11】:

        如果你想同时读取字符串和整数,一个解决方案是使用两个扫描器:

        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();
        

        【讨论】:

        • 请不要这样做。每个输入流只有一个扫描仪。此外,当您执行 intScanner.close(); 时,您将关闭 stringScanner + 整个 System.in 以完成其余的执行。
        【解决方案12】:
        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() 将读取整数行的剩余部分(为空)。
        【解决方案13】:

        sc.nextLine() 比解析输入更好。 因为在性能方面它会很好。

        【讨论】:

          【解决方案14】:

          我想我参加聚会已经很晚了..

          如前所述,在获取您的 int 值后调用 input.nextLine() 将解决您的问题。您的代码不起作用的原因是因为您的输入(您输入 int 的位置)没有其他内容可以存储到 string1 中。我将对整个主题稍作说明。

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

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

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

          54 234

          firstNumbersecondNumber 变量的值分别变为 54 和 234。之所以这样工作,是因为当 nextInt() 方法采用在价值观。它只需要 "next int" 并继续前进。除了 nextLine() 之外,其余的 nextFoo() 方法也是如此。

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

          最后,next() 方法只取最近的字符串而不生成新行;这使得这是在同一行中获取单独字符串的首选方法。

          我希望这会有所帮助.. 编码愉快!

          【讨论】:

          • 什么是“换行符”?
          • @Abcd 换行基本上意味着“从新行开始”。
          • 那个“54 234”的例子真的很清楚。
          【解决方案15】:

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

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

          【讨论】:

            【解决方案16】:

            如果我希望输入非空

            避免:
            – 如果以下输入被未经检查的 scan.nextLine() 作为解决方法吃掉,则会丢失数据
            – 由于scan.nextLine()scan.next() 替换,仅部分读取行导致数据丢失(输入:“yippie ya yeah”)
            – 使用Scanner 方法解析输入时抛出的Exceptions(先读取,后解析)

            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
            

            【讨论】:

            • 以前从未见过。这在(当心垃圾邮件)关闭场景中会如何表现? stackoverflow.com/a/65515359/2148953
            • @aran 虽然这里的问题是,为了防止空输入 (在任何情况下工作),上面提到的另一个问题是,活动 @987654330 的资源@ 正在通过一个错误的调用 close 被释放。使用autoclosing 是关闭Scanner 或任何其他AutoCloseable 的正确方法。
            【解决方案17】:

            由于nextXXX() 方法不读取newline,但nextLine() 除外。我们可以在读取任何non-string 值(在本例中为int)后跳过newline,如下所示使用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);
            

            【讨论】:

              【解决方案18】:

              在我的一个用例中,我遇到了读取 字符串值前面有几个整数值的场景。我不得不使用“for / while 循环”来读取这些值。在这种情况下,上述建议均无效。

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

              【讨论】:

              • 简短的回答是空格很重要。如果同一行有多个整数,仍然可以使用 nextLine 并拆分它们,并且在输入行的最后一次 next 调用之后仍然需要一个 nextLine
              【解决方案19】:

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

              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)
              

              【讨论】:

              • 虽然这解决了问题,但它并没有添加比原始答案更多的细节或解释它的工作原理
              【解决方案20】:

              问题在于 input.nextInt() 方法 - 它只读取 int 值。因此,当您使用 input.nextLine() 继续阅读时,您会收到 "\n" Enter 键。所以要跳过这个,你必须添加 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();
              

              【讨论】:

                【解决方案21】:

                要解决这个问题,只需创建一个 scan.nextLine(),其中 scan 是 Scanner 对象的一个​​实例。例如,我使用一个简单的 HackerRank Problem 进行解释。

                package com.company;
                import java.util.Scanner;
                
                public class hackerrank {
                public static void main(String[] args) {
                    Scanner scan = new Scanner(System.in);
                    int i = scan.nextInt();
                    double d = scan.nextDouble();
                    scan.nextLine(); // This line shall stop the skipping the nextLine() 
                    String s = scan.nextLine();
                    scan.close();
                
                
                
                    // Write your code here.
                
                    System.out.println("String: " + s);
                    System.out.println("Double: " + d);
                    System.out.println("Int: " + i);
                }
                

                }

                【讨论】:

                  【解决方案22】:

                  nextLine() 将直接将 enter 读取为空行,无需等待文本。

                  通过添加额外的扫描器来消耗空行的简单解决方案:

                  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)
                  

                  【讨论】:

                    【解决方案23】:

                    对于java初学者来说,这是一个非常基本的问题。我在开始 java(自学)时也遇到了同样的问题。 实际上,当我们接受整数数据类型的输入时,它只读取整数值并留下 newLine(\n) 字符,当我们尝试接受新输入时,这一行(即由整数动态输入留下新行)会产生问题。 例如。就像我们先输入整数,然后再尝试输入字符串一样。

                    value1=sc.nextInt();
                    value2=sc.nextLine();
                    

                    value2 将自动读取 newLine 字符并且不会接受用户输入。

                    解决方案: 只是我们需要在接受下一个用户输入之前添加一行代码,即

                    sc.nextLine();
                    

                    value1=sc.nextInt();
                    sc.nextLine();
                    value2=sc.nextLine();
                    

                    注意:不要忘记关闭 Scanner 以防止内存泄漏;

                    【讨论】:

                      猜你喜欢
                      相关资源
                      最近更新 更多