¿Qué es EOF y cómo activarlo? [cerrado]

12

Este es mi código fuente C.

Cuando lo compilo en Ubuntu, comienza a obtener caracteres, pero no sé cómo finalizar el programa, ya que no termina ingresando ENTERo un retorno de carro.

¿Qué significa EOF? ¿Cómo puedo activarlo?

Esta fuente también está en un libro de Dennis 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);
}
programador
fuente
44
en lenguaje C -1es equivalente a EOF. Se define /usr/include/stdio.hcomo una constante macro
Edward Torvalds, el
1
Lectura relevante: stackoverflow.com/q/12389518/3701431
Sergiy Kolodyazhnyy
@edwardtorvalds entrando -1como entrada no funciona :)
Sergiy Kolodyazhnyy
Creo que el mismo libro de Dennis Ritchie explica esto.
andy256
También relevante: unix.stackexchange.com/questions/110240/… (Ninguna de las respuestas publicadas a esta pregunta es completamente correcta.)
fkraiem

Respuestas:

22

Tl; dr

En general, puede "disparar EOF" en un programa que se ejecuta en un terminal con una pulsación de tecla CTRL+ Djusto después de la última descarga de entrada.


¿Qué significa EOF? ¿Cómo puedo activarlo?

EOF significa Fin de archivo.

"Activar EOF" en este caso significa "hacer que el programa sea consciente de que no se enviarán más entradas".

En este caso, dado getchar()que devolverá un número negativo si no se lee ningún carácter, la ejecución finaliza.

Pero esto no solo se aplica a su programa específico, se aplica a muchas herramientas diferentes.

En general, "disparar EOF" se puede hacer con una tecla CTRL+ Djusto después de la última descarga de entrada (es decir, enviando una entrada vacía).

Por ejemplo con cat:

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

Lo que sucede debajo del capó al presionar CTRL+ Des que la entrada escrita desde la última descarga de entrada se vacía; cuando se trata de una entrada vacía, la read()llamada al sistema llamada en el STDIN del programa devuelve 0, getchar()devuelve un número negativo ( -1en la biblioteca GNU C) y esto a su vez se interpreta como EOF 1 .


1 - /programming//a/1516177/4316166

kos
fuente
2
La compilación funciona, porque la delimitación de comas no está limitada por estar en la misma línea. Aparte de eso, gran explicación sobre EOF :)
Paulius Šukys
@ PauliusŠukys Huh, tienes razón. Mi C está un poco oxidada. :)
kos
1
iirc EOF no se define como -1 según el estándar. Es justo lo que sucede en glibc, por ejemplo.
larkey
1
EOF no consiste en enviar una "entrada vacía", y la respuesta SO que usted cita no dice lo contrario. Es una señal fuera de banda. En el caso de un terminal, se envía escribiendo Ctrl / d.
user207421
4

TL; DR : EOF no es un carácter, es una macro utilizada para evaluar el retorno negativo de una función de lectura de entrada. Se puede usar Ctrl+ Dpara enviar EOTcaracteres que forzarán el retorno de la función-1

Todo programador debe RTFM

Consultemos el "Manual de referencia de CA", de Harbison y Steele, 4ª ed. desde 1995, página 317:

El entero negativo EOF es un valor que no es una codificación de un "carácter real". . . Por ejemplo, fget (sección 15.6) devuelve EOF cuando está al final del archivo, porque no hay un "carácter real" para leer.

Esencialmente EOFno es un personaje, sino más bien un valor entero implementado stdio.hpara representar -1. Por lo tanto, la respuesta de kos es correcta en lo que respecta a eso, pero no se trata de recibir una entrada "vacía". Nota importante es que aquí EOF sirve como valor de retorno (de getchar()) comparación, no para indicar un carácter real. Los man getcharapoyos que:

VALOR DEVUELTO

fgetc (), getc () y getchar () devuelven el carácter leído como una conversión de caracteres sin firmar a un int o EOF al final del archivo o error.

gets () y fgets () devuelven s en caso de éxito y NULL en caso de error o cuando se produce el final del archivo mientras no se han leído caracteres.

ungetc () devuelve c en caso de éxito o EOF en caso de error.

Considere el whileciclo: su propósito principal es repetir la acción si la condición entre paréntesis es verdadera . Mirar de nuevo:

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

Básicamente dice que siga haciendo cosas si c = getchar()devuelve un código exitoso ( 0o superior; es algo común por cierto, intente ejecutar un comando exitoso, echo $?luego falla echo $?y vea los números que devuelven). Entonces, si obtenemos con éxito el carácter y asignamos a C, el código de estado devuelto es 0, el error es -1. EOFse define como -1. Por lo tanto, cuando se -1 == -1produce la condición , los bucles se detienen. ¿Y cuándo sucederá eso? Cuando no hay más personajes para obtener, cuando c = getchar()falla. Podrías escribir while ((c = getchar ()) != -1)y aún funcionaría

Además, volvamos al código real, aquí hay un extracto de stdio.h

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

Códigos ASCII y EOT

