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
|
||||
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 caso de um erro, ou se o fim do arquivo for alcançado sem que algo seja lido
|
||||
(por exemplo, ao ler um arquivo vazio ou numa leitura interativa cancelada com a
|
||||
combinação de teclas =Ctrl+D=).
|
||||
No nível do sistema, o acesso a arquivos é sempre estabelecido através de uma
|
||||
estrutura complexa identificada por um número inteiro: o descritor de arquivos,
|
||||
assunto da [[../10-dataio/README.org#headline-2][aula 10]]. Então, quando utilizamos chamadas de sistema, como =read= e
|
||||
=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á
|
||||
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=.
|
||||
| Macro | Significado |
|
||||
|---------------+----------------------------------------------------|
|
||||
| =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~).
|
||||
- =ferror(FILE *stream)= - Testa o estado do indicador de erros (~0 = não definido~).
|
||||
Nas funções da biblioteca padrão, que abstrai outras estruturas de controle
|
||||
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:
|
||||
|
||||
#+begin_src c
|
||||
#include <stdio.h>
|
||||
|
||||
#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
|
||||
| Macro | Significado |
|
||||
|--------+----------------------------------------------------------|
|
||||
| =stdin= | Ponteiro para =FILE= associado ao descritor de arquivos =0=. |
|
||||
| =stdout= | Ponteiro para =FILE= associado ao descritor de arquivos =1=. |
|
||||
| =stderr= | Ponteiro para =FILE= associado ao descritor de arquivos =2=. |
|
||||
|
||||
** O problema da quebra de linha
|
||||
|
||||
|
@ -266,28 +250,198 @@ buf[10] | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 | 0x00 | 0x00 | lixo | lixo | lixo |
|
|||
+------+------+------+------+------+------+------+------+------+------+
|
||||
#+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
|
||||
estrutura complexa identificada por um número inteiro: o descritor de arquivos,
|
||||
assunto da [[../10-dataio/README.org#headline-2][aula 10]]. Então, quando utilizamos chamadas de sistema, como =read= e
|
||||
=write=, o fluxo de dados (/stream/) é passado como o número de um descritor de
|
||||
arquivos. Por isso, nós utilizamos as macros:
|
||||
No caso de sucesso, a função =fgets= retorna o endereço do buffer ou =NULL=,
|
||||
no caso de um erro, ou se o fim do arquivo for alcançado sem que algo seja lido
|
||||
(por exemplo, ao ler um arquivo vazio ou numa leitura interativa cancelada com a
|
||||
combinação de teclas =Ctrl+D=).
|
||||
|
||||
| Macro | Significado |
|
||||
|---------------+----------------------------------------------------|
|
||||
| =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=. |
|
||||
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
|
||||
o que aconteceu com as funções =feof= e/ou =ferror=.
|
||||
|
||||
Nas funções da biblioteca padrão, que abstraem outras estruturas de controle
|
||||
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:
|
||||
- =feof(FILE *stream)= - Testa o estado do indicador de fim de arquivo (~0 = não difinido~).
|
||||
- =ferror(FILE *stream)= - Testa o estado do indicador de erros (~0 = não definido~).
|
||||
|
||||
| Macro | Significado |
|
||||
|--------+----------------------------------------------------------|
|
||||
| =stdin= | Ponteiro para =FILE= associado ao descritor de arquivos =0=. |
|
||||
| =stdout= | Ponteiro para =FILE= associado ao descritor de arquivos =1=. |
|
||||
| =stderr= | Ponteiro para =FILE= associado ao descritor de arquivos =2=. |
|
||||
Se isso for relevante, nós podemos chamar a função =fgets= desta forma:
|
||||
|
||||
#+begin_src c
|
||||
#include <stdio.h>
|
||||
|
||||
#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