• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

seth

Dúvida com strtok

12 mensagens neste tópico

Estou com um bug esquisito no meu código. Num projecto pessoal, pretendo que seja feita a leitura das configurações da aplicação a partir de um ficheiro externo. Leio o ficheiro linha a linha e uso a função strtok para dividir cada linha pelas palavras. Quase tudo funciona bem, excepto que ao ler o endereço de um directório, caso apanha um 'P' ou um 'C' a seguir a uma '/' a função strtok termina a palavra aí.

Por exemplo:

/home/xpto/Project/teste é lido apenas como /home/xpto/

A minha pergunta é esta: /P ou /C podem ser interpretados como algum caracter especial (terminação??) no tratamento de strings em C?

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define CONF_FILE "main.conf"
#define MAXLINE 128

FILE *fin;
char line[MAXLINE];
char *pda, *pc;

int analisaLinhasConf(char *linha)
{
int i = 0;
char *cmp;
char tokens[] = " ";
char *destPDA = "PDA", *destPC = "PC";
char *final;

	if(linha[0] == '#')
	{	
		printf("COMENTARIO!\n");
		return 0;
	}

	final = strtok(linha, tokens);

	if((cmp = strstr(final, destPDA) != NULL))
	{
		pda = strtok(NULL, final);
		int tmpPDA = strlen(pda);
		pda[tmpPDA-1] = '\0';
		printf("%s\n",pda);
		printf("Tamanho da string: %d\n",strlen(pda));			
		return 0;
	}

	if((cmp = strstr(final, destPC) != NULL))
	{
		pc = strtok(NULL, final);
		int tmpPC = strlen(pda);
		pda[tmpPC-1] = '\0';
		printf("%s\n",pc);
		printf("Tamanho da string: %d\n",strlen(pc));
		return 0;
	}
}

int leFicheiroConf (void) 
{
if	(	(fin = fopen(CONF_FILE, "r")) == NULL) 
{
		fprintf(stderr, "Impossivel abrir ficheiro main.conf\n");
		return 1;
}

while(fgets(line, MAXLINE, fin) != NULL)
			analisaLinhasConf(line);

return 0;
}


int main()
{
int ret;

if( (ret = leFicheiroConf()) != 0)
	printf("leFicheiroConf terminou com código %d\n", ret);

return 0;
}


Estou a desenvolver o código em Linux recorrendo ao gcc e a um editor de texto (gedit).

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não. O caracter de escape é '\', não '/'.

Qual é o conteúdo do main.conf?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Mas será que não existe nenhuma combinação '/P' ou '/C' que faça terminar a string de resultante de strtok? É que é a única justificação que encontro

main.conf

#Ficheiro exemplo de configuração
#Linhas começadas por # serão ignoradas



PDA /media/disk
PC /home/rui/Projects/Sync/teste

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Por alguma razão a que ainda não consegui chegar, a variável pda está a ser incrementada quando utilizas a strtok no 2º if.

Já agora 2 reparos: estás a declarar ints no meio do código. Isso não é C, é C++, ou então um relaxamento do C.

E na linha tmpPC = strlen(pda); não devia ser strlen(pc)?

É possível que seja um efeito secundário (que não consigo compreender) da utilização de line sempre para ler do ficheiro. Se tornares pda e pc arrays de strings em vez de apontadores e copiares para lá o conteúdo apontado por linha, nos testes que fiz aqui já não truncou o 2º caminho.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

final = strtok(linha, tokens);
[...]
pda = strtok(NULL, final);
[...]
pc = strtok(NULL, final);

O teu problema está nessas linhas. O que está a acontecer é o seguinte.

Imaginemos que a linha que estás a processar é "PC /home/rui/Projects/Sync/teste".

Ao fazeres o primeiro strtok, ele vai processar a linha com os tokens (" "). O teu final ficará então com o valor de "PC".