Aunque el carácter EOF no es un carácter real, existe un carácter EOT(Fin de transmisión) que tiene un valor decimal ASCII de 04; está vinculado a Ctrl+ Dacceso directo (representado también como meta carácter ^D). El carácter de fin de transmisión utilizado para significar el cierre de una secuencia de datos cuando las computadoras se usaban para controlar las conexiones telefónicas, de ahí el nombre de "fin de transmisión".

Por lo tanto, es posible enviar ese valor ASCII al programa así, tenga en cuenta $'\04'cuál es el 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

Por lo tanto, podemos decir que existe, pero no es imprimible

Nota al margen

A menudo olvidamos que en el pasado las computadoras no eran tan versátiles: los diseñadores tienen que hacer uso de todas las teclas disponibles. Por lo tanto, enviar EOTcaracteres con CtrlD sigue siendo "enviar un carácter", no muy diferente de escribir mayúscula A, ShiftA, aún puede hacer que la computadora ingrese con las teclas disponibles. Por lo tanto, EOT es un personaje real en el sentido de que proviene del usuario, es legible por computadora (aunque no imprimible, no visible para los humanos), existe en la memoria de la computadora

Comentario del comandante de bytes

Si intentas leer desde / dev / null, eso también debería devolver un EOF, ¿verdad? ¿O qué consigo allí?

Sí, exactamente correcto, porque /dev/nullno hay ningún carácter real para leer, por lo tanto c = getchar(), devolverá el -1código y el programa se cerrará de inmediato. De nuevo el comando no devuelve EOF. EOF es una variable constante igual a -1, que usamos para comparar el código de retorno de la función getchar . EOFno existe como personaje, es solo un valor estático en su interior stdio.h.

Manifestación:

# 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

Otro clavo en el ataúd

A veces se intenta demostrar que EOF es un personaje con un código como este:

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

El problema con eso es que el tipo de datos char puede ser un valor con signo o sin signo. Además, son el tipo de datos direccionable más pequeño que los hace muy muy útiles en microcontroladores, donde la memoria es limitada. Entonces, en lugar de declarar int foo = 25;, es común ver en microcontroladores con poca memoria char foo = 25;o algo similar. Además, los caracteres pueden estar firmados o sin firmar .

Se podría verificar que el tamaño en bytes con un programa como este:

#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

Cuál es exactamente el punto ? El punto es que EOF se define como -1, pero el tipo de datos char puede imprimir valores enteros .

OKAY . . ¿Qué pasa si intentamos imprimir char como cadena?

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

Obviamente un error, pero no obstante, el error nos dirá algo interesante:

skolodya @ ubuntu: $ gcc EOF.c -o EOF
EOF.c: En la función 'main': EOF.c: 4: 5: advertencia: el formato '% s' espera un argumento del tipo 'char *', pero el argumento 2 tiene escriba 'int' [-Wformat =] printf ("% s", EOF);

Valores hexadecimales

Imprimir EOF como un valor hexadecimal da FFFFFFFF, un valor de 16 bits (8 bytes), dos cumplidos de a -1.

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

Salida:

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

Otra cosa curiosa ocurre con el siguiente código:

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

Si uno presiona Shift+ A, obtenemos el valor hexadecimal 41, obviamente igual que en la tabla ASCII. Pero para Ctrl+ D, tenemos ffffffff, nuevamente, el valor de retorno de getchar()almacenado en 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

Consulte otros idiomas

Tenga en cuenta que otro lenguaje evita esta confusión, porque operan evaluando el estado de salida de una función, no comparándola con una macro. ¿Cómo se lee un archivo en Java?

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

¿Qué tal python?

with open("/etc/passwd") as file:
     for line in file:
          print line
Sergiy Kolodyazhnyy
fuente
Gran punto, de hecho, un personaje se envía de alguna manera en algún momento.
kos
Creo que el personaje EOF es algo que se ha perdido en la traducción, ya que no es un personaje real, pero EOT es un personaje ascii real. Imagínate !
Sergiy Kolodyazhnyy
1
Si intentas leer /dev/null, eso también debería devolver un EOF, ¿verdad? ¿O qué consigo allí?
Byte Commander
@ByteCommander vamos a descubrirlo. Do cat / dev / null | gato -A.
Sergiy Kolodyazhnyy
@ByteCommander agregó una sección que aborda su comentario
Sergiy Kolodyazhnyy
2

EOF significa fin de archivo . Si bien no sé cómo activar el siguiente símbolo, puede ejecutar el siguiente programa mediante la canalización de un archivo, que envía la señal EOF al final:

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

donde a.outesta tu fuente compilada

Paulius Šukys
fuente
1
Ya voté sobre esto, sin embargo, en una nota al margen EOF no es un personaje, creo que la idea errónea surge del hecho de que se señala a través de una combinación de teclas CTRL, que generalmente es la forma de ingresar caracteres no imprimibles. Según tengo entendido, todo lo que realmente sucede es que toda la entrada se vacía y read()volverá la entrada que se vaciará vacía (syscall) 0, lo que se interpreta como EOF: stackoverflow.com/a/1516177/4316166
kos
@kos, tienes razón, es una señal después de todo.
Paulius Šukys