EOF n'est pas un symbole

Récemment, j'ai lu le livre «Systèmes informatiques: architecture et programmation. Le look du programmeur. " Là, dans le chapitre sur le système d'E / S Unix, les auteurs ont mentionné qu'il n'y avait pas de caractère spécial à la fin du fichier EOF. Si vous avez lu des informations sur le système d'E / S Unix / Linux, ou si vous l'avez expérimenté, si vous avez écrit des programmes C qui lisent des données à partir de fichiers, cette déclaration vous semblera probablement complètement évidente. Mais regardons de plus près les deux déclarations suivantes liées à ce que j'ai trouvé dans le livre:





  1. EOF - ce n'est pas un symbole.
  2. Il n'y a pas de caractère spécial à la fin des fichiers.

Qu'est-ce que c'est EOF?

EOF n'est pas un symbole


Pourquoi est-ce que quelqu'un dit ou pense que EOFc'est un symbole? Je suppose que cela peut être le cas parce que dans certains programmes C, vous pouvez trouver du code qui utilise une vérification explicite pour l' EOFutilisation des fonctions getchar()et getc().

Cela pourrait ressembler à ceci:

    #include <stdio.h>
    ...
    while ((c = getchar()) != EOF)
      putchar(c);

Ou alors:

    FILE *fp;
    int c;
    ...
    while ((c = getc(fp)) != EOF)
      putc(c, stdout);

Si vous consultez l'aide de getchar()ou getc(), vous pouvez découvrir que les deux fonctions lisent le caractère suivant du flux d'entrée. Probablement - c'est précisément ce qui provoque la conception erronée de la nature EOF. Mais ce ne sont que mes hypothèses. Revenons à l'idée que EOF- ce n'est pas un symbole.

Et qu'est-ce qu'un symbole en général? Un symbole est la plus petite composante de texte. «A», «a», «B», «b» - tous ces symboles sont différents. Un caractère a un code numérique qui, dans la norme Unicode, est appelé un point de code . Par exemple, la lettre latine «A» a, en décimal, le code 65. Cela peut être vérifié rapidement en utilisant la ligne de commande de l'interpréteur Python:

$python
>>> ord('A')
65
>>> chr(65)
'A'

Ou vous pouvez jeter un œil à la table ASCII sur Unix / Linux:

$ man ascii


Nous allons découvrir à quel code correspond EOFen écrivant un petit programme en C. En ANSI C, une constante est EOFdéfinie dans stdio.h, elle fait partie de la bibliothèque standard. Habituellement écrit à cette constante -1. Vous pouvez enregistrer le code suivant dans un fichier printeof.c, le compiler et l'exécuter:

#include <stdio.h>

int main(int argc, char *argv[])
{
  printf("EOF value on my system: %d\n", EOF);
  return 0;
}

Compilez et lancer le programme:

$ gcc -o printeof printeof.c

$ ./printeof
EOF value on my system: -1

J'ai ce programme, testé sur Mac OS et sur Ubuntu, des rapports qui sont EOFégaux -1. Y a-t-il un caractère avec ce code? Ici, encore une fois, vous pouvez vérifier les codes de caractères dans la table ASCII, vous pouvez regarder la table Unicode et découvrir dans quelle plage les codes de caractères peuvent être. Nous agirons différemment: nous lancerons l'interpréteur Python et utiliserons la fonction standard chr()pour nous donner le symbole correspondant au code -1:

$ python
>>> chr(-1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: chr() arg not in range(0x110000)

Comme prévu, le caractère avec le code -1n'existe pas. Donc, à la fin, EOFet la vérité n'est pas un symbole. Nous passons maintenant à la deuxième déclaration à l'examen.

Il n'y a pas de caractère spécial à la fin des fichiers.


Peut EOF- être - c'est un caractère spécial qui se trouve à la fin du fichier? Je suppose que vous connaissez déjà la réponse. Mais vérifions soigneusement notre hypothèse.

Prenez un fichier texte simple, helloworld.txt , et affichez son contenu en représentation hexadécimale. Pour ce faire, vous pouvez utiliser la commande xxd:

$ cat helloworld.txt
Hello world!

$ xxd helloworld.txt
00000000: 4865 6c6c 6f20 776f 726c 6421 0a         Hello world!.

Comme vous pouvez le voir, le dernier caractère du fichier a un code 0a. Dans le tableau ASCII, vous pouvez découvrir que ce code correspond à un caractère nl, c'est-à-dire à un caractère de nouvelle ligne. Vous pouvez le découvrir en utilisant Python:

$ python
>>> chr(0x0a)
'\n'

Donc. EOF- Ce n'est pas un symbole, et à la fin des fichiers il n'y a pas de symbole spécial. Qu'est-ce que c'est EOF?

Qu'est-ce qu'un EOF?


EOF(fin de fichier) est un état qui peut être détecté par l'application dans une situation où l'opération de lecture de fichier arrive à son terme.

Voyons comment il est possible de détecter l'état EOFdans différents langages de programmation lors de la lecture d'un fichier texte à l'aide des outils d'entrée-sortie de haut niveau fournis par ces langages. Pour ce faire, nous écrirons une version très simple cat, qui sera appelée mcat. Il lit l'octet de texte ASCII (caractère) et recherche explicitement EOF. Nous écrirons le programme dans les langues suivantes:

  • ANSI C
  • Python 3
  • Aller
  • JavaScript (Node.js)

Voici un référentiel avec un exemple de code. Nous procédons à leur analyse.

ANSI C


Commençons par le vénérable C. Le programme présenté ici est une version modifiée catdu livre "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;
}