Portanto irás entrar no 2º if. Ao fazeres então o seguinte strtok (Estás a usar NULL, ou seja, para continuar a processar onde tinha parado antes, mas agora os tokens serão os da variável final ("PC"). Portanto strtok vai procurar na string "/home/rui/Projects/Sync/teste" até encontrar a letra 'P' ou 'C'. O que encontra, e devolve então a dita string que será "/home/rui/".

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Antes de mais nada, muito obrigado ao TheDark e ao Betovsky pela ajuda.  :D

final = strtok(linha, tokens);
[...]
pda = strtok(NULL, final);
[...]
pc = strtok(NULL, final);

O teu problema está nessas linhas. O que está a acontecer é o seguinte.

Imaginemos que a linha que estás a processar é "PC /home/rui/Projects/Sync/teste".

Ao fazeres o primeiro strtok, ele vai processar a linha com os tokens (" "). O teu final ficará então com o valor de "PC".

Portanto irás entrar no 2º if. Ao fazeres então o seguinte strtok (Estás a usar NULL, ou seja, para continuar a processar onde tinha parado antes, mas agora os tokens serão os da variável final ("PC"). Portanto strtok vai procurar na string "/home/rui/Projects/Sync/teste" até encontrar a letra 'P' ou 'C'. O que encontra, e devolve então a dita string que será "/home/rui/".

Era exactamente isso que sucedia. Agora que corrigi já funciona como pretendido.

Por alguma razão a que ainda não consegui chegar, a variável pda está a ser incrementada quando utilizas a strtok no 2º if.

Já agora 2 reparos: estás a declarar ints no meio do código. Isso não é C, é C++, ou então um relaxamento do C.

E na linha tmpPC = strlen(pda); não devia ser strlen(pc)?

Tens razão nos dois pontos. Onde tinha pda era suposto pc e, realmente, misturar C com C++ não fica lá muito bem.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

A função alguma vez entra nos 2 ifs na mesma chamada? Estive ali às voltas e não dei por isso ::D

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Numa chamada apenas processa uma linha e, como tal, apenas entrará num dos if's.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

E de cada vez que chega ao fim de um if, faz return 0, logo nunca entra no outro if. E de cada vez que volta a entrar na função, chama a função strtok com um apontador válido.

Assim não consigo entender os problemas que estão a acontecer, porque não se aplica o que o betovsky disse. Ou então aplica-se e eu não percebi como :D

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

O que o betovsky disse faz todo o sentido e resolveu-me o problema.

Passo a descrever: Se não for comentário avança o primeiro if. A variável final ficará com o 1º token da linha (PDA ou PC). Depois essa variável final será comparada com PDA ou PC para decidir em qual dos if's entra.

Após entrar num if, é feito outro strtok para obter o 2º token (endereço da directoria neste caso). Neste strtok eu estava a chamar com argumentos (NULL, final). Como final tinha ficado com PC, a divisão dos tokens estava a ser feita com base nestes 2 caracteres, dái o problema com os P e C que, por acaso, sucediam após a barra.

Agora estou a usar um ";" para marcar o final de cada linha no ficheiro .CONF e por isso estou a usar, na 2ª chamada a strtok, os argumentos (NULL, ";"). Desta forma todo o endereço é lido até ao final da linha.

No entanto agora estou com outro problema: desta forma obtenho o endereço a partir do ficheiro .CONF e se fizer printf aparece correctamente, no entanto ao usar opendir(), só funciona para a última linha. Por isto parece-me que está a ser incluido algum caracter entre a 1ª e 2ª linha que inviabiliza a função opendir(). Alguém me pode ajudar com isto?

O código actual é o seguinte:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>

#define CONF_FILE "main.conf"
#define MAXLINE 128

FILE *fin;
char line[MAXLINE];
char *pda, *pc;
DIR *directory1, *directory2;
struct dirent *d1, *d2;

int analisaLinhasConf(char *linha)
{
int i = 0;
char *cmp;
char *tokens = " ";
char *destPDA = "PDA", *destPC = "PC";
char *final;

	if(linha[0] == '#')
	{	
		//printf("COMENTARIO!\n");
		return 0;
	}

	final = strtok(linha, tokens);

	if((cmp = strstr(final, destPDA)) != NULL)
	{
		pda = strtok(NULL, ";");
		//pda[strlen(pda)-1] = '\0';
		printf("%s\n",pda);
		printf("Tamanho da string: %d\n",strlen(pda));				
		return 0;
	}

	if((cmp = strstr(final, destPC)) != NULL)
	{
		pc = strtok(NULL, ";");
		//pc[strlen(pc)-1] = '\0';
		printf("%s\n",pc);
		printf("Tamanho da string: %d\n",strlen(pc));	
		return 0;
	}
return 0;
}

int leFicheiroConf (void) 
{
if	(	(fin = fopen(CONF_FILE, "r")) == NULL) 
{
		fprintf(stderr, "Impossivel abrir ficheiro main.conf\n");
		return 1;
}

while(fgets(line, MAXLINE, fin) != NULL)
			analisaLinhasConf(line);

return 0;
}

int abreDirectorios (void)
{
printf("Abrindo directorio 1: \n");
if( (directory1 = opendir(pda)) == NULL) return -1;
while( (d1 = readdir(directory1)) != NULL)
	printf("%s\n", d1->d_name);

closedir(directory1);

printf("Abrindo directorio 2: \n");
if( (directory2 = opendir(pc)) == NULL) return -2;
while( (d2 = readdir(directory2)) != NULL)
	printf("%s\n", d2->d_name);

closedir(directory2);

return 0;
}

int fechaDirectorias (void)
{
closedir(directory1);
closedir(directory2);
return 0;
}


int main()
{
int ret;

if( (ret = leFicheiroConf()) != 0)
	printf("leFicheiroConf terminou com código %d\n", ret);

if( (ret = abreDirectorios()) != 0)
	printf("Erro a abrir directorias!\nRC:%d\n", ret);

return 0;
}

#Ficheiro exemplo de configuração

#Linhas começadas por # serão ignoradas









PC /home/rui/Projects/Sync/teste;

PDA /media/EI04099;

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Ok, faz sentido, eu é que não tinha percebido o que ele quis dizer. Percebi que após sair do 1º if ia entrar no 2º, o que era impossível porque tem um return no fim de cada if. E depois também não tinha reparado que estavas a usar a final nas chamadas seguintes à strtok.

O novo problema vai do que eu disse antes. Estás a usar sempre o mesmo espaço de memória para ler os caminhos (line). Logo, pc e pda ficam a apontar para uma parte de line (com 1 byte de diferença, que é a diferença de tamanho entre "PC" e "PDA", e por isso é que o 1º dá inválido), que quando tentas abrir as directorias só tem a 2ª lida.

Torna pc e pda arrays de strings e copia os caminhos para lá em vez do resultado das chamadas a strtok que estão dentro dos ifs.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Problema resolvido graças à ajuda dada pelo TheDark  :). Obrigado

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora