EnzoFerber
(usa FreeBSD)
Enviado em 19/02/2016 - 06:47h
Olá, bom dia.
Existem diversos erros no seu código, como a definição de
buff , o uso de
fflush em
stdin , o uso de
gets , o uso de
strcmp com os parâmetros errados e a lógica da utilização da função
strtok . Além de não pegar o retorno de
strtok , você constantemente
reseta o ponteiro de posição dela.
O manual:
http://linux.die.net/man/3/strtok
Considere o seguinte programa (que é incorreto):
/* test.c
* Demonstracao do uso *incorreto* de STRTOK
*
* (C) 2016 - Enzo Ferber, <enzoferber@gmail.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *buf = malloc(100);
char *t;
strcpy(buf, "Programa mostrando o uso incorreto de strtok.");
while((t = strtok(buf, " ")))
printf("token: %s\n", t);
free(buf);
return 0;
}
Quando compilar e rodar, o programa irá ficar em um loop infinito, imprimindo
token: Programa . A função
strtok armazena internamente a posição onde parou a última análise. Porém, toda vez que o argumento é não nulo, ela redefine essa posição.
*
buf = Programa mostrando o uso incorreto de strtok.
pos = 0123456789...................................
Considere
pos as posições dos caracteres na string. Agora chamamos:
t = strtok(buf, " ");
O mecanismo de
strtok irá receber esse pointeiro e
setar um ponteiro estático (
static ) para essa string. Depois,
strtok irá encontrar o token que você delimitou. E o encontra na posição 8 de buf. (
buf[8] == ' ' )
O que
strtok faz agora é:
1. coloca um caractere NULL na posicao buf[8] de maneira que você possa
manipular o token como um string normal.
2. atribui a posicao 9 ao ponteiro de referencia
3. Retorna o atual ponteiro de referencia
Todas essas informações podem ser deduzidas lendo o manual de
strtok(3) em
http://linux.die.net/man/3/strtok ou digitando
man strtok no terminal.
Primeira chamada:
buf = Programa mostrando o uso incorreto de strtok.
pos = 0123456789...................................
| |- nova posicao
|
|- posicao atual
|- t
O problema é que uma segunda chamada a
strtok utilizando o mesmo token que ele retornou vai retornar o
primeiro token novamente! Como toda vez que você chama
strtok com um argumento diferente de
NULL ele
reseta o ponteiro de posição, você termina analisando apenas o primeiro
token da sua string (se todo o resto do código estivesse correto). Além do mais, a lógica por traz dos testes à
contpp está incorreta. Pois após encontrar um token a primeira vez, o referido teste sempre será falso, o que resultará na avaliação apenas do bloco
else para
todas as outras linhas subsequentes : isto faz com que as outras linhas tenham referências completamente erradas dentro de
strtok .
Resumindo, são tantos os erros que optei por postar um novo código. Para tentar demonstrar melhor o conceito por trás de
strtok , o código analisa um arquivo palavra por palavra em um
estilo parecido com
strtok : utilizando armazenamento estático de posições. Ambas as funções
next_word e
next_line armazenam as referências de forma estática. A diferença com relação ao design de
strtok é que essas funções só
resetam as referências baseadas em uma condição interna, e não uma condição explícita nos parâmetros. Isto simplifica o uso sequencial e a API, porém custa flexibilidade. (Também é interessante notar que a função
next_word entende por palavras apenas sequências de letras e números, qualquer coisa diferente é tratado como um delimitador e uma palavra é retornada.)
/* text.c
*
* (C) 2016 - Enzo Ferber, <enzoferber@gmail.com>
*/
#include <stdio.h> /* printf */
#include <stdlib.h> /* malloc */
#include <string.h> /* strncmp */
#include <ctype.h> /* isalnum */
#define LINE_MAX 500
#define WORD_MAX LINE_MAX
char *next_word(char *line)
{
static char *ref = NULL;
char *word = malloc(WORD_MAX);
char *w = word;
if(!word) return NULL;
if(!ref) ref = line;
while(*ref) {
if(isalnum(*ref)) {
*w++ = *ref++;
} else {
*w = 0x0;
ref++;
return word;
}
}
return (ref = NULL);
}
char *next_line(char *filepath)
{
static FILE *fp = NULL;
char *line;
if(!fp) {
if(!(fp = fopen(filepath, "r"))) {
perror("fopen");
return NULL;
}
}
/* fim de arquivo */
if(feof(fp)) {
fclose(fp);
return (char*)(fp = NULL);
}
/* buffer para linha */
if(!(line = malloc(LINE_MAX))) {
perror("malloc");
fclose(fp);
return (char*)(fp = NULL);
}
/* proxima linha */
if(!(fgets(line, LINE_MAX, fp))) {
fclose(fp);
free(line);
return (char*)(fp = NULL);
}
return line;
}
int main(int argc, char *argv[])
{
char *w, *line;
int count = 0;
if(argc < 3) return 0;
while((line = next_line(argv[1]))) {
while((w = next_word(line))) {
if(!strncmp(argv[2], w, LINE_MAX)) count++;
free(w);
}
free(line);
}
printf("%s encontrada %d vezes no arquivo %s.\n", argv[2], count, argv[1]);
return 0;
}
$ gcc -o text text.c -Wall -Wextra -Werror -pedantic --std=c11
$ ./text text.c char
char encontrada 16 vezes no arquivo text.c.
$
Espero ter ajudado,
Qualquer coisa posta denovo.
[]'s
Enzo Ferber
$ indent -kr -i8 src.c
"(...)all right-thinking people know that (a) K&R are _right_ and (b) K&R are right." - linux/Documentation/CodingStyle - TORVALDS, Linus.