Ir para conteúdo


Revista PROGRAMAR – Edição 46 (Setembro 2014): Download já disponível!

- - - - -

Problema com abertura de ficheiro txt


  • Por favor inicie sessão para responder
48 respostas a este tópico

#1 Psycop

Psycop

    Boolean User

  • Membro
  • PipPipPip
  • 171 mensagens

Publicado 07 de Abril de 2012 - 16:34

Boas

Estou a tentar abrir um ficheiro txt que contem a seguinte informação:

Citar

sessao 0
totais 0


Apenas é esta a informação que esta no ficheiro, o que acontece é que não estou a conseguir ler correctamente as strings sessão e totais.
Estou a programar usando o codeblocks e Linux.

Aqui fica o pedaço de código responsável pela leitura do ficheiro:

Código :
printf("\t\t\t*Estatisticas Totais do Jogo*\n\n\n");
                            FILE *entrada;
                            char tipo[100];
                            int valor;

                            //Abrir o Ficheiro

                            entrada = fopen("//home/nuno//Dropbox//Projectos em C//Jogo do Galo", "r");

                            if (entrada == NULL)
                            {
                                printf("Erro na Abretura do Ficheiro:\n");
                                getchar();
                            }

                            while(entrada != EOF)
                            {
                                fscanf(entrada,"%s %d",tipo, &valor);
                                printf("%s = %d\n", tipo, valor);
                                break;
                            }

                            fclose(entrada);
                            break;

Não consigo entender onde está o erro uma vez que o output é o seguinte:

Citar

(espaço que seria para a string ) = 0
.

Alguém sabe o que estou a fazer de mal?
Cumps

#2 pmg

pmg

    Unsigned User

  • Moderador
  • PipPipPipPipPipPip
  • 3688 mensagens

Publicado 07 de Abril de 2012 - 17:55

entrada é um ponteiro; EOF é um inteiro: nao os podes comparar directamente.
Sugestao: aumenta o nivel de warnings do teu compilador e faz compilacoes limpas, sem erros, claro! nem warnings.

Outra coisa: o scanf é perigoso para buffer overflows. Para o tornares seguro limita o numero de caracteres a ler na especificacao do formato
Código (C):
char tipo[100];
fscanf(entrada, "%99s %d", tipo, &valor);

Verifica sempre o resultado do scanf!
Com duas variaveis a atribuir o resultado devia ser 2; qulquer outra coisa é erro;

Código (C):
if (fscanf(entrada, "%99s%d", tipo, &valor) != 2) /* erro no scanf */;
Em vez de /* erro no scanf */ mostra uma mensagem e aborta ...

Tirando isto parece-me que o programa devia funcionar.

#3 Psycop

Psycop

    Boolean User

  • Membro
  • PipPipPip
  • 171 mensagens

Publicado 07 de Abril de 2012 - 18:08

Olá

Experimentei com as dicas que me indicaste e o retorno que tenho é um erro no fscanf, ou seja o fscan não está a ler os dois parâmetros.

Agora assim:

Código (C):
FILE *entrada;
                            char tipo[100];
                            int valor;

                            //Abrir o Ficheiro

                            entrada = fopen("//home/nuno//Dropbox//Projectos em C//Jogo do Galo", "r");

                            if (entrada == NULL)
                            {
                                printf("Erro na Abretura do Ficheiro:\n");
                                getchar();
                            }

                            while(fscanf(entrada,"%99s %d",tipo, &valor) != EOF)
                            {

                                if(fscanf(entrada,"%99s %d",tipo, &valor) != 2)
                                {
                                    printf("Erro de Leitura");
                                    break;
                                }
                                else
                                {
                                    printf("%s = %d\n", tipo, valor);
                                    break;
                                }
                            }

                            fclose(entrada);
                            break;
Ao compilar não tenho qualquer warning relativo a esta situação, mas continua a não aparecer a apresentação das palavras "sessão" e "totais".
Não consigo mesmo entender o porque...

Cumps

#4 bsccara

bsccara

    Try-Catch User

  • Membro
  • PipPipPipPip
  • 482 mensagens

Publicado 07 de Abril de 2012 - 18:35

Definição: Espaço branco = espaço, '\t' ou '\n'.

Quando testas a condição no início do ciclo 'while' tentas ler do ficheiro até 99 caracteres ou até encontrar espaço branco, depois zero ou mais espaços brancos e depois um número inteiro. Tudo isso pode ser carregado.
Mas uma linha de ficheiro de texto é terminada com um '\n' (em Linux). Quando tentas carregar a segunda linha o 'fscanf' vai encontrar isso e não os caracteres que espera e falha.
Para resolver come o '\n' no fim de cada linha com:

