verificare la disponibilità dei dati prima di chiamare std :: getline

Vorrei leggere alcuni dati da un stream che ho usato std::getline . Sotto un campione usando lo std::cin .

 std::string line; std::getline( std::cin, line ); 

Questa è una funzione di blocco, ovvero se non ci sono dati o la riga da leggere blocca l’esecuzione.

Sai se esiste una funzione per verificare la disponibilità dei dati prima di chiamare std::getline ? Non voglio bloccare.

Come posso verificare se il buffer del stream è pieno di dati validi per una chiamata riuscita a std::getline ?

Qualunque cosa assomigli al codice qui sotto

 if( dataAvailableInStream() ) { std::string line; std::getline( std::cin, line ); } 

La libreria iostream non supporta il concetto di I / O non bloccante. Non penso che ci sia qualcosa nello standard C ++ che lo fa. Qualsiasi buona soluzione sarebbe probabilmente specifica per la piattaforma. Se è ansible utilizzare le librerie POSIX, è ansible select . Solitamente è usato per fare networking, ma funzionerà bene se lo passate al descrittore di file per stdin.

Non esiste un modo standard per verificare se getline bloccherà. Puoi usare:

 std::cin.rdbuf()->in_avail() 

per vedere quanti caratteri sono definitivamente disponibili prima che un’operazione di lettura possa bloccarsi, ma dovresti leggere i caratteri uno per uno prima di ricontrollare in_avail quanto non c’è modo di sapere in anticipo se uno dei caratteri in sospeso è una nuova riga o la fine effettiva del stream. Una chiamata getline potrebbe bloccare se questo non fosse il caso.

Si noti che, sebbene in_avail() restituisca un numero in_avail() è garantito che almeno molti caratteri sono disponibili prima della fine dello stream, il contrario non è vero. Se in_avail() restituisce zero, potrebbero ancora essere disponibili caratteri e il stream potrebbe non bloccarsi immediatamente.

Questo codice può aiutarti a verificare l’esistenza dei dati nello stdin senza bloccare:

 std::cin.seekg(0, std::cin.end); int length = std::cin.tellg(); if (length < 0) return; //- no chars available 

Se stdin ha alcuni dati, non dimenticare di riportare la posizione all'inizio.

 std::cin.seekg(0, std::cin.beg); 

Puoi quindi leggere tutti i dati incluso \0 (può essere più di uno) alla fine del buffer:

 std::vector s(length); std::cin.read(s.data(), length); 

o linea per linea:

 std::string line; while (std::cin) { std::getline(std::cin, line); //..... } 

Questo codice funziona in MSVC e gcc (Ubuntu)

Un trucco potrebbe essere chiamare kbhit () prima della lettura. Probabilmente non portatile e irto di pericoli …

 #include  #include  using namespace std; char buffer[128]; if (kbhit()) { cin.getline(buffer, sizeof(buffer)); } 

Anche se la risposta di peek() di Nathan vedrà se ci sono dati, non vi è alcuna garanzia che std::getline() avrà successo nella lettura di una “linea”.

È sempre molto più facile, anche se un po ‘indietro, provare la linea e controllare il risultato della chiamata alla funzione stessa:

 std::string line; while( !std::getline(std::cin, line) ) { cout << "Enter something please" << endl; } 

Questo codice verrà eseguito fino a quando cin riceve qualcosa che gli piace (cioè può estrarre e posizionare in line ). Non vedo che peek() sia necessario o utile qui.

EDIT: Il fatto è che cin (== input della tastiera standard) dovrà bloccare il programma mentre attende input, in quale altro modo può ricevere input quando non aspetterà?

Che problema stai cercando di risolvere evitando il blocco della lettura qui?

Non portabile Immagino che potresti usare il poll o select per vedere se ci sono dati da leggere su stdin (spesso fd 0 su sistemi unix).

In alternativa è ansible creare un secondo thread per eseguire I / O e lasciarlo bloccare in modo da poter continuare la normale elaborazione nel thread principale.

std :: iostream fornisce una funzione di sbirciata che restituisce il carattere successivo nello stream senza rimuoverlo. Quindi potresti fare qualcosa come il seguente (totalmente non testato).

 bool dataAvailableInStream( std::iostream &stream ) { return stream.peek() != std::iostream::traits_type::eof(); } 

modificare

Come sottolinea Rubenvb, std::cin blocks by design. Quindi il codice sopra ti farà superare il blocco della linea, ma non cin .

Modifica Modifica

Come sottolinea Charles di seguito, peek blocchi se non ci sono dati disponibili. Pertanto questo non fornisce una soluzione completa. Ti getline bloccare su getline , ma non di bloccare in generale.