Under what conditions does Java's Scanner.hasNextLine() block?

前端 未结 3 1073
伪装坚强ぢ
伪装坚强ぢ 2021-02-05 07:19

The javadoc for Scanner.hasNextLine() states:

Returns true if there is another line in the input of this scanner. This method m

相关标签:
3条回答
  • 2021-02-05 07:57

    How to decide if it will block?

    To decide if hasNextLine will block or not is unfortunately not a supported use case.

    This is because the underlying sources doesn't always provide an API for peeking in the stream. Put differently, the implementation of hasNextLine calls methods that themselves may block so the problem is sort of inherent.


    So, what to do?

    If this is indeed a required use case, I would recommend one of the following approaches:

    • Make sure the conditions are right for the hasNextLine. Only provide the scanner with sources that have a definite end (such as a file or string) and never an "open ended" input such as System.in.

      If this is part of an API you could wrap the scanner in your own class that only exposes "safe" constructors.

    • Roll your own class from scratch that does have a willHasNextLineBlock type of method. This could probably be implemented somewhat robustly using InputStream.available.


    Under the category of super ugly workarounds we find:

    • Making an attempt at calling hasNextLine in a separate thread and see if it returns within reasonable time, as follows:

      boolean wouldBlock = false;
      Thread t = new Thread(() -> s.hasNextLine());
      t.start();
      try {
          t.join(100);
      } catch (InterruptedException e) {
          wouldBlock = true;
      }
      
    • Use a custom input stream (something like a peekable stream that one could tap into before calling hasNextLine. Something like the this

      CustomStream wrapped = new CustomStream(originalSource)
      Scanner s = new Scanner(wrapped);
      ...
      if (wrapped.hasNextLine())
          // s.hasNextLine would not block
      else
          // s.hasNextLine would block
      

      (Note however that this is somewhat unsafe, since the scanner may have buffered some data from the CustomStream.)

    0 讨论(0)
  • 2021-02-05 07:57

    Assuming by "decide if it will block" you mean that you want to know when it will bock.

    Have a look at where the input is assigned in the hasNextLine method

    String result = findWithinHorizon(linePattern(), 0);
    

    Now, have a look at the findWithinHorizon method

    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) { //it may block here if it never break
            String token = findPatternInBuffer(pattern, horizon);
            if (token != null) {
                matchValid = true;
                return token;
            }
            if (needInput)
                readInput();
            else
                break; // up to end of input
        }
        return null;
    }
    

    As you can see, it will loop infinitely until the end is reached, or until it succeed to read.

    findPatternInBuffer is a private method of the Scanner class that try to read the input.

    private String findPatternInBuffer(Pattern pattern, int horizon) {
        matchValid = false;
        matcher.usePattern(pattern);
        int bufferLimit = buf.limit();
        int horizonLimit = -1;
        int searchLimit = bufferLimit;
        if (horizon > 0) {
            horizonLimit = position + horizon;
            if (horizonLimit < bufferLimit)
                searchLimit = horizonLimit;
        }
        matcher.region(position, searchLimit);
        if (matcher.find()) {
            if (matcher.hitEnd() && (!sourceClosed)) {
                // The match may be longer if didn't hit horizon or real end
                if (searchLimit != horizonLimit) {
                     // Hit an artificial end; try to extend the match
                    needInput = true;
                    return null;
                }
                // The match could go away depending on what is next
                if ((searchLimit == horizonLimit) && matcher.requireEnd()) {
                    // Rare case: we hit the end of input and it happens
                    // that it is at the horizon and the end of input is
                    // required for the match.
                    needInput = true;
                    return null;
                }
            }
            // Did not hit end, or hit real end, or hit horizon
            position = matcher.end();
            return matcher.group();
        }
    
        if (sourceClosed)
            return null;
    
        // If there is no specified horizon, or if we have not searched
        // to the specified horizon yet, get more input
        if ((horizon == 0) || (searchLimit != horizonLimit))
            needInput = true;
        return null;
    }
    

    I posted the whole method to give you a better idea of what I meant by "succeed to read".

    0 讨论(0)
  • 2021-02-05 08:03

    It depends on the source that the Scanner gets the input from.

    For example, if it's a file, the entire input is available, so hasNextLine() wouldn't block (since it can know with certainty when the end of the file is reached and there's no more input.

    On the other hand, if the source is standard input, there can always be more input - the user can always type more input - so hasNextLine() would block until the user types in a new line of input.

    0 讨论(0)
提交回复
热议问题