Código (C):
fscanf(entrada,"%99s %d ",tipo, &valor)
                       ^------ Espaço aqui


#5 pmg

pmg

    Unsigned User

  • Moderador
  • PipPipPipPipPipPip
  • 3688 mensagens

Publicado 07 de Abril de 2012 - 18:41

Agora tens 2 fscanf seguidos (um no while, outro no if). Se o teu programa funcionasse so iria apresentar a linha com "totais 0"; a linha com "sessao 0" teria sido 'comida' no while.

Verifica qual é o valor que o fscanf devolve ...
Código (C):
while (1) {
    int chk = fscanf(entrada, "%99s%d", tipo, &valor);
    printf("o fscanf devolveu %d.\n", chk);
    /* ... break algures ... */
}




Ver Mensagembsccara, em 07 de Abril de 2012 - 18:35, disse:

Código (C):
fscanf(entrada,"%99s %d ",tipo, &valor)
                       ^------ Espaço aqui

No loop a seguir, o "%99s" 'come' os espacos que o teu espaço pretende comer.
Ter, "%99s %d" e "%99s%d" é absolutamente igual: no primeiro caso os espaços sao ignorados por causa do espaço na string de conversao (e o "%d" começa logo com coisas diferentes de espaço); no segundo caso os espaços sao ignorados porque o "%d" ignora espaços.

#6 Psycop

Psycop

    Boolean User

  • Membro
  • PipPipPip
  • 171 mensagens

Publicado 07 de Abril de 2012 - 18:46

Correndo isto o printf devolve o valor "-1". Coisa que não esta dentro do ficheiro...

Cumps

Ver Mensagempmg, em 07 de Abril de 2012 - 18:41, disse:

Agora tens 2 fscanf seguidos (um no while, outro no if). Se o teu programa funcionasse so iria apresentar a linha com "totais 0"; a linha com "sessao 0" teria sido 'comida' no while.

Verifica qual é o valor que o fscanf devolve ...
Código (C):
while (1) {
    int chk = fscanf(entrada, "%99s%d", tipo, &valor);
    printf("o fscanf devolveu %d.\n", chk);
    /* ... break algures ... */
}


#7 bsccara

bsccara

    Try-Catch User

  • Membro
  • PipPipPipPip
  • 482 mensagens

Publicado 07 de Abril de 2012 - 19:13

Ver Mensagempmg, em 07 de Abril de 2012 - 18:44, disse:

No loop a seguir, o "%99s" 'come' os espacos que o teu espaço pretende comer.

Tens razão, mas de qualquer maneira acho preferível consumir toda a linha de uma vez.
Já agora, para referência futura e corrige-me se estiver enganado: todos os identificadores, com a excepção do '%c', ignoram espaço branco até que encontrem um caracter válido para eles. Só depois o espaço branco é considerado um terminador.

#8 pmg

pmg

    Unsigned User

  • Moderador
  • PipPipPipPipPipPip
  • 3688 mensagens

Publicado 07 de Abril de 2012 - 19:41

Ver MensagemPsycop, em 07 de Abril de 2012 - 18:46, disse:

Correndo isto o printf devolve o valor "-1". Coisa que não esta dentro do ficheiro...
O -1 é o sinal que o ficheiro nao tem mais dados (normalmente escreve-se EOF). É a unica maneira do scnaf devolver um numero negativo.




Ver Mensagembsccara, em 07 de Abril de 2012 - 19:13, disse:

Já agora, para referência futura e corrige-me se estiver enganado: todos os identificadores, com a excepção do '%c', ignoram espaço branco até que encontrem um caracter válido para eles. Só depois o espaço branco é considerado um terminador.
um caracter válido ... on inválido. Se for válido o scanf faz a conversao; se for inválido ele pára deixando esse caracter 'pendurado'.

Os conversores que nao ignoram espacos sao o "%c" e o "%[...]".

#9 bsccara

bsccara

    Try-Catch User

  • Membro
  • PipPipPipPip
  • 482 mensagens

Publicado 07 de Abril de 2012 - 19:47

Já agora, o ficheiro foi criado em Linux ou em Windows ? As linhas dos ficheiros criados em Linux terminam com '\n' mas os criados em Windows terminam com '\r' + '\n'. Sendo o teu programa compilado em Linux julgo que o fscanf não considera o '\r' como um espaço branco.

#10 pmg

