EOF não é um símbolo

Recentemente, li o livro “Sistemas de Computadores: Arquitetura e Programação. A aparência do programador. " Lá, no capítulo sobre o sistema de E / S do Unix, os autores mencionaram que não há caracteres especiais no final do arquivo EOF. Se você leu sobre o sistema de E / S Unix / Linux ou fez experiências com ele, se escreveu programas em C que lêem dados de arquivos, essa declaração provavelmente parecerá completamente óbvia para você. Mas vamos dar uma olhada mais de perto nas duas declarações a seguir relacionadas ao que encontrei no livro:





  1. EOF - isto não é um símbolo.
  2. Não há caracteres especiais no final dos arquivos.

O que é isso EOF?

EOF não é um símbolo


Por que alguém diz ou pensa que EOFisso é um símbolo? Suponho que seja assim porque em alguns programas C você pode encontrar código que usa verificação explícita para EOFusar funções getchar()e getc().

Pode ser assim:

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

Ou então:

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

Se você procurar a ajuda para getchar()ou getc(), pode descobrir que ambas as funções lêem o próximo caractere no fluxo de entrada. Provavelmente - é exatamente isso que causa o equívoco sobre a natureza EOF. Mas essas são apenas minhas suposições. Voltemos à ideia de que EOF- isto não é um símbolo.

E o que é um símbolo em geral? Um símbolo é o menor componente do texto. "A", "a", "B", "b" - todos esses são símbolos diferentes. Um caractere possui um código numérico, que no padrão Unicode é chamado de ponto de código . Por exemplo, a letra latina “A” possui, em decimal, o código 65. Isso pode ser verificado rapidamente usando a linha de comando do interpretador Python:

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

Ou você pode dar uma olhada na tabela ASCII no Unix / Linux:

$ man ascii


Descobriremos qual código corresponde EOFescrevendo um pequeno programa em C. No ANSI C, uma constante é EOFdefinida em stdio.h, faz parte da biblioteca padrão. Geralmente escrito para esta constante -1. Você pode salvar o seguinte código em um arquivo printeof.c, compilá-lo e executá-lo:

#include <stdio.h>

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

Compile e rode o programa:

$ gcc -o printeof printeof.c

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

Eu tenho este programa, testado no Mac OS e no Ubuntu, informa que EOFé igual -1. Existe algum caractere com esse código? Aqui, novamente, você pode verificar os códigos de caracteres na tabela ASCII, pode ver a tabela Unicode e descobrir em que intervalo os códigos de caracteres podem estar. Agiremos de maneira diferente: iniciaremos o interpretador Python e usaremos a função padrão chr()para nos fornecer o símbolo correspondente ao 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 esperado, o caractere com o código -1não existe. Então, no final, EOFe a verdade não é um símbolo. Passamos agora para a segunda declaração em consideração.

Não há caracteres especiais no final dos arquivos.


Talvez EOF- esse é um caractere especial que pode ser encontrado no final do arquivo? Suponho que você já saiba a resposta. Mas vamos verificar cuidadosamente nossa suposição.

Pegue um arquivo de texto simples, helloworld.txt , e exiba seu conteúdo em representação hexadecimal. Para fazer isso, você pode usar o comando xxd:

$ cat helloworld.txt
Hello world!

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

Como você pode ver, o último caractere do arquivo tem um código 0a. Na tabela ASCII, você pode descobrir que esse código corresponde a um caractere nl, ou seja, a um caractere de nova linha. Você pode descobrir isso usando Python:

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

Assim. EOF- Este não é um símbolo e, no final dos arquivos, não há um símbolo especial. O que é isso EOF?

O que é um EOF?


EOF(fim de arquivo) é um estado que pode ser detectado pelo aplicativo em uma situação em que a operação de leitura de arquivo chega ao fim.

