Co to jest EOF i jak go uruchomić? [Zamknięte]

12

To jest mój kod źródłowy C.

Kiedy buduję go w Ubuntu, zaczyna on zbierać znaki, ale nie wiem, jak zakończyć program, ponieważ nie kończy się on na wprowadzeniu ENTERlub znaku powrotu karetki.

Co oznacza EOF? Jak mogę go uruchomić?

To źródło znajduje się również w książce Dennisa Ritchie:

#include <stdio.h>
    /* count digits, white space, others */
main ()
{
  int c, i, nwhite, nother;
  int ndigit[10];
  nwhite = nother = 0;
  for (i = 0; i < 10; ++i)
    ndigit[i] = 0;
  while ((c = getchar ()) != EOF)
    if (c >= '0' && c <= '9')
      ++ndigit[c - '0'];
    else if (c == ' ' || c == '\n' || c == '\t')
      ++nwhite;
    else
      ++nother;
  printf ("digits =");
  for (i = 0; i < 10; ++i)
    printf (" %d", ndigit[i]);
  printf (", white space = %d, other = %d\n", nwhite, nother);
}
programator stosów
źródło
4
w języku C -1jest równoważne EOF. Jest zdefiniowany /usr/include/stdio.hjako stała makro
Edward Torvalds
1
Odpowiednia lektura: stackoverflow.com/q/12389518/3701431
Sergiy Kolodyazhnyy
@edwardtorvalds wprowadzanie -1jako dane wejściowe nie działa :)
Sergiy Kolodyazhnyy
Myślę, że ta sama książka Dennisa Ritchiego to wyjaśnia.
andy256
Również istotne: unix.stackexchange.com/questions/110240/… (Żadna z odpowiedzi wysłanych na to pytanie nie jest całkowicie poprawna.)
fkraiem 25.01.2016

Odpowiedzi:

23

Tl; dr

Generalnie można „wyzwolić EOF” w programie działającym w terminalu za pomocą klawisza CTRL+ Dtuż po ostatnim płukaniu wejścia.


Co oznacza EOF? Jak mogę go uruchomić?

EOF oznacza koniec pliku.

„Wyzwalanie EOF” w tym przypadku z grubsza oznacza „uświadomienie programowi, że nie będzie wysyłane więcej danych wejściowych”.

W takim przypadku, ponieważ getchar()zwróci liczbę ujemną, jeśli żaden znak nie zostanie odczytany, wykonanie zostanie zakończone.

Ale dotyczy to nie tylko konkretnego programu, ale także wielu różnych narzędzi.

Zasadniczo „wyzwalanie EOF” można wykonać za pomocą klawisza CTRL+ Dzaraz po ostatnim płukaniu wejścia (tj. Wysyłając puste wejście).

Na przykład z cat:

% cat >file # Hit ENTER
foo # Hit ENTER and CTRL+D
% 

To, co dzieje się pod maską po naciśnięciu CTRL+, Dpolega na tym, że dane wejściowe wpisane od ostatniego koloru wejściowego są opróżniane; kiedy to okazuje się być pusta wprowadzić read()syscall wezwał programu stdin zwrotów 0, getchar()zwraca liczbę ujemną ( -1w bibliotece GNU C), a to z kolei jest interpretowane jako EOF 1 .


1 - /programming//a/1516177/4316166

kos
źródło
2
Kompilacja działa, ponieważ rozgraniczenie przecinkiem nie jest związane z byciem w tej samej linii. Poza tym świetne wyjaśnienie na temat EOF :)
Paulius Šukys
@ PauliusŠukys Huh, masz rację. Moje C jest trochę zardzewiałe. :)
Kos
1
iirc EOF nie jest zdefiniowany jako -1 według normy. Tak właśnie dzieje się na przykład w glibc.
larkey
1
EOF nie „polega na wysyłaniu„ pustych danych wejściowych ””, a cytowana odpowiedź SO nie mówi inaczej. Jest to sygnał poza pasmem. W przypadku terminala jest wysyłany przez naciśnięcie Ctrl / d.
user207421,
4

TL; DR : EOF nie jest znakiem, to makro używane do oceny ujemnego zwrotu funkcji odczytu danych wejściowych. Można użyć Ctrl+, Daby wysłać EOTznak, który wymusi powrót funkcji-1

Każdy programista musi RTFM

Odwołajmy się do „CA Reference Manual” Harbison i Steele, wydanie 4. od 1995 r., strona 317:

Ujemna liczba całkowita EOF to wartość, która nie jest kodowaniem „prawdziwego znaku”. . . Na przykład fget (sekcja 15.6) zwraca EOF na końcu pliku, ponieważ nie ma „prawdziwego znaku” do odczytania.

Zasadniczo EOFnie jest postacią, ale raczej liczbą całkowitą zaimplementowaną stdio.hdo reprezentowania -1. Tak więc odpowiedź kos jest poprawna, ale nie chodzi o otrzymywanie „pustych” danych wejściowych. Ważna uwaga jest taka, że tutaj EOF służy jako wartości zwracanej (z getchar()) stosunku, aby nie oznaczać rzeczywistego charakteru. Na man getcharpodpory, że:

