当前位置: 首页 > article >正文

【基础语法】Java Scanner hasNext() 和 hasNextLine() 的区别

OJ在线编程常见输入输出练习中默认模板

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int a = in.nextInt();
            int b = in.nextInt();
            System.out.println(a + b);
        }
    }
}

API源码解析

JDK 8

Java.util.Scanner Structure
Java.util.Scanner 类结构

hasNext

功能:判断是否还有下一个值,通过检查缓冲区数据和标记(token)实现。

关键方法:

  1. 循环检查输入:使用一个while循环来不断检查输入源是否关闭(sourceClosed变量)。在循环内部:

    • 如果当前缓冲区中有可用的标记(通过hasTokenInBuffer()方法检查),则通过revertState(true)恢复状态并返回true
    • 如果没有可用的标记,则调用readInput()方法从输入源中读取更多的输入到缓冲区中。
  2. 处理循环结束后的状态:如果循环因为sourceClosedtrue而结束(即输入源已经关闭),则再次检查缓冲区中是否有可用的标记。

Note:有三种重载。

    /**
     * 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.
     *
     * @return true if and only if this scanner has another token
     * @throws IllegalStateException if this scanner is closed
     * @see java.util.Iterator
     */
    public boolean hasNext() {
        ensureOpen();
        saveState();
        while (!sourceClosed) {    //sourceClosed 跟踪输入源是否已经关闭
            if (hasTokenInBuffer())
                return revertState(true);
            readInput();
        }
        boolean result = hasTokenInBuffer();
        return revertState(result);
    }

    /**
     * Returns true if the next complete token matches the specified pattern.
     * A complete token is prefixed and postfixed by input that matches
     * the delimiter pattern. This method may block while waiting for input.
     * The scanner does not advance past any input.
     *
     * @param pattern the pattern to scan for
     * @return true if and only if this scanner has another token matching
     *         the specified pattern
     * @throws IllegalStateException if this scanner is closed
     */
    public boolean hasNext(Pattern pattern) {
        ensureOpen();
        if (pattern == null)
            throw new NullPointerException();
        hasNextPattern = null;
        saveState();

        while (true) {
            if (getCompleteTokenInBuffer(pattern) != null) {
                matchValid = true;
                cacheResult();
                return revertState(true);
            }
            if (needInput)
                readInput();
            else
                return revertState(false);
        }
    }

    /**
     * Returns true if the next token matches the pattern constructed from the
     * specified string. The scanner does not advance past any input.
     *
     * <p> An invocation of this method of the form <tt>hasNext(pattern)</tt>
     * behaves in exactly the same way as the invocation
     * <tt>hasNext(Pattern.compile(pattern))</tt>.
     *
     * @param pattern a string specifying the pattern to scan
     * @return true if and only if this scanner has another token matching
     *         the specified pattern
     * @throws IllegalStateException if this scanner is closed
     */
    public boolean hasNext(String pattern)  {
        return hasNext(patternCache.forName(pattern));
    }

hasNextLine

功能:判断是否还有下一行。利用正则表达式匹配来查找行分隔符,并根据查找结果决定是否返回true。

方法实现:

  1. 保存当前状态:通过saveState()方法保存当前扫描器的状态,以便在方法结束时可以恢复到这个状态。这是为了确保方法调用前后扫描器的状态不变。

  2. 查找下一行:使用findWithinHorizon(linePattern(), 0)方法尝试在输入中查找符合行模式的字符串。这里linePattern()返回用于匹配行模式的正则表达式,0表示没有超时限制。

  3. 处理匹配结果

    • 如果找到了匹配项(即result不为null),则进一步处理:
      • 通过this.match()获取匹配结果(MatchResult)。
      • 提取出匹配到的行分隔符(lineSep)。
      • 如果存在行分隔符,则从结果中去除行分隔符部分,并缓存处理后的结果。
      • 如果不存在行分隔符,则直接缓存结果(此时可能是读取到了输入的末尾,但没有明确的行分隔符)。
    • 如果没有找到匹配项,则不缓存任何结果。
  4. 恢复之前的状态:通过revertState()方法恢复扫描器到调用saveState()时的状态。

  5. 返回结果:如果找到了下一行的匹配项(即result不为null),则返回true;否则返回false

