EOF no es un símbolo

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:





  1. EOF - Esto no es un símbolo.
  2. 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 EOFesto 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 EOFusar 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 EOFesto 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 EOFescribiendo un pequeño programa en C. En ANSI C, una constante se EOFdefine 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 EOFes 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 -1no existe. Entonces, al final, EOFy 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 EOFen 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 catdel libro "C Programming Language".

/* mcat.c */
#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 whilecopia 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 EOFsi 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:

// mcat.go
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// 1-byte buffer
    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 .

/* mcat.js */
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); /* chunk is one byte */
  }
});

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 EOFsi 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 catnombrada syscatusando 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:

/* syscat.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.hen la que generalmente se escribe el valor -1.
  • EOFNo 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?


All Articles