2016-04-19 28 views
8

Przeczytałem około 5-10 różnych porad, jak wyczyścić stdin, ale żaden z nich nie pasuje do moich potrzeb. Rzecz w tym, że fflush(stdin) działało idealnie na moim komputerze, ale niestety nie działa wszędzie, więc potrzebuję czegoś z tą samą funkcjonalnością. Każdy inny sposób, w jaki próbowałem, usuwa stdin, gdy nie jest pusty, ale wymaga podania danych przez użytkownika, gdy stdin IS jest pusty, co oznacza, że ​​wymaga on wprowadzenia w momencie, którego nie chcę uzyskać (+ odrzuca to mimo to).Jak wyczyścić wejście standardowe przed uzyskaniem nowego wejścia?

Pytanie brzmi: czy mogę w jakiś sposób upewnić się, że stdin jest pusty, zanim wymagam wprowadzenia użytkownika? (A jeśli nie, wtedy i tylko wtedy jasne, że jakoś?) coś takiego:

if (stdin is NOT empty) 
    while (getchar() != '\n') 
     continue; 

EDIT: chodzi o to, że ja załadować znaki z stdin jeden po drugim, a w pewnym momencie część wkładu ze poprzednia iteracja może zostać odrzucona lub nie. tak czy inaczej, potrzebuję mieć jasne stdin zanim poprosimy użytkownika o kolejne przetwarzanie danych wejściowych. Kasowanie samego bufora nie jest tak wielką sprawą, problemem jest to, co dzieje się, gdy dane wejściowe są puste, gdy program osiągnie punkt zerowania stdin, ponieważ w tym momencie program potrzebuje innego wejścia, które zostanie zjedzone przez rozliczenie funkcjonować. To jest to, czego chcę się pozbyć. (Jeśli mogę użyć fflush(stdin); po prostu wiedziałem, że do następnego wiersza mojego programie stdin będzie pusta bez względu na to, bez zbędnych pytań ...)

+2

'fflush (stdin)' jest UB. –

+1

Nie ma czegoś takiego jak * pusty strumień I/O *, jest to ** strumień **. Rozważ: './myprog joop

+0

programowanie go w systemie Windows, musi działać w systemie Linux ... – Tom

Odpowiedz

1

TL; DRfflush(stdin) wywołuje undefined behavior zgodnie z normą , nigdy nie powinieneś go używać.


Jadąc do kodu (logiczne), zamiast szukać nowej linii, można spojrzeć na EOF. Nie ma warunku, aby przed uruchomieniem tej pętli należało wprowadzić jakieś .

Coś

while (getchar() != EOF); //; is not a mistake 

powinna spełniać swoje potrzeby.

+0

podczas (getchar()! = EOF); ustawia mnie w nieskończonej pętli wymagającej innego i innego wejścia. Próbowałem również if (! Feof (stdin)) itp. Ale nic z tego nie działało. Wygląda na to, że stdin nie ma żadnego zbioru EOF, wymaga jedynie innego wejścia po osiągnięciu końca. – Tom

+1

Przynajmniej w Windowsach musisz jawnie wygenerować EOF dla stdio za pomocą Ctrl + Z. – HolyBlackCat

+1

Er, no. Twoja pętla czeka, aż użytkownik wygeneruje EOF ('^ D' lub'^Z'), podczas gdy OP poprosił o sposób na opróżnienie bufora wejściowego. – jch

1

Użyj tylkofgets() do odczytu stdin.

Użyj wystarczająco dużego bufora i/lub testu dla pełnych linii.

Korzystanie z fgets() nigdy nie musisz się martwić o dodatkowe znaki w stdin.

// read characters until 'X' 
while (((ch = getchar()) != EOF) && (ch != 'X')) putchar(ch); 
// discard X and the rest of the line 
fflush(stdin); // UB except for Windows 

// read full line 
char tmp[1000], *p; 
if (!fgets(tmp, sizeof tmp, stdin)) /* deal with error */; 
if (!*tmp) /* embedded NUL detected: input is not a text file */; 
if (tmp[strlen(tmp) - 1] != '\n') /* partial line */; 
p = tmp; 
while (*p && *p != 'X') putchar(*p++); 
// ignore the X and all the subsequent characters 
+0

'if (tmp [strlen (tmp) - 1]! = '\ N')' jest UB jeśli 'tmp [0] == 0'. Wystarczająco łatwe, aby pierwszy znak został odczytany przez 'fgets()', aby był znakiem pustym. – chux

+0

@chux: prawy, ale nie w ** plikach tekstowych. ** Pliki tekstowe ** ('stdin', klawiatura, ...) nie mają osadzonych NUL-ów. Kod został zmieniony, aby w jakikolwiek sposób sobie z tym poradzić. Dzięki – pmg

+0

