2016-12-19 21 views
7

Jak na tytuł Próbuję zrozumieć dokładnie zachowanie Ctrl + D/Ctrl + Z w pętli while with a gets (którego muszę użyć). Kod I 'm testowania jest następująca:Different zachowanie Ctrl-D (Unix) i Ctrl-Z (Windows)

#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 

    char str[80]; 

    while(printf("Insert string: ") && gets(str) != NULL) { 

     puts(str); 
    } 

    return 0; 
} 

Jeśli moje wejście jest po prostu Ctrl + D (lub Ctrl + Z Windows) gets zwraca NULL a program wychodzi poprawnie. Niejasna sytuacja występuje, gdy wstawiam coś w stylu: house^D^D (Unix) lub house^Z^Z\n (Windows).

  1. W pierwszym przypadku moja interpretacja jest getchar (lub coś podobnego wewnątrz funkcji gets) czeka na read(), aby uzyskać dane wejściowe, pierwszy Ctrl + D opróżnia bufor, który nie jest pusty (stąd nie EOF), a następnie drugi odczyt() zwany jest EOF.
  2. W drugim przypadku zauważyłem, że pierwszy Ctrl + Z jest wstawiany do bufora, podczas gdy wszystko, co następuje, jest po prostu ignorowane. Stąd moje rozumienie jest pierwszym wywołaniem read() wstawionym house^Z i odrzucił wszystko inne, zwracając 5 (liczba odczytanych znaków). (Mówię 5, ponieważ w przeciwnym razie myślę, że proste powinno być zwrócenie 1 bez wywoływania EOF). Następnie program czeka na więcej danych wejściowych od użytkownika, stąd drugie wywołanie read().

Chciałbym wiedzieć, co mi się dobrze i źle, jak to działa i która część jest po prostu zależna od wykonania, jeśli w ogóle.


Ponadto zauważyłem, że zarówno w Unix i Windows, nawet po EOF jest wyzwalany wydaje się, aby przywrócić false w następujący gets() wezwanie i nie rozumiem, dlaczego tak się dzieje iw której linii kodu.

Byłbym wdzięczny za jakąkolwiek pomoc.


(12/20/2016) I mocno edytowany moje pytanie, w celu uniknięcia nieporozumień

+0

to wyrażenie: '|| ! feof (stdin)) 'nie ma żadnego użytecznego efektu. Głównie dlatego, że funkcja: 'feof()' sprawdza, czy kod próbował odczytać przeszłość EOF, A to wyrażenie jest sprawdzane tylko wtedy, gdy napotkano EOF, więc zawsze będzie fałszywe. Ponieważ ta funkcja NIE wykonuje tego, co jest (zwykle) spodziewane, zdecydowanie sugeruje, że nigdy jej nie użyje. – user3629249

+0

to wyrażenie: '* curr ++' can/będzie problemem. Problem jest z powodu pierwszeństwa operatorów w C. Zaproponuj: '* curr = (char) c; curr ++; ' – user3629249

+0

Użyłem! feof (stdin), aby sprawdzić, czy napotkałem błąd odczytu zamiast eof. Próbowałem tylko emulować funkcję get (...), aby lepiej wyizolować źródło moich wątpliwości (co jest getchar()) –

Odpowiedz

1

CTRL-D i CTRL-Z "koniec pliku" wskaźniki służą podobnemu celowi na Unix i Windows systemów, ale są wdrażane zupełnie inaczej.

W systemach Unix (w tym klonach Unix, takich jak Linux) CTRL-D, oficjalnie opisany jako znak końca pliku, jest w rzeczywistości znakiem ograniczającym. Działa to prawie tak samo, jak znak końca wiersza (zwykle powrót karetki lub CTRL-M), który służy do ograniczania linii. Obie postacie informują system operacyjny, że linia wejściowa została zakończona i aby udostępnić program. Jedyna różnica polega na tym, że w przypadku znaku końca wiersza znak końca linii (CTRL-J) jest wstawiany na końcu bufora wejściowego, aby oznaczyć koniec linii, natomiast znak końca pliku nic nie jest wstawiane .

Oznacza to, że po wpisaniu house^D^D na systemie Unix, wywołanie systemowe najpierw zwróci bufor o długości 5 z 5 znakami house. Po ponownym wywołaniu read w celu uzyskania większej ilości danych wejściowych, powróci on do bufora o długości 0 bez żadnych znaków. Ponieważ odczyt zerowej długości zwykłego pliku wskazuje, że koniec pliku został osiągnięty, funkcja biblioteczna gets interpretuje to również jako koniec pliku i przestaje czytać dane wejściowe. Jednak ponieważ wypełnił bufor 5 znakami, nie zwraca wartości NULL, aby wskazać, że osiągnął koniec pliku. A ponieważ faktycznie nie dotarł on do końca pliku, ponieważ urządzenia końcowe nie są w rzeczywistości plikami, dalsze wywołania do gets będą następnie wywoływać kolejne wywołania read, które zwrócą wszystkie kolejne znaki wpisane przez użytkownika.