WARTOŚĆ ZWRACANA

fgetc (), getc () i getchar () zwracają znak odczytany jako rzut bez znaku do int lub EOF na końcu pliku lub błędu.

gets () i fgets () zwracają s w przypadku powodzenia, a NULL w przypadku błędu lub gdy nastąpi koniec pliku, gdy nie zostały odczytane żadne znaki.

ungetc () zwraca c w przypadku sukcesu lub EOF w przypadku błędu.

Rozważmy whilepętlę - jej podstawowym celem jest powtórzenie akcji, jeśli warunek w nawiasach jest spełniony . Spójrz ponownie:

while ((c = getchar ()) != EOF)

Zasadniczo mówi: kontynuuj robienie rzeczy, jeśli c = getchar()zwróci udany kod ( 0lub wyższy; przy okazji, spróbuj wykonać udane polecenie, echo $?a potem się nie powiedzie echo $?i zobacz liczby, które zwracają). Więc jeśli uda nam się uzyskać znak i przydzielić C, zwrócony kod stanu to 0, błąd to -1. EOFjest zdefiniowany jako -1. Dlatego gdy -1 == -1wystąpi warunek , pętle zatrzymują się. A kiedy to się stanie? Gdy nie ma już postaci do zdobycia, gdy się c = getchar()nie powiedzie. Możesz pisać while ((c = getchar ()) != -1)i nadal będzie działać

Wróćmy też do samego kodu, oto fragment stdio.h

/* End of file character.
   Some things throughout the library rely on this being -1.  */
#ifndef EOF
# define EOF (-1)
#endif

Kody ASCII i EOT

Chociaż znak EOF nie jest znakiem rzeczywistym, istnieje EOTznak (End of Transmission), który ma wartość dziesiętną ASCII 04; jest połączony ze skrótem Ctrl+ D(reprezentowanym również jako znak meta ^D). Znak końca transmisji używany do oznaczania zamknięcia strumienia danych z powrotem, gdy komputery były używane do kontrolowania połączeń telefonicznych, stąd nazewnictwo „koniec transmisji”.

Możliwe jest przesłanie takiej wartości ascii do programu w ten sposób, zwróć uwagę na $'\04'to, co jest EOT:

skolodya@ubuntu:$ ./a.out  <<< "a,b,c $'\04'"                                  
digits = 1 0 0 0 1 0 0 0 0 0, white space = 2, other = 9

Możemy zatem powiedzieć, że istnieje, ale nie można go wydrukować

Dygresja

Często zapominamy, że w przeszłości komputery nie były tak wszechstronne - projektanci musieli korzystać z każdego dostępnego klawisza klawiatury. Tak więc, wysyłanie EOTznaku za pomocą CtrlD nadal „wysyła znak”, podobnie jak pisanie wielką literą A, ShiftA, nadal dajesz komputerowi wejście z dostępnymi kluczami. Zatem EOT jest prawdziwą postacią w tym sensie, że pochodzi od użytkownika, jest czytelny dla komputera (choć nie do wydrukowania, niewidoczny dla ludzi), istnieje w pamięci komputera

Komentarz Byte Commander

Jeśli spróbujesz czytać z / dev / null, powinno to również zwrócić EOF, prawda? A co tam dostanę?

Tak, dokładnie tak, ponieważ /dev/nullnie ma żadnego rzeczywistego znaku do odczytania, dlatego c = getchar()zwróci -1kod, a program natychmiast zakończy działanie. Ponownie polecenie nie zwraca EOF. EOF to tylko stała zmienna równa -1, której używamy do porównywania kodu powrotu funkcji getchar . EOFnie istnieje jako postać, jest tylko wartością statyczną w środku stdio.h.

Próbny:

# cat /dev/null shows there's no readable chars
DIR:/xieerqi
skolodya@ubuntu:$ cat /dev/null | cat -A        

# Bellow is simple program that will open /dev/null for reading. Note the use of literal -1                                   
   DIR:/xieerqi
skolodya@ubuntu:$ cat readNull.c                                               
#include<stdio.h>

void main()
{
   char c;
    FILE *file;
    file = fopen("/dev/null", "r");

    if (file) 
    {
    printf ("Before while loop\n");
        while ((c = getc(file)) != -1)
            putchar(c);
    printf("After while loop\n"); 
    fclose(file);
    }
}

DIR:/xieerqi
skolodya@ubuntu:$ gcc readNull.c -o readNull                                   

DIR:/xieerqi
skolodya@ubuntu:$ ./readNull
Before while loop
After while loop

Kolejny gwóźdź do trumny

Czasami próbuje się udowodnić, że EOF jest znakiem o takim kodzie:

#include <stdio.h>
int main(void)
{
    printf("%c", EOF);
    return 0;
}

