Recientemente, leí el libro "Sistemas informáticos: arquitectura y programación". El aspecto del programador ". Allí, en el capítulo sobre el sistema Unix I / O, los autores mencionaron que no hay un carácter especial al final del archivo EOF
. Si lees sobre el sistema de E / S Unix / Linux, o experimentas con él, si escribiste programas en C que leen datos de archivos, entonces esta afirmación probablemente te parecerá completamente obvia. Pero echemos un vistazo más de cerca a las siguientes dos declaraciones relacionadas con lo que encontré en el libro:
EOF
- Esto no es un símbolo.- No hay caracteres especiales al final de los archivos.
¿Qué es esto EOF
?EOF no es un símbolo
¿Por qué alguien dice o piensa que EOF
esto es un símbolo? Supongo que esto puede deberse a que algunos programas escritos en C pueden encontrar código que usa una verificación explícita para EOF
usar funciones getchar()
y getc()
.Podría verse así: #include <stdio.h>
...
while ((c = getchar()) != EOF)
putchar(c);
Más o menos: FILE *fp;
int c;
...
while ((c = getc(fp)) != EOF)
putc(c, stdout);
Si observa la ayuda de getchar()
o getc()
, puede descubrir que ambas funciones leen el siguiente carácter de la secuencia de entrada. Probablemente, esto es precisamente lo que causa la idea errónea sobre la naturaleza EOF
. Pero estos son solo mis suposiciones. Volvamos a la idea de que EOF
esto no es un símbolo.¿Y qué es un símbolo en general? Un símbolo es el componente más pequeño del texto. “A”, “a”, “B”, “b”: todos estos son símbolos diferentes. Un carácter tiene un código numérico, que en el estándar Unicode se denomina punto de código . Por ejemplo, la letra latina “A” tiene, en decimal, el código 65. Esto puede verificarse rápidamente usando la línea de comando del intérprete de Python:$python
>>> ord('A')
65
>>> chr(65)
'A'
O puede echar un vistazo a la tabla ASCII en Unix / Linux:$ man ascii
Descubriremos qué código corresponde EOF
escribiendo un pequeño programa en C. En ANSI C, una constante se EOF
define en stdio.h
, es parte de la biblioteca estándar. Generalmente escrito a esta constante -1
. Puede guardar el siguiente código en un archivo printeof.c
, compilarlo y ejecutarlo:#include <stdio.h>
int main(int argc, char *argv[])
{
printf("EOF value on my system: %d\n", EOF);
return 0;
}
Compilar y ejecutar el programa:$ gcc -o printeof printeof.c
$ ./printeof
EOF value on my system: -1
Tengo este programa, probado en Mac OS y Ubuntu, informa que EOF
es igual -1
. ¿Hay algún personaje con este código? Aquí, nuevamente, puede verificar los códigos de caracteres en la tabla ASCII, puede mirar la tabla Unicode y averiguar en qué rango pueden estar los códigos de caracteres. Actuaremos de manera diferente: iniciaremos el intérprete de Python y utilizaremos la función estándar chr()
para darnos el símbolo correspondiente al código -1
:$ python
>>> chr(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: chr() arg not in range(0x110000)
Como se esperaba, el carácter con el código -1
no existe. Entonces, al final, EOF
y la verdad no es un símbolo. Pasamos ahora a la segunda declaración bajo consideración.No hay caracteres especiales al final de los archivos.
Tal vez EOF
, ¿este es un carácter especial que se puede encontrar al final del archivo? Supongo que ya sabes la respuesta. Pero revisemos cuidadosamente nuestra suposición.Tome un archivo de texto simple, helloworld.txt , y muestre su contenido en representación hexadecimal. Para hacer esto, puede usar el comando xxd
:$ cat helloworld.txt
Hello world!
$ xxd helloworld.txt
00000000: 4865 6c6c 6f20 776f 726c 6421 0a Hello world!.
Como puede ver, el último carácter del archivo tiene un código 0a
. En la tabla ASCII, puede descubrir que este código corresponde a un carácter nl
, es decir, a un carácter de nueva línea. Puedes encontrar esto usando Python:$ python
>>> chr(0x0a)
'\n'
Entonces. EOF
- Esto no es un símbolo, y al final de los archivos no hay un símbolo especial. ¿Qué es esto EOF
?¿Qué es un EOF?
EOF
(fin de archivo) es un estado que la aplicación puede detectar en una situación en la que la operación de lectura de archivo llega a su fin.Veamos cómo puede detectar el estado EOF
en diferentes lenguajes de programación al leer un archivo de texto utilizando las herramientas de entrada y salida de alto nivel proporcionadas por estos lenguajes. Para hacer esto, escribiremos una versión muy simple cat
, que se llamará mcat
. Lee el byte de texto ASCII (carácter) y lo comprueba explícitamente EOF
. Escribiremos el programa en los siguientes idiomas:- ANSI C
- Python 3
- Vamos
- JavaScript (Node.js)
Aquí hay un repositorio con código de muestra. Procedemos a su análisis.ANSI C
Comencemos con el venerable C. El programa presentado aquí es una versión modificada cat
del libro "C Programming Language".
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
int c;
if ((fp = fopen(*++argv, "r")) == NULL) {
printf("mcat: can't open %s\n", *argv);
return 1;
}
while ((c = getc(fp)) != EOF)
putc(c, stdout);
fclose(fp);
return 0;
}
Compilacion:$ gcc -o mcat mcat.c
Lanzamiento:$ ./mcat helloworld.txt
Hello world!
Aquí hay algunas explicaciones sobre el código anterior:- El programa abre el archivo que se le pasa como argumento de línea de comando.
- El bucle
while
copia datos del archivo a la secuencia de salida estándar. Los datos se copian byte a byte, esto sucede hasta que se alcanza el final del archivo. - Cuando el programa llega
EOF
, cierra el archivo y sale.
Python 3
En Python, no existe un mecanismo para verificar explícitamente EOF
, similar al que está disponible en ANSI C. Pero si lee el archivo carácter por carácter, puede revelar el estado EOF
si la variable que almacena el siguiente carácter leído está vacía:# mcat.py
import sys
with open(sys.argv[1]) as fin:
while True:
c = fin.read(1) # 1
if c == '': # EOF
break
print(c, end='')
Ejecute el programa y eche un vistazo a los resultados que se le devuelven:$ python mcat.py helloworld.txt
Hello world!
Aquí hay una versión más corta del mismo ejemplo escrito en Python 3.8+. Aquí se utiliza el operador : = (se llama "operador de morsa" u "operador de morsa"):# mcat38.py
import sys
with open(sys.argv[1]) as fin:
while (c := fin.read(1)) != '': # 1 EOF
print(c, end='')
Ejecute este código:$ python3.8 mcat38.py helloworld.txt
Hello world!
Vamos
En Go, puede verificar explícitamente el error devuelto por Read () para ver si indica que llegamos al final del archivo:
package main
import (
"fmt"
"os"
"io"
)
func main() {
file, err := os.Open(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "mcat: %v\n", err)
os.Exit(1)
}
buffer := make([]byte, 1)
for {
bytesread, err := file.Read(buffer)
if err == io.EOF {
break
}
fmt.Print(string(buffer[:bytesread]))
}
file.Close()
}
Ejecuta el programa:$ go run mcat.go helloworld.txt
Hello world!
JavaScript (Node.js)
Node.js no tiene ningún mecanismo para verificar explícitamente EOF
. Pero cuando, al llegar al final del archivo, se intenta leer algo más, se genera el evento de transmisión final .
const fs = require('fs');
const process = require('process');
const fileName = process.argv[2];
var readable = fs.createReadStream(fileName, {
encoding: 'utf8',
fd: null,
});
readable.on('readable', function() {
var chunk;
while ((chunk = readable.read(1)) !== null) {
process.stdout.write(chunk);
}
});
readable.on('end', () => {
console.log('\nEOF: There will be no more data.');
});
Ejecuta el programa:$ node mcat.js helloworld.txt
Hello world!
EOF: There will be no more data.
Mecanismos de bajo nivel del sistema
¿Cómo determinan los mecanismos de E / S de alto nivel utilizados en los ejemplos anteriores el final del archivo? En Linux, estos mecanismos utilizan directa o indirectamente la llamada al sistema read () proporcionada por el núcleo. Una función (o macro) getc()
de C, por ejemplo, usa una llamada al sistema read()
y regresa EOF
si read()
indica el estado de llegar al final del archivo. En este caso, read()
vuelve 0
. Si representa todo esto en forma de diagrama, obtiene lo siguiente:Resulta que la función se getc()
basa en read()
.Escribiremos una versión cat
nombrada syscat
usando solo llamadas al sistema Unix. Haremos esto no solo por interés, sino también porque puede traernos algún beneficio.Aquí está este programa escrito en C:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char c;
fd = open(argv[1], O_RDONLY, 0);
while (read(fd, &c, 1) != 0)
write(STDOUT_FILENO, &c, 1);
return 0;
}
Ejecutarlo:$ gcc -o syscat syscat.c
$ ./syscat helloworld.txt
Hello world!
Este código utiliza el hecho de que la función read()
, que indica que se alcanza el final del archivo, regresa 0
.Aquí está el mismo programa escrito en Python 3:# syscat.py
import sys
import os
fd = os.open(sys.argv[1], os.O_RDONLY)
while True:
c = os.read(fd, 1)
if not c: # EOF
break
os.write(sys.stdout.fileno(), c)
Ejecutarlo:$ python syscat.py helloworld.txt
Hello world!
Aquí está lo mismo escrito en Python 3.8+:# syscat38.py
import sys
import os
fd = os.open(sys.argv[1], os.O_RDONLY)
while c := os.read(fd, 1):
os.write(sys.stdout.fileno(), c)
Ejecute este código también:$ python3.8 syscat38.py helloworld.txt
Hello world!
Resumen
EOF
- Esto no es un símbolo.- No hay caracteres especiales al final de los archivos.
EOF
- este es el estado informado por el núcleo y que la aplicación puede detectar en el caso en que la operación de lectura de datos llega al final del archivo.- En ANSI C
EOF
, esto nuevamente no es un personaje. Esta es la constante definida stdio.h
en la que generalmente se escribe el valor -1. EOF
No se puede encontrar un "carácter" en una tabla ASCII o en Unicode.
¡Queridos lectores! ¿Conoces ideas erróneas más o menos generalizadas del mundo de las computadoras?