Compilation:

$ gcc -o mcat mcat.c

Lancement:

$ ./mcat helloworld.txt
Hello world!

Voici quelques explications concernant le code ci-dessus:

  • Le programme ouvre le fichier qui lui est transmis en tant qu'argument de ligne de commande.
  • La boucle whilecopie les données du fichier vers le flux de sortie standard. Les données sont copiées octet par octet, cela se produit jusqu'à la fin du fichier.
  • Lorsque le programme atteint EOF, il ferme le fichier et se ferme.

Python 3


En Python, il n'y a pas de mécanisme de vérification explicite EOF, similaire à celui disponible dans ANSI C. Mais si vous lisez le fichier caractère par caractère, vous pouvez révéler l'état EOFsi la variable contenant le caractère suivant lu est vide:

# 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='')

Exécutez le programme et examinez les résultats qui lui sont renvoyés:

$ python mcat.py helloworld.txt
Hello world!

Voici une version plus courte du même exemple écrit en Python 3.8+. Ici l'opérateur est utilisé : = (il est appelé "opérateur morse" ou "opérateur morse"):

# mcat38.py
import sys

with open(sys.argv[1]) as fin:
    while (c := fin.read(1)) != '':  #   1    EOF
        print(c, end='')

Exécutez ce code:

$ python3.8 mcat38.py helloworld.txt
Hello world!

Aller


Dans Go, vous pouvez vérifier explicitement l'erreur renvoyée par Read () pour voir si cela indique que nous sommes arrivés à la fin du fichier:

// 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()
}

Exécutez le programme:

$ go run mcat.go helloworld.txt
Hello world!

JavaScript (Node.js)


Node.js n'a pas de mécanisme de vérification explicite EOF. Mais, lorsque, en atteignant la fin du fichier, une tentative est faite pour lire autre chose, l'événement de fin de flux est déclenché .

/* 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.');
});

Exécutez le programme:

$ node mcat.js helloworld.txt
Hello world!

EOF: There will be no more data.

Mécanismes du système de bas niveau


Comment les mécanismes d'E / S de haut niveau utilisés dans les exemples ci-dessus déterminent-ils la fin du fichier? Sous Linux, ces mécanismes utilisent directement ou indirectement l'appel système read () fourni par le noyau. Une fonction (ou macro) getc()de C, par exemple, utilise un appel système read()et retourne EOFsi elle read()indique l'état d'atteindre la fin du fichier. Dans ce cas, read()renvoie 0. Si vous décrivez tout cela sous la forme d'un diagramme, vous obtenez ce qui suit:


Il s'avère que la fonction est getc()basée sur read().

Nous allons écrire une version catnommée en syscatutilisant uniquement les appels système Unix. Nous le ferons non seulement par intérêt, mais aussi parce que cela pourrait très bien nous apporter des avantages.

Voici ce programme écrit 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;
}

Exécuter:

$ gcc -o syscat syscat.c

$ ./syscat helloworld.txt
Hello world!

Ce code utilise le fait que la fonction read(), indiquant que la fin du fichier est atteinte, retourne 0.

Voici le même programme écrit 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)

Exécuter:

$ python syscat.py helloworld.txt
Hello world!

Voici la même chose écrite 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)

Exécutez également ce code:

$ python3.8 syscat38.py helloworld.txt
Hello world!

Sommaire


  • EOF - ce n'est pas un symbole.
  • Il n'y a pas de caractère spécial à la fin des fichiers.
  • EOF - c'est l'état signalé par le noyau et qui peut être détecté par l'application dans le cas où l'opération de lecture des données atteint la fin du fichier.
  • Dans ANSI C EOF, ce n'est encore pas un caractère. Il s'agit de la constante définie stdio.hdans laquelle la valeur -1 est généralement écrite.
  • Un «caractère» EOFest introuvable dans une table ASCII ou en Unicode.

Chers lecteurs! Connaissez-vous des idées fausses plus ou moins répandues dans le monde des ordinateurs?


All Articles