2015-03-24 5 views
20

документация Javadoc для Scanner.hasNextLine() состояний:При каких условиях блок Java Scanner.hasNextLine()?

Возвращает истину, если есть еще одна линия на входе этого сканера. Этот метод май блок в ожидании ввода. Сканер не продвигается мимо каких-либо входных данных.

При каких условиях будет блокироваться метод?

ответ

20

Это зависит от источника, из которого сканер получает вход.

Например, если это файл, весь вход доступен, так hasNextLine() не будет блокировать (поскольку он может точно знать, когда конец файла достигнут, и там больше нет ввода.

На с другой стороны, если источник является стандартным входом, всегда может быть больше ввода - пользователь может всегда вводить больше ввода - поэтому hasNextLine() будет блокироваться до тех пор, пока пользователь не будет вводить новую строку ввода.

+2

Это хороший ответ и объяснение того, почему это работает как это происходит. Но это как бы позволяет избежать актуального вопроса. – aioobe

+6

@aioobe Я не думаю, что это избегает вопроса. Когда вы создаете экземпляр Scanner, вы знаете, из какого источника он получает вход, поэтому вы знаете, будет ли он блокироваться или нет. Если вам предоставлен экземпляр Scanner, не зная его источник ввода, вы не можете знать, будет ли он блокироваться. – Eran

+0

@aloobe: Вы удалили свой ответ так быстро, что я не мог опубликовать этот комментарий для комментариев здесь, но я хотел бы отметить, что ваша общая идея кажется звуковой - нам нужен только лучший класс-оболочка, который фактически использовал ['InputStream. available() '] (http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#available%28%29), чтобы проверить, сколько символов он может заглянуть вперед, не блокируя. (Конечно, это будет работать только на практике, если поток, который вы читаете, фактически реализует полезный метод 'available()', реализация по умолчанию всегда возвращает ноль.) –

2

Предполагая, что «решить если он будет блокировать «, вы имеете в виду, что хотите знать, когда он будет работать.

Посмотрите, где вход назначен в методе hasNextLine

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

Теперь посмотрим на findWithinHorizon метод

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

Как вы можете видеть, это будет цикл бесконечно до конца или до тех пор, пока ему не удастся его прочитать.

findPatternInBuffer - частный метод класса Scanner, который пытается прочитать ввод.

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

Я отправил весь метод, чтобы дать вам лучшее представление о том, что я имел в виду «удастся прочитать».

+0

После прочтения кода, вот мое понимание 'hasNextLine()'. Сканер имеет внутренний буфер для хранения данных, считываемых с источника входного сигнала. И шаблон строки используется для поиска соответствия в этом буфере. Если совпадение не найдено, и можно считывать больше данных (т.needInput = true), сканер будет продолжать чтение из источника ввода и подавать свой внутренний буфер. Это метод 'readInput()' на разных входных источниках может блокироваться или не блокироваться. – smwikipedia

+0

@ Jean-FrançoisSavard, * документация * является нормативной спецификацией для стандартного API. Вы не можете указать код * одной конкретной реализации и сказать: «Посмотрите здесь, вот как это работает». Я мог бы запускать GNU classpath, или apache-гармонию или что-то еще. – aioobe

+0

@aioobe Я считаю, что если только java помечен, мы можем предположить, что OP ссылается на стандартный API? –

4

Как решить, будет ли он блокироваться?

Чтобы решить, заблокирован ли hasNextLine, к сожалению, не поддерживается.

Это связано с тем, что базовые источники не всегда предоставляют API для поиска в потоке. Иными словами, реализация hasNextLine вызывает методы, которые сами могут блокировать, поэтому проблема является своего рода неотъемлемой.


Итак, что делать?

Если это действительно необходимый случай использования, я рекомендовал бы один из следующих подходов:

  • Убедитесь, что условия являются правильными для hasNextLine. Предоставляйте сканеру только те источники, которые имеют определенный конец (например, файл или строку) и никогда не имеют «открытых» входных данных, таких как System.in.

    Если это часть API, вы можете обернуть сканер в своем классе, который предоставляет только «безопасные» конструкторы.

  • Сверните свой собственный класс с нуля, который имеет тип метода willHasNextLineBlock. Это могло бы возможно быть реализовано несколько прочно используя InputStream.available.


В категории супер уродливые обходные пути мы находим:

  • Создание попытку вызова hasNextLine в отдельном потоке и посмотреть, если он возвращается в течение разумного периода времени, а именно:

    boolean wouldBlock = false; 
    Thread t = new Thread(() -> s.hasNextLine()); 
    t.start(); 
    try { 
        t.join(100); 
    } catch (InterruptedException e) { 
        wouldBlock = true; 
    } 
    
  • Использовать собственный входной поток (вещь как peekable stream, которую можно было бы использовать, прежде чем звонить hasNextLine. Что-то вроде этого

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

    (Обратите внимание на то, что это несколько небезопасно, так как сканер может быть буферном некоторые данные из CustomStream.)

+2

Действительно ли безопасно прерывать произвольный код рамки? Это не может быть ожесточено от прерывания. Это может привести к повреждению внутреннего состояния. – usr

+0

К сожалению, решение, которое вы предлагаете, не будет работать, по крайней мере, не используя ссылку PeekableInputStream [реализация] (http://www.heatonresearch.com/articles/147/page2.html), на которую вы ссылаетесь; его метод 'peek()' вызывает 'InputStream.read()', который может (и действительно, явно документирован) блокироваться до тех пор, пока запрошенные данные не будут доступны, EOF достигнут или произойдет ошибка. –

+0

Ответ переписан. – aioobe

Смежные вопросы