pmg

    Unsigned User

  • Moderador
  • PipPipPipPipPipPip
  • 3688 mensagens

Publicado 07 de Abril de 2012 - 19:53

Um "espaço branco" é um dos caracteres para os quais a funcao isspace(), declarada em <ctype.h> devolve "TRUE". Normalmente, nos locales "C" ou "POSIX" esses caracteres sao '\f' (form-feed), '\n' (newline), '\r' (carriage return), '\t' (horizontal tab), e '\v' (vertical tab) e o espaco, claro.

#11 Psycop

Psycop

    Boolean User

  • Membro
  • PipPipPip
  • 171 mensagens

Publicado 07 de Abril de 2012 - 20:13

Boas

Foi tudo criado em linux.
O ficheiro foi com o gedit.

Cumps

#12 Flinger

Flinger

    CRLF

  • Membro
  • PipPipPipPipPip
  • 818 mensagens

Publicado 09 de Abril de 2012 - 17:27

Código :
sessao 0
está na primeira linha do ficheiro? se tiveres um \n pode estar a falhar devido a isso (embora eu ache que devia retornar 1). Para saber mais acerca do erro podes tentar algo como:


Código (C):
while (1) {
    int chk = fscanf(entrada, "%99s%d", tipo, &valor);
    perror("resultado fscanf:");
    printf("o fscanf devolveu %d.\n", chk);
    /* ... break algures ... */
}


#13 Psycop

Psycop

    Boolean User

  • Membro
  • PipPipPip
  • 171 mensagens

Publicado 10 de Abril de 2012 - 18:04

Boas

Estou mesmo perdido com esta situação. Alterei o ficheiro para a seguinte forma só para testar:

Citar

0

Apenas o Zero na primeira linha do ficheiro, sem espaços antes,

E usando este código:

Código (C):
case 2:
                        {
                            printf("\t\t\t*Estatisticas Totais do Jogo*\n\n\n");

                            FILE *entrada;
                            int sessao, totais;

                            //Abrir o Ficheiro

                            entrada = fopen("//home/nuno//Dropbox//Projectos em C//Jogo do Galo", "r");

                            if (entrada == NULL)
                            {
                                printf("Erro na Abretura do Ficheiro:\n");
                                getchar();
                            }

                        while(!feof(entrada))
                        {
                            fscanf(entrada,"%d",&sessao);
                            printf("%d", sessao);
                            break;
                        }

                            fclose(entrada);
                            break;
                        }

Mesmo assim não consigo ler o conteúdo do ficheiro e apresenta-lo, tendo o 1 no ficheiro o que me é apresentado no programa é o valor zero.

#14 pmg

pmg

    Unsigned User

  • Moderador
  • PipPipPipPipPipPip
  • 3688 mensagens

Publicado 10 de Abril de 2012 - 18:39

Ver MensagemPsycop, em 10 de Abril de 2012 - 18:04, disse:

Código (C):
                            FILE *entrada;
                            //Abrir o Ficheiro
                            entrada = fopen("//home/nuno//Dropbox//Projectos em C//Jogo do Galo", "r");
                            if (entrada == NULL)
                            {
                                printf("Erro na Abretura do Ficheiro:\n");
                                getchar();
                            }
Tens a certeza que o nome esta bem? em vejo no meio do nome uma quebra de linha ...
De qualquer maneira, se houver problemas na abertura do ficheiro o programa continua normalmente como se estivesse tudo bem.

Experimenta sair do programa em vez de esperar por uma tecla.
Ja agora podes tambem imprimir uma mensagem melhorzita, que explica qual o erro em vez de apenas dizer que houve erro
Código (C):
if (entrada == NULL) {
    perror("entrada");
    exit(EXIT_FAILURE); /* nao te esquecas de #include <stdlib.h> */
}


Ver MensagemPsycop, em 10 de Abril de 2012 - 18:04, disse:

Código (C):
                        while(!feof(entrada))
                        {
                            fscanf(entrada,"%d",&sessao);
                            printf("%d", sessao);
                            break;
                        }
Verifica o valor devolvido pelo fscanf em vez de usares o feof(). O feof() serve para averiguar se o ultimo erro que ocorreu se deveu ao ficheiro ter chegado ao fim. Em particular, se nao tiver ocorrido um erro, o feof() devolve sempre falso. O fscanf verifica que houve erro antes.
Código (C):
while (fscanf(entrada, "%d", &sessao) == 1) { /* ... */ }
/* quando a execucao chega aqui, ocorreu um erro no fscanf(),
** se quiseres averiguar porque, podes usar o feof() */
fclose(entrada);