Note:只有一种类型的接口,无参。

    /**
     * Returns true if there is another line in the input of this scanner.
     * This method may block while waiting for input. The scanner does not
     * advance past any input.
     *
     * @return true if and only if this scanner has another line of input
     * @throws IllegalStateException if this scanner is closed
     */
    public boolean hasNextLine() {
        saveState();

        String result = findWithinHorizon(linePattern(), 0);
        if (result != null) {
            MatchResult mr = this.match();
            String lineSep = mr.group(1);
            if (lineSep != null) {
                result = result.substring(0, result.length() -
                                          lineSep.length());
                cacheResult(result);

            } else {
                cacheResult();
            }
        }
        revertState();
        return (result != null);
    }

结论用法

采用 hasXxxx 判断时,后面读取输入也要使用匹配的 nextXxxx 。

比如先用 hasNextLine 对控制台输入判断时,那么后续需要使用 nextLine 来处理输入。

  • hasNext 
  • hasNextXxxx 判断下一个xxx类型
  • hasNextLine 判断是否有下一行

关于 token 说明

上面有提到判断场景的token标记,那么这是什么概念和实际作用呢?

下一个标记可以先粗略理解为下一个数据(但数据类型未知)。

在Java的Scanner类中,术语“标记”(token)通常指的是输入流中可以被识别并作为一个单元处理的字符串或字符序列Scanner类使用分隔符(如空格、制表符、换行符等)来区分不同的标记。例如,当你使用Scanner从控制台读取输入时,你可以通过空格、逗号或其他分隔符来分隔不同的标记。

Scanner 的使用案例

在Java输入控制台(即标准输入流,通常是键盘输入)中,Scanner类通过以下方式实现标记的识别:

1、创建Scanner对象:new并将其与标准输入流System.in关联起来。

Scanner scanner = new Scanner(System.in);

2、设置分隔符(可选):默认情况下,Scanner使用空白(空格、制表符、换行符等)作为分隔符。但是,你可以通过调用useDelimiter(String pattern)方法来设置自定义的分隔符。

例如:逗号分隔标记

scanner.useDelimiter(",");

注意:设置分隔符会影响Scanner识别标记的方式。

3、读取标记:一旦Scanner对象被创建并配置了分隔符,你就可以使用next()nextInt()nextDouble()等方法来读取下一个标记了这些方法会返回输入流中下一个与指定类型匹配的标记。例如:

String token = scanner.next(); // 读取下一个标记作为字符串  
int number = scanner.nextInt(); // 读取下一个标记并尝试将其解析为整数

4、检查是否有下一个标记:在读取标记之前,建议使用hasNext()hasNextLine()方法先检查输入流中是否还有下一个标记或行。这些方法会返回一个布尔值,指示是否还有更多的输入可供读取。例如: 

if (scanner.hasNext()) {  
    String token = scanner.next();  
    // 处理标记  
}

5、关闭Scanner对象:完成输入操作后,应该关闭Scanner对象以释放资源。可以通过调用close()方法来实现:(虽然大部分时间没关闭也不会像数据库那样报错)。

scanner.close();


http://www.kler.cn/a/375913.html

相关文章:

  • 使用Django Channels实现WebSocket实时通信
  • Windows部署rabbitmq
  • Spring Boot技术栈:打造大学城水电管理系统
  • Linux内核与用户空间
  • Effective C++ 学习笔记二
  • 如何实现图片懒加载,原生 + React 实现方式
  • HTML 基础标签——文本内容标签 <ul>、<ol>、<blockquote> 、<code> 等标签的用法详解
  • LeetCode100之二叉树的中序遍历(94)--Java
  • go语言中协程详解
  • Java8的新特性——Stream与completableFuture详解
  • 数字隔离器与光隔离器有何不同?---腾恩科技
  • 「Mac畅玩鸿蒙与硬件18」鸿蒙UI组件篇8 - 高级动画效果与缓动控制
  • spreadjs实现类似于企业微信的协同提示
  • Linux 查看 nginx 安装目录和配置文件路径
  • LeetCode994. 腐烂的橘子(2024秋季每日一题 54)
  • 智能护栏碰撞监测终端:内蒙古高速的安全守护者
  • vite和webpack
  • 在工作中常用到的 Linux 命令总结
  • 算法实现 - 快速排序(Quick Sort) - 理解版
  • 2. 从服务器的主接口入手
  • Android 蚂蚁面经
  • 第三十三章:docker 启动mysql web管理工具- MyWebSQL
  • Hadoop:单机伪分布式部署
  • 大学城水电管理系统开发:Spring Boot指南
  • 【qwen2-1.5-instruct 好于Gemma2-2b-instruct\Llama3.2-1B-instruct】
  • 智慧税务管理:金融企业报税效率与合规性提升