java中当输入行数未知时,读取命令行输入的解决方法

今天帮同学做笔试题,在scanner的使用中遇到一个小坑,都怪自己学艺不精,赶紧来记录一下

问题来源

在线笔试题中,有时会给定多行输入,但是行数是未知的,这时该如何解决这一问题呢

容易想到但错误的解决方案

import java.util.Scanner;

public class test {
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        String s;
        while (sc.hasNext()){
            System.out.println("loop");
            if ((s = sc.nextLine()).equals(""))
                break;
        }

    }
}

以上代码试图通过hasNext()方法解决这一问题,问题似乎是解决了,当没有输入的时候自动退出while不是很好吗?我们来看一下运行结果
在这里插入图片描述
注意看命令行中光标的位置,这是我点击了多次回车后的效果,可以看出,程序丝毫没有要退出while循环的迹象。

问题追踪

在这里插入图片描述
我们来看一下hasNext()方法的源码,注释中写的很清楚,这一方法很可能造成线程阻塞,也就是说如果方法发现缓冲区中没有数据,它就会一直等下去。观察源码,方法中第三行有一个while,聪明的同学可能会想,我们把sourceClosed改了,把数据源关闭是不是就可以了,答案也是否定的,因为线程在这个方法中已经阻塞了,没有多余的精力去分析现状并关闭数据源。
那到底该怎么解决这一问题呢?
往下看

解决方案

先放代码

import java.util.Scanner;

public class test {
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        String s;
        while (true){
            System.out.println("loop");
            if ((s = sc.nextLine()).equals(""))
                break;
        }

    }
}

这一次就可以了,因为当线程不会在hasNext方法中阻塞了,一旦出现空行,我们可以自行读取,并退出循环。

那这样不判断hasNext直接读取数据,会不会造成异常呢?
我们来看下源码是怎么实现的

 /**
     * 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;
    }

源码中写了
if (result == null)
throw new NoSuchElementException(“No line found”);
那难道不会造成异常吗?
我们看到这一行的上面调用了findWithinHorizon方法,看看这一方法是怎么实现的吧?

   /**
     * Attempts to find the next occurrence of the specified pattern.
     *
     * <p>This method searches through the input up to the specified
     * search horizon, ignoring delimiters. If the pattern is found the
     * scanner advances past the input that matched and returns the string
     * that matched the pattern. If no such pattern is detected then the
     * null is returned and the scanner's position remains unchanged. This
     * method may block waiting for input that matches the pattern.
     *
     * <p>A scanner will never search more than <code>horizon</code> code
     * points beyond its current position. Note that a match may be clipped
     * by the horizon; that is, an arbitrary match result may have been
     * different if the horizon had been larger. The scanner treats the
     * horizon as a transparent, non-anchoring bound (see {@link
     * Matcher#useTransparentBounds} and {@link Matcher#useAnchoringBounds}).
     *
     * <p>If horizon is <code>0</code>, then the horizon is ignored and
     * this method continues to search through the input looking for the
     * specified pattern without bound. In this case it may buffer all of
     * the input searching for the pattern.
     *
     * <p>If horizon is negative, then an IllegalArgumentException is
     * thrown.
     *
     * @param pattern the pattern to scan for
     * @param horizon the search horizon
     * @return the text that matched the specified pattern
     * @throws IllegalStateException if this scanner is closed
     * @throws IllegalArgumentException if horizon is negative
     */
    public String findWithinHorizon(Pattern pattern, int horizon) {
        ensureOpen();
        if (pattern == null)
            throw new NullPointerException();
        if (horizon < 0)
            throw new IllegalArgumentException("horizon < 0");
        clearCaches();

        // Search for the pattern
        while (true) {
            String token = findPatternInBuffer(pattern, horizon);
            if (token != null) {
                matchValid = true;
                return token;
            }
            if (needInput)
                readInput();
            else
                break; // up to end of input
        }
        return null;
    }

可以看到,这一方法的实现中用了一个while(true)来读取缓冲区,如果读到数据才返回,所以直接读不会造成异常啦~

总结

采用本文中介绍的方法读取多行时,因为hasNext()方法仅仅将回车符作为结束符,所以当我们如果想停止输入,在最后一行直接点击回车就好了,这时nextLine()方法会返回一个空字符串(就是""),程序就会通过break退出循环了。

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值