#15 Psycop

Psycop

    Boolean User

  • Membro
  • PipPipPip
  • 171 mensagens

Publicado 10 de Abril de 2012 - 18:47

Verifiquei a situação do nome e se retirar os espaços surge-me então a mensagem de erro na abertura do ficheiro e "Segmentation fault".

Se voltar a colocar os espaços, não dá erro mas continua a retornar apenas o valor zero...

Utilizando Código (C):
while (fscanf(entrada, "%d", &sessao) == 1)
nem sequer é impresso qualquer valor no ecrã!

Cumps

#16 pmg

pmg

    Unsigned User

  • Moderador
  • PipPipPipPipPipPip
  • 3688 mensagens

Publicado 10 de Abril de 2012 - 18:55

Olha ... mete o nome do ficheiro numa variavel, e imprime essa variavel antes de abrir o ficheiro.

Código (C):
char nomeficheiro[] = "//home/bla bla bla/Jogo do Galo";
printf("A abrir o ficheiro '%s' ...\n", nomeficheiro);
entrada = fopen(nomeficheiro, "r");


#17 Psycop

Psycop

    Boolean User

  • Membro
  • PipPipPip
  • 171 mensagens

Publicado 10 de Abril de 2012 - 19:18

Implementando desta forma apena é impresso o nome do ficheiro, nada mais....

Código (C):
case 2:
                        {
                            printf("\t\t\t*Estatisticas Totais do Jogo*\n\n\n");

                            FILE *entrada;
                            int sessao, totais;

                            char nomeficheiro[] = "//home/nuno//Dropbox//Projectos em C//Jogo do Galo";
                            printf("A abrir o ficheiro '%s' ... \n", nomeficheiro);

                            //Abrir o Ficheiro

                            entrada = fopen(nomeficheiro, "r");

                            if (entrada == NULL)
                            {
                                printf("Erro na Abretura do Ficheiro:\n");
                                getchar();
                            }

                        while(fscanf(entrada, "%d", &sessao) == 1)
                        {
                            printf("%d", sessao);
                            break;
                        }

                            fclose(entrada);
                            break;
                        }

Cumps

#18 pmg

pmg

    Unsigned User

  • Moderador
  • PipPipPipPipPipPip
  • 3688 mensagens

Publicado 10 de Abril de 2012 - 19:59

Eu estava a confundir as barras no nome do ficheiro com as barras ao contrario ... mas acho estranho estarem duas ou mais seguidas. Normalmente ve-se /home/nuno/Dropbox/Projects em C/etc :-)

Verifica o conteudo do ficheiro com um examinador hexadecimal, por exemplo
Código (Bash):
hd '/home/nuno/.../Jogo do Galo'

Se nao tiveres um utilitario que funcione podes usar o teu proprio programa, mas le caracter a caracter (com fgetc()) em vez de usares scanf()
Código (C):
int ch;
fopen();
while ((ch = fgetc(entrada)) != EOF) {
    printf("valor lido: %d (caracter '%c')\n", ch, ch);
}
fclose();


#19 Psycop

Psycop

    Boolean User

  • Membro
  • PipPipPip
  • 171 mensagens

Publicado 10 de Abril de 2012 - 20:05

As directorias só tem apenas uma \, mas o que aprendi e pelo que tenho lido colocam-se duas \\ devido a apenas representar um caracter.

Quanto ao directorio retirei-o directamente das propriedades do ficheiro de texto.

Não sei mesmo o que possa estar a acontecer...

#20 pmg

pmg

    Unsigned User

  • Moderador
  • PipPipPipPipPipPip
  • 3688 mensagens

Publicado 10 de Abril de 2012 - 20:28

A barra normal / usa-se em Unix.
A barra invertida \ usa-se em Windows.

Dentro do codigo fonte em C, dentro duma string literal, usa-se a barra invertida tambem para especificar "caracteres especiais", por exemplo o tab "\t". Para especificar um t a seguir a uma barra invertida tem-se de duplicar a barra invertida "\\t". A mesma logica aplica-se aos nomes de ficheiros.

Mas, mesmo em Windows, o teu programa deve funcionar com barras normais nos nomes de ficheiros.
As duas expressoes abaixo deviam funcionar exactamente da mesma forma
Código (C):
fopen("C:/DOS/AUTOEXEC.BAT", "r")
Código (C):
fopen("C:\\DOS\\AUTOEXEC.BAT", "r") /* hehe, o GeSHi atrapalha-se todo */

Se estas em Windows, provavelmente tens um BOM no ficheiro.