Prawda, to nie jest tak, że pierwszy znak jest pusty. Definicja _text file_ nie jest formalna i często nie wyklucza wyraźnie '\ 0''. Jednak nie jest też rzadkością odczytywanie pliku tekstowego UTF-16BE ** text, który nie zawiera pliku BOM (myśląc, że jest to prosty ASCII), który może łatwo mieć wiodącą pustą literę. Jeśli klawiatura może generować znak null, jest to problem na poziomie platformy, poza kontrolą programu. IMO to exploit hakerski, a odporny kod radzi sobie. zagłosuj na lepszą odpowiedź. – chux

0

Moduł select oferuje funkcję o nazwie select że osiąga dokładnie to, czego szukasz. select.select przyjmuje trzy argumenty:

select.select(rlist, wlist, xlist) 

Każdy argument powinien być lista deskryptorów plików (takich jak [sys.sdtin]) i następnie czeka aż konkretnej operacji IO jest dostępny.Operacje we/wy są wykonywane na niektórych deskryptorach plików, w zależności od rytuału lub innej procedury e x. Zwraca ona tuple odpowiednich list zapełnionych deskryptorami plików, które są gotowe.

Tak więc, jeśli istnieje wejście czeka w sys.stdin wówczas funkcja będzie zachowywać się tak:

>>> import select 
>>> import sys 
>>> 
>>> select.select([sys.stdin], [], []) 
([sys.stdin], [], []) 
>>> 

Sama to nie rozwiąże problemu, ponieważ domyślnie funkcja będzie czekać, aż operacja jest IO dostępny. Co ważne jednak, select.select ma opcjonalny argument timeout określający, jak długo będzie czekać, zanim zrezygnuje. Musimy po prostu ustawić limit czasu na zero i możemy sprawdzić wejście bez blokowania przepływu programu.

Zobaczmy przykład, w którym nie ma wejście czeka w sys.stdin:

>>> import select 
>>> import sys 
>>> 
>>> timeout = 0 
>>> select.select([sys.stdin], [], [], timeout) 
([], [], []) 
>>> 

Wiedząc, że chcemy tylko pierwszy element tego krotki (strumieni wejściowych) jesteśmy gotowi uczynić użytecznym if :

if sys.stdin in select.select([sys.stdin], [], [], 0)[0]: 
    print('Input is waiting to be read.') 

oznacza to wyczyszczenie strumień wejściowy po prostu potrzebuje trochę iteracji:

while sys.stdin in select.select([sys.stdin], [], [], 0)[0]: 
    sys.stdin.readline() 

Możemy oczywiście użyć tego na każdym strumieniu wejściowym, więc pozwala umieścić go w funkcji:

def clear_input(stream, timeout=0): 
    '''Takes an input stream and discards each line in the buffer. 
    The given timeout denotes how long in seconds to wait for 
    further input when none is available. 
    ''' 
    while stream in select.select([stream], [], [], timeout)[0]: 
     stream.readline() 

Warto więc wykazać naszą funkcję, aby osiągnąć to, czego poprosić w swoim pytaniu:

import select 
import sys 
import time 

def clear_input(stream, timeout=0): 
    while stream in select.select([stream], [], [], timeout)[0]: 
     stream.readline() 

if __name__ == '__main__': 
    print('Type some lines now. They will be ignored.') 
    time.sleep(5) 

    print('Clearing input.') 
    clear_input(sys.stdin) 

    user_input = raw_input('Please give some fresh input: ') 
    print(user_input) 

Funkcja clear_input może być używana jako nieblokujący sposób do czyszczenia strumieni wejściowych i powinna działać w Python2 i Python3.

+3

Ale ty * zauważyłeś *, że to jest pytanie C, a nie pyton? – Jens

+0

@ jens/facepalm :-) – dsclose

+0

Świetna odpowiedź. Po prostu potrzebuje innego pytania. –

2

Jak wyczyścić wejście standardowe przed uzyskaniem nowego wejścia?
.. więc potrzebuję czegoś z tą samą funkcjonalnością.

Z przenośnym C nie jest to możliwe.


Zamiast zaproponować inną (zwykle bardziej C) paradygmat: Upewnić poprzednich funkcji wejścia zużywają wszystkie poprzedniego wejściowego.

fgets() (lub * nix getline()) to typowe podejście i rozwiązuje większość sytuacji.

Lub zrób własne. Następujące zużywa, ale nie zapisuje dodatkowych danych wejściowych.

int mygetline(char *buf, size_t size) { 
    assert(size > 0 && size < INT_MAX); 
    size_t i = 0; 
    int ch; 
    while ((ch = fgetc(stdin)) != EOF) { 
    if (i + 1 < size) { 
     buf[i++] = ch; 
    } 
    if (ch == '\n') { 
     break; 
    } 
    } 
    buf[i] = '\0'; 
    if (i == 0) { 
    return EOF; 
    } 
    return i; 
}