2015-03-24 12 views

risposta

20

Dipende dalla sorgente da cui lo scanner riceve l'input.

Ad esempio, se si tratta di un file, l'intero input è disponibile, così hasNextLine() non sarebbe bloccare (poiché può sapere con certezza quando viene raggiunta la fine del file e non c'è alcun input.

On D'altra parte, se la sorgente è un input standard, può sempre esserci più input - l'utente può sempre digitare più input - quindi hasNextLine() bloccherebbe fino a quando l'utente digita una nuova riga di input.

+2

Questa è una buona risposta e la spiegazione del perché funziona come fa Ma evita la vera domanda. – aioobe

+6

@aioobe Non credo che eviti la domanda. Quando crei un'istanza di Scanner, sai da quale fonte riceve l'input, quindi sai se potrebbe bloccare o meno. Se ti viene data un'istanza di Scanner senza conoscere la sua fonte di input, non puoi sapere se bloccherà. – Eran

+0

@aloobe: hai cancellato la tua risposta così velocemente che non ho potuto postare questo commento di follow-up qui, ma volevo notare che la tua idea generale sembra valida - avremmo solo bisogno di una migliore classe wrapper che effettivamente usasse ['InputStream. disponibile() '] (http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#available%28%29) per verificare quanti caratteri può guardare avanti senza bloccare. (Naturalmente, ciò funzionerà in pratica solo se lo stream che stai leggendo implementa effettivamente un utile metodo 'available()', l'implementazione predefinita restituisce sempre zero.) –

2

Supponendo di se bloccherà " vuoi dire che vuoi sapere quando buggera '

Dai un'occhiata a dove l'ingresso è assegnato nel metodo hasNextLine

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

Ora, date un'occhiata al findWithinHorizon metodo

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

Come si può vedere, è in loop all'infinito fino alla fine viene raggiunto, o fino a quando non riesce a leggere.

findPatternInBuffer è un metodo privato della classe Scanner che tenta di leggere l'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; 
} 

ho postato l'intero metodo per darvi una migliore idea di quello che volevo dire da "riescono a leggere".

+0

Dopo aver letto il codice, ecco la mia comprensione di 'hasNextLine()'. Lo scanner ha un buffer interno per contenere i dati letti dalla sorgente di input. E un modello di linea è usato per trovare una corrispondenza in questo buffer. Se una corrispondenza non viene trovata e altri dati possono essere letti (ad es.needInput = true), Scanner continuerà a leggere dalla sorgente di input e ad alimentare il suo buffer interno. È il metodo 'readInput()' su diverse sorgenti di input può o non può bloccare. – smwikipedia

+0

@ Jean-FrançoisSavard, la * documentazione * è la specifica normativa per l'API standard. Non è possibile indicare il codice di * una * implementazione specifica e dire "guarda qui, è così che funziona". Potrei essere in esecuzione GNU classpath, o l'armonia Apache o qualsiasi altra cosa. – aioobe

+0

@aioobe Credo che se solo java è taggato possiamo supporre che OP si riferisca all'API standard? –

4

Come decidere se bloccherà?

Per decidere se bloccare o meno hasNextLine non è un caso di utilizzo supportato.

Questo perché le origini sottostanti non forniscono sempre un'API per sbirciare nello stream. In altre parole, l'implementazione di hasNextLine chiama metodi che possono essere bloccati da soli, quindi il problema è in qualche modo inerente.


Quindi, cosa fare?

Se questo è davvero un caso d'uso richiesto, mi sento di raccomandare uno dei seguenti approcci:

  • Assicurarsi che ci sono le condizioni per l'hasNextLine. Fornire allo scanner solo sorgenti con una fine definita (come un file o una stringa) e mai un input "aperto" come System.in.

    Se questo fa parte di un'API, è possibile avvolgere lo scanner nella propria classe che espone solo costruttori "sicuri".

  • Arrotolare la propria classe da zero che ha un tipo di metodo willHasNextLineBlock. Questo potrebbe probabilmente essere implementato in modo piuttosto robusto utilizzando InputStream.available.


Sotto la categoria di super brutti soluzioni troviamo:

  • Fare un tentativo di chiamare hasNextLine in un thread separato e vedere se restituisce entro un tempo ragionevole, come segue:

    boolean wouldBlock = false; 
    Thread t = new Thread(() -> s.hasNextLine()); 
    t.start(); 
    try { 
        t.join(100); 
    } catch (InterruptedException e) { 
        wouldBlock = true; 
    } 
    
  • Utilizzare un flusso di input personalizzato (cosa come uno peekable stream che si potrebbe attingere prima di chiamare hasNextLine. Qualcosa di simile al questo

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

    (Si noti, tuttavia, che questo è un po pericoloso, dal momento che lo scanner può aver tamponato alcuni dati dal CustomStream.)

+2

È davvero sicuro interrompere il codice quadro arbitrario? Potrebbe non essere indurito contro l'interruzione. Ciò potrebbe causare il danneggiamento dello stato interno. – usr

+0

Sfortunatamente, la soluzione che suggerisci non funzionerà, almeno non usando PeekableInputStream [implementazione] (http://www.heatonresearch.com/articles/147/page2.html) a cui ti colleghi; il suo metodo 'peek()' chiama 'InputStream.read()', che può (e in effetti è esplicitamente documentato) bloccare fino a quando i dati richiesti sono disponibili, EOF viene raggiunto o si verifica un errore. –

+0

Risposta riscritta. – aioobe

Problemi correlati