W systemie Windows CTRL-Z jest obsługiwany w różny sposób. Największą różnicą jest to, że nie jest on w ogóle traktowany przez system operacyjny. Po wpisaniu house^Z^Z^M w systemie Windows tylko znak powrotu karetki jest traktowany w specjalny sposób. Podobnie jak w systemie Unix, powrót karetki sprawia, że ​​linia wpisana jest dostępna dla programu, ale w tym przypadku powrót karetki i linia są dodawane do bufora, aby oznaczyć koniec linii. W rezultacie funkcja ReadFile zwraca 9-bajtowy bufor z 9 znakami house^Z^Z^M^J w nim.

To właściwie sam program, w szczególności biblioteka uruchomieniowa C, która specjalnie traktuje CTRL-Z. W przypadku biblioteki środowiska wykonawczego Microsoft C, gdy widzi znak CTRL-Z w buforze zwróconym przez ReadFile, traktuje go jako znacznik końca pliku i ignoruje wszystko inne po nim. Korzystając z przykładu z poprzedniego akapitu, gets kończy wywoływanie ReadFile, aby uzyskać więcej danych wejściowych, ponieważ fakt, że widział znak CTRL-Z nie jest pamiętany podczas odczytu z konsoli (lub innego urządzenia) i jeszcze nie widział końca -of-line (które zostało zignorowane). Jeśli ponownie naciśniesz enter, gets powróci z buforem wypełnionym 7 bajtami house^Z\0 (dodając 0 bajtów, aby wskazać koniec napisu). Domyślnie robi to samo podczas odczytu z normalnych plików, jeśli w pliku pojawia się znak CTRL-Z, to i wszystko po nim jest ignorowane. Ma to na celu kompatybilność wsteczną z CP/M, która obsługuje tylko pliki o długościach wielokrotności 128 i używa CTRL-Z do zaznaczania, gdzie pliki tekstowe miały się kończyć.

Należy zauważyć, że opisane powyżej zachowania zarówno w systemie Unix, jak i w Windows są normalną domyślną operacją wprowadzania danych przez użytkownika. Uniksowa obsługa CTRL-D występuje tylko podczas odczytu z urządzenia terminalowego w trybie kanonicznym i istnieje możliwość zmiany znaku "końca pliku" na coś innego. W systemie Windows system operacyjny nigdy specjalnie nie traktuje CTRL-Z, ale to, czy biblioteka środowiska wykonawczego C działa czy nie, zależy od tego, czy odczytany strumień PLIK jest w trybie tekstowym czy binarnym. Dlatego w programach przenośnych zawsze należy dołączyć znak b w ciągu znaków podczas otwierania plików binarnych (np. fopen("foo.gif", "rb")).

+0

Przede wszystkim chcę Ci podziękować za szczegółową analizę różnych wdrożeń. Jednak zauważyłem, że CTRL-Z jest obsługiwany w nieco inny sposób w systemie Windows, w szczególności gdy wpisuję "dom^Z^Z^Z \ n', tylko drugi i trzeci"^Z "są ignorowane. –

+0

Oznacza to, że 'puts (str)' wyświetla na ekranie ciąg "house^Z" (gdzie^Z jest drukowane jako znak spoza ASCII). Moja interpretacja jest taka, że ​​wywołanie 'getchar()' wewnątrz 'gets' zwraca EOF tylko jeśli' ReadFile': a) nie ma znaku do odczytania (koniec pliku) b) napotka '^ Z' jako pierwszy znak w buforze . Jeśli '^ Z' znajduje się pośrodku bufora, to po prostu powoduje zamknięcie bufora, ale wtedy' getchar() 'nadal zwraca"^Z ". PS: Mam na myśli implementację 'dostaje' jak' getS' powyżej przez 'user3856986'. –

+0

To właściwie wszystko po pierwszym^Z, które jest ignorowane, przynajmniej z implementacją biblioteki wykonawczej Microsoft C. Inne implementacje są możliwe, jak na przykład opublikowany "user3856986", ale ten nie jest zgodny, więc nie jestem pewien, czy faktycznie jest on używany w dowolnej rzeczywistej bibliotece środowiska wykonawczego C, nie mówiąc już o systemie Windows. Jak dokładnie^Z jest obsługiwane zależy od implementacji i ewentualnie innych szczegółów, podobnie jak tryb buforowania strumienia. –

Powiązane problemy