Problem w tym, że typ danych char może być wartością podpisaną lub niepodpisaną. Ponadto są najmniejszymi adresowalnymi typami danych, co czyni je bardzo przydatnymi w mikrokontrolerach, w których pamięć jest ograniczona. Zamiast deklarować int foo = 25;, często obserwuje się w mikrokontrolerach z małą pamięcią char foo = 25;lub czymś podobnym. Ponadto znaki mogą być podpisane lub niepodpisane .

Można sprawdzić, czy rozmiar w bajtach za pomocą takiego programu:

#include <stdio.h>
int main(void)
{
    printf("Size of int: %lu\n",sizeof(int));
    printf("Sieze of char: %lu\n",sizeof(char));
    //printf("%s", EOF);
    return 0;
}

skolodya@ubuntu:$ ./EOF                                                        
Size of int: 4
Sieze of char: 1

O co dokładnie chodzi? Chodzi o to, że EOF jest zdefiniowany jako -1, ale typ danych char może wydrukować wartości całkowite .

OK . . . więc co, jeśli spróbujemy wydrukować znak jako ciąg?

#include <stdio.h>
int main(void)
{
    printf("%s", EOF);
    return 0;
}

Oczywiście błąd, ale błąd powie nam coś ciekawego:

skolodya @ ubuntu: $ gcc EOF.c -o EOF
EOF.c: W funkcji „main”: EOF.c: 4: 5: ostrzeżenie: format „% s” oczekuje argumentu typu „char *”, ale argument 2 ma wpisz „int” [-Wformat =] printf („% s”, EOF);

Wartości szesnastkowe

Drukowanie EOF jako wartości szesnastkowej daje FFFFFFFF16-bitową (8-bajtową) wartość, komplement dwóch z a -1.

#include <stdio.h>
int main(void)
{
    printf("This is EOF: %X\n", EOF);
    printf("This is Z: %X\n",'Z');
    return 0;
}

Wynik:

DIR:/xieerqi
skolodya@ubuntu:$ ./EOF                                                        
This is EOF: FFFFFFFF
This is Z: 5A

Następująca ciekawa rzecz występuje z następującym kodem:

#include <stdio.h>
int main(void)
{
   char c;
   if (c = getchar())
    printf ("%x",c);
    return 0;
}

Jeśli naciśniesz Shift+ A, otrzymamy wartość szesnastkową 41, oczywiście taką samą jak w tabeli ASCII. Ale dla Ctrl+ Dmamy ffffffffponownie wartość zwracaną getchar()przechowywaną w c.

DIR:/xieerqi
skolodya@ubuntu:$ gcc  EOF.c -o ASDF.asdf                                      

DIR:/xieerqi
skolodya@ubuntu:$ ./ASDF.asdf                                                  
A
41
DIR:/xieerqi
skolodya@ubuntu:$ ./ASDF.asdf                                                  
ffffffff

Zobacz inne języki

Zauważ, że inne języki unikają tego zamieszania, ponieważ działają na ocenie stanu wyjścia funkcji, a nie na porównaniu z makrem. Jak można odczytać plik w Javie?

    File inputFile  = new File (filename);
    Scanner readFile = new Scanner(inputFile);
    while (readFile.hasNext())
        { //more code bellow  }

Co powiesz na python?

with open("/etc/passwd") as file:
     for line in file:
          print line
Sergiy Kolodyazhnyy
źródło
Świetna sprawa, w pewnym momencie postać jest w jakiś sposób wysyłana.
Kos
Myślę, że postać EOF zaginęła w tłumaczeniu, ponieważ nie jest to rzeczywista postać, ale EOT jest rzeczywistą, ascii. Domyśl !
Sergiy Kolodyazhnyy
1
Jeśli spróbujesz czytać /dev/null, to również powinien zwrócić EOF, prawda? A co tam dostanę?
Bajt Dowódca
@ByteCommander pozwala się dowiedzieć. Czy cat / dev / null | cat -A.
Sergiy Kolodyazhnyy
@ByteCommander dodał sekcję dotyczącą twojego komentarza
Sergiy Kolodyazhnyy
2

EOF oznacza koniec pliku . Chociaż nie wiem, jak wywołać następujący symbol, możesz uruchomić następujący program poprzez potokowanie pliku, który wysyła sygnał EOF na końcu:

echo "Some sample text" | ./a.out

gdzie a.outjest twoje skompilowane źródło

Paulius Šukys
źródło
1
Przegłosowałem to już, jednak na marginesie EOF nie jest znakiem, myślę, że nieporozumienie wynika z faktu, że jest sygnalizowany naciśnięciem klawisza CTRL, który jest zwykle sposobem na wprowadzenie znaków niedrukowalnych. Jak rozumiem, wszystko, co faktycznie się dzieje, to to, że wszystkie dane wejściowe są opróżniane, a bycie danymi wejściowymi do opróżnienia pustym read()(syscall) powróci 0, co jest interpretowane jako EOF: stackoverflow.com/a/1516177/4316166
kos
@kos, masz rację, to mimo wszystko sygnał.
Paulius Šukys