Vamos dar uma olhada em como é possível detectar o estado EOFem diferentes linguagens de programação ao ler um arquivo de texto usando as ferramentas de alto nível de entrada e saída fornecidas por esses idiomas. Para fazer isso, escreveremos uma versão muito simples cat, que será chamada mcat. Ele lê o byte de texto ASCII (caractere) e verifica explicitamente EOF. Escreveremos o programa nos seguintes idiomas:

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

Aqui está um repositório com código de amostra. Prosseguimos com a análise deles.

ANSI C


Vamos começar com o venerável C. O programa apresentado aqui é uma versão modificada catdo livro "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;
}

Compilação:

$ gcc -o mcat mcat.c

Lançamento:

$ ./mcat helloworld.txt
Hello world!

Aqui estão algumas explicações sobre o código acima:

  • O programa abre o arquivo passado para ele como um argumento de linha de comando.
  • O loop whilecopia dados do arquivo para o fluxo de saída padrão. Os dados são copiados byte a byte, isso ocorre até o final do arquivo.
  • Quando o programa chega EOF, ele fecha o arquivo e sai.

Python 3


No Python, não há mecanismo para verificação explícita EOF, semelhante ao disponível no ANSI C. Mas se você ler o arquivo caractere por caractere, poderá revelar o estado EOFse a variável que armazena o próximo caractere lido estiver vazia:

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

Execute o programa e veja os resultados retornados para ele:

$ python mcat.py helloworld.txt
Hello world!

Aqui está uma versão mais curta do mesmo exemplo, escrita em Python 3.8+. Aqui o operador é usado : = (é chamado de "operador de morsa" ou "operador de morsa"):

# mcat38.py
import sys

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

Execute este código:

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

Vai


No Go, você pode verificar explicitamente o erro retornado por Read () para ver se isso indica que chegamos ao final do arquivo:

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

Execute o programa:

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

JavaScript (Node.js)


O Node.js não tem mecanismo para procurar explicitamente EOF. Porém, quando, ao chegar ao final do arquivo, é feita uma tentativa de ler outra coisa, o evento do fluxo final é gerado .

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

Execute o programa:

$ node mcat.js helloworld.txt
Hello world!

EOF: There will be no more data.

Mecanismos do sistema de baixo nível


Como os mecanismos de E / S de alto nível usados ​​nos exemplos acima determinam o final do arquivo? No Linux, esses mecanismos usam direta ou indiretamente a chamada de sistema read () fornecida pelo kernel. Uma função (ou macro) getc()de C, por exemplo, usa uma chamada do sistema read()e retorna EOFse read()indicar o estado de chegada ao final do arquivo. Nesse caso, read()retorna 0. Se você descreve tudo isso na forma de um diagrama, obtém o seguinte:


Acontece que a função é getc()baseada read().

Escreveremos uma versão catnomeada syscatusando apenas chamadas do sistema Unix. Faremos isso não apenas por interesse, mas também porque isso pode nos trazer algum benefício.

Aqui está este programa escrito em 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;
}

Executá-lo:

$ gcc -o syscat syscat.c

$ ./syscat helloworld.txt
Hello world!

Esse código usa o fato de que a função read(), indicando o fim do arquivo, é retornada 0.

Aqui está o mesmo programa escrito em 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)

Executá-lo:

$ python syscat.py helloworld.txt
Hello world!

Aqui está a mesma coisa escrita em 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)

Execute este código também:

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

Sumário


  • EOF - isto não é um símbolo.
  • Não há caracteres especiais no final dos arquivos.
  • EOF - este é o estado relatado pelo kernel e que pode ser detectado pelo aplicativo no caso em que a operação de leitura de dados chega ao final do arquivo.
  • No ANSI C EOF, isso novamente não é um caractere. Essa é a constante definida stdio.hna qual o valor -1 geralmente é escrito.
  • Um "caractere" EOFnão pode ser encontrado em uma tabela ASCII ou em Unicode.

Queridos leitores! Você conhece algum equívoco mais ou menos difundido do mundo dos computadores?


All Articles