mirror of
https://gitlab.com/blau_araujo/cblc.git
synced 2025-05-09 18:16:37 -03:00
conteúdo da aula 12
This commit is contained in:
parent
c4eb988b85
commit
3b99cba06d
1 changed files with 212 additions and 58 deletions
|
@ -87,47 +87,31 @@ descritor de arquivos =0= (entrada padrão).
|
||||||
De pronto, fica evidente a facilidade que nós termos para alterar o tamanho do
|
De pronto, fica evidente a facilidade que nós termos para alterar o tamanho do
|
||||||
vetor =buf= quando necessário: basta alterar a constante simbólica =BUFMAX=.
|
vetor =buf= quando necessário: basta alterar a constante simbólica =BUFMAX=.
|
||||||
|
|
||||||
*** Retorno nulo ambíguo
|
*** Uma nota sobre o tipo ponteiro para FILE
|
||||||
|
|
||||||
No caso de sucesso, a função =fgets= retorna o endereço do buffer ou =NULL=,
|
No nível do sistema, o acesso a arquivos é sempre estabelecido através de uma
|
||||||
no caso de um erro, ou se o fim do arquivo for alcançado sem que algo seja lido
|
estrutura complexa identificada por um número inteiro: o descritor de arquivos,
|
||||||
(por exemplo, ao ler um arquivo vazio ou numa leitura interativa cancelada com a
|
assunto da [[../10-dataio/README.org#headline-2][aula 10]]. Então, quando utilizamos chamadas de sistema, como =read= e
|
||||||
combinação de teclas =Ctrl+D=).
|
=write=, o fluxo de dados (/stream/) é passado como o número de um descritor de
|
||||||
|
arquivos. Por isso, nós utilizamos as macros:
|
||||||
|
|
||||||
Como você pode ver, o significado do retorno =NULL= é ambíguo, mas isso só será
|
| Macro | Significado |
|
||||||
uma preocupação se estivermos lendo arquivos. De todo modo, nós podemos testar
|
|---------------+----------------------------------------------------|
|
||||||
o que aconteceu com as funções =feof= e/ou =ferror=.
|
| =STDIN_FILENO= | Valor inteiro relativo ao descritor de arquivos =0=. |
|
||||||
|
| =STDOUT_FILENO= | Valor inteiro relativo ao descritor de arquivos =1=. |
|
||||||
|
| =STDERR_FILENO= | Valor inteiro relativo ao descritor de arquivos =2=. |
|
||||||
|
|
||||||
- =feof(FILE *stream)= - Testa o estado do indicador de fim de arquivo (~0 = não difinido~).
|
Nas funções da biblioteca padrão, que abstrai outras estruturas de controle
|
||||||
- =ferror(FILE *stream)= - Testa o estado do indicador de erros (~0 = não definido~).
|
associadas aos descritores de arquivos, os fluxos de dados são passados como
|
||||||
|
ponteiros para essas estruturas através do tipo =FILE *= (ponteiro para =FILE=).
|
||||||
|
Os fluxos de dados padrão também são abstraídos como macros definidas na
|
||||||
|
biblioteca de funções:
|
||||||
|
|
||||||
Se isso for relevante, nós podemos chamar a função =fgets= desta forma:
|
| Macro | Significado |
|
||||||
|
|--------+----------------------------------------------------------|
|
||||||
#+begin_src c
|
| =stdin= | Ponteiro para =FILE= associado ao descritor de arquivos =0=. |
|
||||||
#include <stdio.h>
|
| =stdout= | Ponteiro para =FILE= associado ao descritor de arquivos =1=. |
|
||||||
|
| =stderr= | Ponteiro para =FILE= associado ao descritor de arquivos =2=. |
|
||||||
#define BUFMAX 10
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
char buf[BUFMAX];
|
|
||||||
|
|
||||||
if (fgets(buf, BUFMAX, stdin) == NULL) {
|
|
||||||
// Só preciamos testar se o retorno for NULL...
|
|
||||||
if (feof(stdin)) {
|
|
||||||
// Se feof() retornar algo diferente de zero...
|
|
||||||
fprintf(stderr, "Fim de arquivo alcançado.\n");
|
|
||||||
} else if (ferror(stdin)) {
|
|
||||||
// Se ferror() retornar algo diferente de zero...
|
|
||||||
fprintf(stderr, "Erro de leitura.\n");
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** O problema da quebra de linha
|
** O problema da quebra de linha
|
||||||
|
|
||||||
|
@ -266,28 +250,198 @@ buf[10] | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x00 | 0x00 | lixo | lixo | lixo |
|
||||||
+------+------+------+------+------+------+------+------+------+------+
|
+------+------+------+------+------+------+------+------+------+------+
|
||||||
#+end_example
|
#+end_example
|
||||||
|
|
||||||
** Uma nota sobre o tipo ponteiro para FILE
|
** Retorno nulo ambíguo
|
||||||
|
|
||||||
No nível do sistema, o acesso a arquivos é sempre estabelecido através de uma
|
No caso de sucesso, a função =fgets= retorna o endereço do buffer ou =NULL=,
|
||||||
estrutura complexa identificada por um número inteiro: o descritor de arquivos,
|
no caso de um erro, ou se o fim do arquivo for alcançado sem que algo seja lido
|
||||||
assunto da [[../10-dataio/README.org#headline-2][aula 10]]. Então, quando utilizamos chamadas de sistema, como =read= e
|
(por exemplo, ao ler um arquivo vazio ou numa leitura interativa cancelada com a
|
||||||
=write=, o fluxo de dados (/stream/) é passado como o número de um descritor de
|
combinação de teclas =Ctrl+D=).
|
||||||
arquivos. Por isso, nós utilizamos as macros:
|
|
||||||
|
|
||||||
| Macro | Significado |
|
Como você pode ver, o significado do retorno =NULL= é ambíguo, mas isso só será
|
||||||
|---------------+----------------------------------------------------|
|
uma preocupação se estivermos lendo arquivos. De todo modo, nós podemos testar
|
||||||
| =STDIN_FILENO= | Valor inteiro relativo ao descritor de arquivos =0=. |
|
o que aconteceu com as funções =feof= e/ou =ferror=.
|
||||||
| =STDOUT_FILENO= | Valor inteiro relativo ao descritor de arquivos =1=. |
|
|
||||||
| =STDERR_FILENO= | Valor inteiro relativo ao descritor de arquivos =2=. |
|
|
||||||
|
|
||||||
Nas funções da biblioteca padrão, que abstraem outras estruturas de controle
|
- =feof(FILE *stream)= - Testa o estado do indicador de fim de arquivo (~0 = não difinido~).
|
||||||
associadas aos descritores de arquivos, os fluxos de dados são passados como
|
- =ferror(FILE *stream)= - Testa o estado do indicador de erros (~0 = não definido~).
|
||||||
ponteiros para essas estruturas através do tipo =FILE *= (ponteiro para =FILE=).
|
|
||||||
Os fluxos de dados padrão também são abstraídos como macros definidas na
|
|
||||||
biblioteca de funções:
|
|
||||||
|
|
||||||
| Macro | Significado |
|
Se isso for relevante, nós podemos chamar a função =fgets= desta forma:
|
||||||
|--------+----------------------------------------------------------|
|
|
||||||
| =stdin= | Ponteiro para =FILE= associado ao descritor de arquivos =0=. |
|
#+begin_src c
|
||||||
| =stdout= | Ponteiro para =FILE= associado ao descritor de arquivos =1=. |
|
#include <stdio.h>
|
||||||
| =stderr= | Ponteiro para =FILE= associado ao descritor de arquivos =2=. |
|
|
||||||
|
#define BUFMAX 10
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
char buf[BUFMAX];
|
||||||
|
|
||||||
|
if (fgets(buf, BUFMAX, stdin) == NULL) {
|
||||||
|
// Só preciamos testar se o retorno for NULL...
|
||||||
|
if (feof(stdin)) {
|
||||||
|
// Se feof() retornar algo diferente de zero...
|
||||||
|
fprintf(stderr, "Fim de arquivo alcançado.\n");
|
||||||
|
} else if (ferror(stdin)) {
|
||||||
|
// Se ferror() retornar algo diferente de zero...
|
||||||
|
fprintf(stderr, "Erro de leitura.\n");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** Conversão para números
|
||||||
|
|
||||||
|
Diferente da função =scanf=, =fgets= apenas lê caracteres e escreve num buffer
|
||||||
|
como uma string (caracteres terminados com ='\0'=). Isso significa que, se
|
||||||
|
estivermos interessados em valores numéricos, os bytes no buffer terão que
|
||||||
|
ser tratado, geralmente em três etapas:
|
||||||
|
|
||||||
|
- Remoção da quebra de linha, se houver uma;
|
||||||
|
- Validação dos caracteres segundo o tipo esperado (=int=, =float=, =double=, etc);
|
||||||
|
- Conversão.
|
||||||
|
|
||||||
|
Nós já falamos sobre a remoção da quebra de linha, então podemos nos concentrar
|
||||||
|
nas duas etapas seguintes. Porém, os métodos de conversão mais comuns já integram
|
||||||
|
alguma forma de validação (geralmente, como retornos). Sendo assim, vamos nos
|
||||||
|
concentrar no que pode ser feito para obter os tipos esperados.
|
||||||
|
|
||||||
|
*** Conversão para inteiros com 'sscanf'
|
||||||
|
|
||||||
|
Provavelmente, a forma mais simples e direta para converter o conteúdo
|
||||||
|
de um buffer para tipos numéricos é com a função =sscanf=:
|
||||||
|
|
||||||
|
#+begin_src c
|
||||||
|
char buf[BUFMAX];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fgets(buf, BUFMAX, stdin);
|
||||||
|
|
||||||
|
sscanf(buf, "%d", &i);
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Aqui, se houver dígitos válidos no começo de =buf=, eles serão convertidos
|
||||||
|
em seu correspondente inteiro na base 10, exatamente como seria feito
|
||||||
|
lendo uma entrada digitada no terminal com a função =scanf= -- e com suas
|
||||||
|
mesmas limitações:
|
||||||
|
|
||||||
|
- A dificuldade de detectar entradas inválidas (por exemplo, =42abc= seria
|
||||||
|
casado e convertido para o inteiro =42=);
|
||||||
|
- O retorno será apenas a quantidade de casamentos com a string de formato
|
||||||
|
que forem convertidos e escritos nas variáveis, ou =EOF=, no caso do fim
|
||||||
|
do buffer ser alcançado antes de um primeiro casamento ou no caso de
|
||||||
|
um erro;
|
||||||
|
- Não oferece uma detecção de erros detalhada, como com as funções que
|
||||||
|
alteram a variável =errno=.
|
||||||
|
|
||||||
|
Além disso, =sscanf= não lida bem com valores fora dos limites do tipo de
|
||||||
|
destino, por exemplo:
|
||||||
|
|
||||||
|
#+begin_src c
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
char str[] = "9999999999";
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
sscanf(str, "%d", &i);
|
||||||
|
|
||||||
|
printf("Valor de INT_MAX : %d\n", INT_MAX);
|
||||||
|
printf("Dígitos na string: %s\n", str);
|
||||||
|
printf("Valor convertido : %d\n", i);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
|
||||||
|
Neste exemplo, o comportamento de =sscanf= é indeterminado, mas compilaria
|
||||||
|
sem erros:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ gcc -Wall teste.c
|
||||||
|
:~$ ./a.out
|
||||||
|
Valor de INT_MAX : 2147483647
|
||||||
|
Dígitos na string: 9999999999
|
||||||
|
Valor convertido : 1410065407
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Aqui, =9999999999= é convertido para um valor com 5 bytes (=0x02540be3ff=),
|
||||||
|
dos quais, apenas os 4 últimos cabem no espaço do tipo =int= (=0x540be3ff=),
|
||||||
|
resultando no valor =1410065407=.
|
||||||
|
|
||||||
|
*** Conversão para inteiros longos com 'strtol'
|
||||||
|
|
||||||
|
A função =strtol= converte a parte inicial de uma string para =long= de
|
||||||
|
acordo com a base de numeração indicada. O endereço do primeiro caractere
|
||||||
|
inválido é atribuído a um ponteiro -- e isso nos dá uma excelente forma
|
||||||
|
de controle da conversão.
|
||||||
|
|
||||||
|
Simplificadamente, este seria o protótipo da função:
|
||||||
|
|
||||||
|
#+begin_src c
|
||||||
|
long strtol(char *str, char **endptr, int base);
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Exemplo:
|
||||||
|
|
||||||
|
#+begin_src c
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
char str[] = "1234567abc";
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
long i = strtol(str, &end, 10);
|
||||||
|
|
||||||
|
printf("Valor convertido : %ld\n", i);
|
||||||
|
printf("Primeiro caractere inválido: 0x%02x\n", *end);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Compilando e executando:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ gcc -Wall teste.c
|
||||||
|
:~$ ./a.out
|
||||||
|
Valor convertido: 1234567
|
||||||
|
Primeiro caractere inválido: 0x61
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
#+begin_quote
|
||||||
|
O caractere =0x61= é =a=, na tabela ASCII.
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
Outros detalhes:
|
||||||
|
|
||||||
|
- No caso de valores maiores que o limite máximo de um inteiro longo,
|
||||||
|
o retorno será =LONG_MAX=.
|
||||||
|
- No caso de valores menores que o limite mínimo de um inteiro longo,
|
||||||
|
o retorno será =LONG_MIN=.
|
||||||
|
- Em ambos os casos, a variável =errno= recebe o valor de =ERANGE= (valor
|
||||||
|
fora da faixa do tipo).
|
||||||
|
- Se o valor for inválido para a base de numeração, =errno= recebe
|
||||||
|
o valor de =EINVAL= (valor inválido para a base).
|
||||||
|
- A descrição do erro em =errno= pode ser impressa com a função =perror=,
|
||||||
|
chamada logo após =strtol=.
|
||||||
|
- Se não houver dígitos e caracteres válidos no começo da string,
|
||||||
|
o endereço do primeiro caractere inválido será o mesmo da string
|
||||||
|
e a função retornará =0=.
|
||||||
|
- O ponto negativo de =strtol= é que o retorno é =long= e, se precisarmos
|
||||||
|
de um valor =int=, teremos que verificar os limites máximo e mínimo
|
||||||
|
do valor convertido.
|
||||||
|
|
||||||
|
*** Outras funções de conversão
|
||||||
|
|
||||||
|
- =strtoll= - Idêntica a =strtol=, mas converte para =long long int=.
|
||||||
|
- =strtod=, =strtof= e =strtold= - Funcionam como =strtol=, mas convertem
|
||||||
|
para =double=, =float= e =long double=, respectivamente, sem a passagem
|
||||||
|
de uma base de numeração.
|
||||||
|
- =atoi=, =atof= e =atol= - São muito simples, mas devem ser evitadas pela
|
||||||
|
falta de validação e de meios para tratamento de erros.
|
||||||
|
|
Loading…
Add table
Reference in a new issue