Ir ao conteúdo

Posts recomendados

Postado

Faça um programa que lê de um arquivo, o sexo, o primeiro nome, e a idade de uma lista de pessoas. Imprima em outro arquivo, o número de homens, o número de mulheres e a idade da pessoa mais velha. A primeira linha do arquivo de entrada deve conter o número de pessoas.

Ex: Entrada.txt

3

M Joao 40

F Maria 32

M Carlos 27

 

Saída.txt

O numero de homens: 2

O numero de mulheres: 1

A idade da pessoa mais velha e: 40

 

Alguem poderia me dar um rumo, um exemplo para resolver essa questão

  • Obrigado 1
Postado

@naruto100    para abrir um arquivo você pode usar a função fopen assim :

FILE *arq = fopen("nome_do_arquivo.txt","r");

e depois use o comando fscanf para ler o que está gravado e vai fazendo a contagem . então faça um código sobre esse exercício e poste aqui para podermos ajudar caso precise de alguma correção .

  • Curtir 1
Postado

@devair1010 oi, cara , eu fiz desse jeito, so uma parte do codigo, eu gravei os dados no arquivo e fiz uma função para ler o mesmo, no entato eu estou lendo pelo fgets, que está transformando ate os numeros inteiros em strings. você me segere outra forma? eu nao consegui pelo fscanf

 

void flush(){

int c;

while((c = fgetc(stdin)) != EOF && c != '\n'){}

}

 

void gravararquivo(){

FILE *p;

char nome[30];

char sexo;

int idade;

int numero;

int i;

int n;

p = fopen("entrada.txt", "w");

if(p == NULL){

printf("Erro no arquivo!");

}

else{

 

printf("Digite a quantidade de pessoas que quer armazenar no arquivo: ");

scanf("%d", &numero);

 

fprintf(p, "%d\n", numero);

for(i=0; i<numero;i++){

printf("Digite um nome: ");

flush();

gets(nome);

printf("Digite o sexo: ");

scanf("%c", &sexo);

printf("Digite a idade: ");

scanf("%d", &idade);

 

fprintf(p, "%c %s %d\n ", toupper(sexo), nome, idade);

}

 

}

fclose(p);


 

}

void lerarquivo(){

FILE *p;

char tamanho[100];

p = fopen("entrada.txt", "r");

if(p == NULL){

printf("Arquivo nao encontrado!");

}

else{

while(!feof(p)){

fgets(tamanho, 100, p);

printf("%s", tamanho);

}

 

}

}

  • Obrigado 1
Postado

@naruto100 Não tem nenhuma função main() no seu programa, e também estão faltando as bibliotecas que você usou. Como espera que testemos assim?

 

1 hora atrás, naruto100 disse:

eu estou lendo pelo fgets, que está transformando ate os numeros inteiros em strings

Então converta de volta. Percorra toda a string com um loop, e se o caractere for um número de 0 a 9, subtraia 48 do mesmo (na tabela ASCII, o valor decimal do caractere 0 é 48, então para os números de 0 a 9, temos um padrão), depois é só concatenar em uma variável do tipo int.

Ou então use uma função pronta: atoi().

 

2 horas atrás, naruto100 disse:

eu nao consegui pelo fscanf

Não tem fscanf() no seu programa. Fica difícil saber o motivo de não ter conseguido...

  • Curtir 1
Postado
Em 27/11/2020 às 17:08, naruto100 disse:

eu nao consegui pelo fscanf

 

E o que houve?

 

  • por que razão escreveu  gravararquivo()??? Está em busca de problemas? O enunciado nada diz sobre gravar o arquivo. Faça o óbvio e simples: digite o arquivo no editor do IDE, no bloco de notas... Tudo menos isso.
  • Para ler 10 pessoas para testar vai mesmo ficar parado digitando 10, depois ENTER e depois TRINTA valores para sexo nome e idade? Quantas vezes acha que vai testar seu programa?
  • não use gets(). Nunca
  • nunca deixe de ler o retorno de scanf(). É uma bobagem. Não pode seguir com o programa sem saber se leu algo.
     
    Em 27/11/2020 às 17:08, naruto100 disse:

    uma função para ler o mesmo, no entato eu estou lendo pelo fgets

     

Num caso em que afinal a entrada é formatada, exceto pela primeira linha, e você tem uma função cujo nome vem de "scan formatted input" você resolve ler usando fgets() e converter os campos? Não devia, Faça o simples. Essa função foi escrita exatamente para isso: ler entrada formatada onde todas as linhas são iguais...

  • use o botão code lá como pedido no forum para separar seu código e ajudar alguém a ajudar você....
     
  • Em 27/11/2020 às 19:10, Lucca Rodrigues disse:

    o valor decimal do caractere 0 é 48, então para os números de 0 a 9, temos um padrão), depois é só concatenar em uma variável do tipo int

  • Não, não é o caso de concatenar: o sistema numérico é posicional: tem que multiplicar o dígito pela potência de 10 correspondente à posição dele... Sabe, centenas, dezenas. milhares. unidades... Mas aqui é perda de tempo.

fscanf() e seu arquivo

 

Um simples arquivo
 

10 pessoas no teste
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Maria 32
M Carlos 27

 

Copiar e colar os dados do enunciado...

 

O que tem em cada linha? 
 

Na primeira importa apenas o número...

As demais vão ter o sexo, o nome, a idade e um '\n', o enter que você digitou no bloco de notas ao criar o arquivo

  • Entenda que o melhor para você e para quem usa esse programa é passar o nome do arquivo como argumento, como é feito desde a criação da linguagem nos '60. Se o seu programa se chama ex08 não é o esperado você poder escrever
     
        ex08
        ex08 entrada.txt
        ex08 pessoas.doc
        ex08 teste

     

E o programa entender que o arquivo será "entrada.txt",  "pessoas.doc" ou "teste"? E se você digitar apenas ex08 ele assumir o padrão do enunciado, "entrada.txt"? 

Não é o simples, em especial se considerar que

  • todos os programas fazem isso
  • é muito chato ficar fixo dentro do programa
  • demora mais para testar vários casos logo para terminar esse programa
  • custa 4 linhas para fazer isso?

    Exemplo do simples:
     
        const char* padrão = "entrada.txt";
        char        arquivo[80];
    
        if( argc>1)
            strcpy(arquivo, argv[1]);
        else
            strcpy(arquivo,padrão);
    
        printf("Vai abrir \"%s\"\n", arquivo);
        FILE*  E = fopen(arquivo, "r");
        if ( E == 0 ) return -1; // nao abriu
  • Pois é: o padrão é "entrada.txt". Se o cara digitou algo ao invocar o programa então é o nome do arquivo. Um if() deixa o valor correto em arquivo.
  • Um printf() dá um toque de sanidade e mostra se leu direito o nome do arquivo.
  • um fopen() abre o arquivo
  • se deu erro retorna -1. E você testa o código de retorno na linha de comando. Como? que tal echo $?
  • e se der erro foi o cara que errou o nome do arquivo afinal. Claro, pode dar uma mensagem mais educada.

Não é muito mais simples?  Veja dois exemplos, no Ubuntu Linux, não que faça diferença

 

toninho@DSK-2009:~/projects/crj$ ./t azul.doc
Vai abrir "azul.doc"
Esperadas + 4 linhas no arquivo 'azul.doc'
M Joao 40
F Maria 32
M Carlos 270
M Joao 400
Lidas 4 linhas de 'azul.doc'
toninho@DSK-2009:~/projects/crj$ ./t
Vai abrir "entrada.txt"
Esperadas + 10 linhas no arquivo 'entrada.txt'
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
Lidas 10 linhas de 'entrada.txt'
toninho@DSK-2009:~/projects/crj$ 

 

Não importa se o arquivo tem menos linhas que o esperado. Não nos testes.

 

E fscanf() afinal?

 

Como está claro, os dados tem que estar FORMATADOS. Certinhos. Formatted Input. Então vai ter a letra do sexo, o nome do cara, a idade que é um int e pode ter até sinal já que é um int. E um ENTER depois da idade,

 

Que usar para ler isso? O simples: %c para uma letra, %s para o nome, %d para a idade e %c para o ENTER, Ou não?

E qual seria a máscara complexa para ler esses 4 elementos? 🤔 Que tal "%c%s%d%d" ? Pois é: essa mesma.

 

Pode ser útil deixar separado no programa, lá no início, porque se você está testando ou quer mudar os campos não vai gostar de ficar caçando essa m3rd@ no meio do fonte:
 

    const char* mascara = "%c%s%d%c"; // para scanf()

 

E fscanf() vai calmamente ler seu arquivo todo usando essa chamada

        res = fscanf( E, mascara, &sexo, nome, &idade, delim);

 

E como vai saber se leu? Fazendo o que NÃO fez no seu programa e parece que ninguém que posta aqui faz: testando o retorno. TEM QUE LER OS QUATRO e então deve retornar..... 4. Ler do arquivo E usando a mascara mascara e colocando os valores nas váriaveis declaradas e alocadas para isso. Não leu 4? Já era.

 

De seu exemplo, isso é um erro grande:

 

printf("Digite o sexo: ");
scanf("%c", &sexo);
printf("Digite a idade: ");
scanf("%d", &idade);

 

Não pode deixar o programa seguir reto sem testar. E se não leu nada para o sexo? Que adianta ler a idade? Boa parte dos tópicos aqui no forum se refere a programas que "passam reto sem ler valores". Anos e anos de versões do mesmo erro por não ler o manual ou ter um instrutor ruim. Ou os dois.

 

Um loop assim leria todos os seus arquivos de teste
 

   
    while ( ! feof(E) )
    {
        res = fscanf( E, mascara, &sexo, nome, &idade, delim);
        if (res != 4) break; // acabou?
        printf( "%c %s %d\n", sexo, nome, idade );
        lidas += 1;
        if ( lidas >= N ) break;
    };  // while()
    fclose(E);
    printf("Lidas %d linhas de '%s'\n", 
        lidas, arquivo);
 

 

Sim, 5 linhas.

 

Recomendo testar. Eis o exemplo

 

Spoiler

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

int main(int argc, char** argv)
{
    // os valores
    char        sexo = 0;
    char        nome[50];
    unsigned    idade = 0;
    char delim[4]; // para 'comer' o fim de linha

    const char* padrão = "entrada.txt";
    char        arquivo[80];

    if( argc>1)
        strcpy(arquivo, argv[1]);
    else
        strcpy(arquivo,padrão);

    printf("Vai abrir \"%s\"\n", arquivo);
    FILE*  E = fopen(arquivo, "r");
    if ( E == 0 ) return -1; // nao abriu

    char        linha[80];
    const char* mascara = "%c%s%d%c"; // para scanf()
    unsigned    N = 0; // total esperado de linhas
    unsigned    lidas = 0;
    int         res = 0;

    res = fscanf(E, "%d", &N);
    if ( res != 1) return -2; // bobagem: nao veio o número de linhas

    printf("Esperadas + %d linhas no arquivo '%s'\n", N, arquivo);
    fgets(linha,80,E); // para ler o resto da primeira linha
    // o arquivo foi criado como parte do exercicio
    // entao nao importa muito se tem ou nao as N linhas
    return 0;
}

 

 

  • Obrigado 2
Postado

@naruto100@naruto100    seu código funciona bem , mas o fgets pega uma linha inteira e tudo  como  string ,  então precisaria pegar as partes dessa string e converter para o que precisa um char uma string e um inteiro ,  então você pode usar o scanf , e seu código completo com algumas modificações poderia ser assim  :

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
void lerarquivo();
void flush();
void gravararquivo();
int main()
{
    gravararquivo();
    lerarquivo();
    return 128;
}
void flush()
{
    int c;
    while ((c = fgetc(stdin)) != EOF && c != '\n') {}
}

void gravararquivo()
{
    FILE* p;
    char nome[30],sexo;
   int idade, numero,
        i,n;
    p = fopen("entrada.txt", "w");
    if (p == NULL)
    {
        printf("Erro no arquivo!");
    }
    else
    {
        printf("Digite a quantidade de pessoas que quer armazenar no arquivo: ");
        scanf("%d", &numero);

        fprintf(p, "%d\n", numero);
        for (i = 0; i < numero; i++)
        {
            printf("Digite um nome: ");
            flush();
            fgets(nome,sizeof(nome),stdin);
            nome[strlen(nome) - 1] = '\0';
            printf("Digite o sexo: ");
            scanf("%c", &sexo);
            printf("Digite a idade: ");
            scanf("%d", &idade);

            fprintf(p,"%c %s %d\n", toupper(sexo), nome, idade);
        }
    }
    fclose(p);
}
void lerarquivo()
{
    FILE* p, *out_arq;
    char nome[30],sexo[2];
    int idade,numero,Tot_h=0,
        Tot_m=0,Tot_o=0,p_m_v;
    p = fopen("entrada.txt", "r");
    if (p == NULL)
    {
        printf("Arquivo nao encontrado!");
        return;
    }
    fscanf(p,"%d", &numero);
    printf("\n%d\n",numero);
    while ( fscanf(p, "%s" , sexo  ) != EOF &&
            fscanf(p, "%s" , nome  ) != EOF &&
            fscanf(p, "%d" , &idade) != EOF  )
    {
        printf("%s %s %d\n", sexo,nome,idade);
        if( ! Tot_h)p_m_v = idade;
        if     (sexo[0] == 'M')Tot_h++;
        else if(sexo[0] == 'F')Tot_m++;
        else Tot_o++;
        if(p_m_v < idade)p_m_v = idade;
    }
    fclose(p);
    out_arq = fopen("outro_Arquivo.txt","w");
    fprintf(out_arq,"Numero de Homens => %d\nNumero de Mulhres => %d\nOutros => %d",Tot_h,Tot_m,Tot_o);
    fclose(out_arq);
    printf("Numero de Homens %d\nNumero de Mulhres %d\nOutros %d",Tot_h,Tot_m,Tot_o);
}

 

Postado
  while ( fscanf(p, "%s" , sexo  ) != EOF &&
            fscanf(p, "%s" , nome  ) != EOF &&
            fscanf(p, "%d" , &idade) != EOF  )
    {

 

@devair1010 Logo acima eu mostrei  que pode usar uma única chamada a fscanf() para ler cada registro da entrada. Na verdade essas é a aplicação mais comum de fscanf(): consumir tabelas como arquivos CSV por exemplo.

 

    res = fscanf( E, mascara, &sexo, nome, &idade, delim);
      

 

Uma linha assim leria os 3 campos da tabela e o enter al final da linha.

 

Não há propósito em fazer 3 chamadas para a mesma função para fazer a mesma coisa em campos consecutivos de um arquivo e que não podem ser diferentes.

 

EOF não é tudo

 

Isso 

 

	while ( 
		(fscanf(p, "%s" , sexo  ) + 
		fscanf(p, "%s" , nome  ) +
		fscanf(p, "%d" , &idade)) != 3 
    )
	{}

 

seria ainda um pouco ingênuo mas estaria ao menos correto.

 

No entanto testar o retorno comparando com EOF está errado: EOF é -1, indica só fim de arquivo. E nem sempre

 

Entenda:

 

fscanf() vai retornar no seu caso 0, 1 ou EOF. Para cada uma das 3 chamadas. Isso quer dizer que se não conseguir ler sexo porque na linha o cara esqueceu de digitar vai retornar ZERO e seu programa já era.

 

Nessas situações faça o simples: leia um registro por linha.

 

Entenda também que mesmo quando fscanf() retorna EOF pode não ser o caso. EOF é o único indicador de erro e então para saber o que houve você deve testar errno e ver que erro foi. Pode ser algo totalmente diferente de EOF. Quando da erro scanf() retorna EOF. 

 

Sobre esse trecho
 

int main()
{
    gravararquivo();
    lerarquivo();
    return 128;
}

 

o mais simples é poder escrever algo como
 

int main()
{
    const char* arquivo = "exemplo.txt";
    gravararquivo(arquivo);
    lerarquivo(arquivo);
    return 128;
}

 

Imagine que se quiser mudar o nome do arquivo precisa mudar EM DOIS lugares. Não faz sentido. Se mudar em uma e esquecer de mudar na outra vai reclamar de quem?

 

E entenda que esse nome claramente deveria vir na linha de comando. Imagine ter que compilar o programa apenas para mudar o nome de um arquivo. E em dois lugares. ;) 

 

E você tiver levado o programa para outra máquina?

 

E eu mostrei como logo acima como fazer.

 

E são só 5 linhas.

 

E todos os programas em qualquer linguagem --- C e C++ e java e .BAT e scripts por exemplo ---funcionam assim desde sempre.

 

Imagine se o comando DIR perguntasse o diretório a cada execução por exemplo... A lógica é simples: sem parâmetro lista o diretório corrente. Com parâmetro é o diretório...

 

   printf("Digite um nome: ");
   flush();
   fgets(nome,sizeof(nome),stdin);
   nome[strlen(nome) - 1] = '\0';
   printf("Digite o sexo: ");
   scanf("%c", &sexo);
   printf("Digite a idade: ");
   scanf("%d", &idade);

 

@devair1010 provavelmente poderia passar sem esses flush() mas tem que testar o retorno de scanf(): não há sentido em tentar ler a idade se não leu o sexo... Não pode seguir cegamente com o programa se não leu um campo do registro. Vai gravar o que no lugar?

 

E repito: o enunciado nem fala em criar o arquivo no programa. Para que o esforço?

  • Obrigado 1
Postado

@arfneto      sim , seu código é muito melhor e mais fácil , mesmo ,  tive que montar as partes e deu erro de sintaxe , pois havia um reclamação do compilador , que dizia alguma coisa sobre stray , e demorei para descobrir que esse erro estranho era , e simplesmente um TiL na palavra "padrão" , creio que seja pelo corretor ortografico ter colocado ele ali na hora de postar , e achei curioso o fgets para pegar o restante da primeira linha , pois sem ele o fscanf não funciona , o que acontece nessa parte do código ?  , e com um arquivo criado no compilador funciona bem , mas um digitado no bloco de notas , não mostra todos os dados do arquivo 

383794799_progArfneto.thumb.jpg.2c6c2728ecb3a64383f225455a4da0ab.jpg

aqui é como ficou o código que montei usando as partes dali de cima , posso não ter usado as partes certas , veja se ficou faltando alguma coisa .
 

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

int main(int argc, char** argv)
{
    // os valores
    char        sexo = 0;
    char        nome[50];
    unsigned    idade = 0;
    char delim[4]; // para 'comer' o fim de linha

    const char* padrão = "entrada6.txt";
    char        arquivo[80];

    if( argc>1)
        strcpy(arquivo, argv[1]);
    else
        strcpy(arquivo,padrão);

    printf("Vai abrir -: \"%s\"\n", arquivo);
    FILE*  E = fopen(arquivo, "r");
    if ( E == 0 ) return -1; // nao abriu

    char        linha[80];
    const char* mascara = "%c%s%d%c"; // para scanf()
    unsigned    N = 0; // total esperado de linhas
    unsigned    lidas = 0;
    int         res = 0;

    res = fscanf(E, "%d", &N);
    if ( res != 1) return -2; // bobagem: nao veio o número de linhas

    printf("Esperadas + %d linhas no arquivo '%s'\n", N, arquivo);
    fgets(linha,2,E); // para ler o resto da primeira linha
    // o arquivo foi criado como parte do exercicio
    // entao nao importa muito se tem ou nao as N linhas
    while ( ! feof(E) )
    {
        res = fscanf( E, mascara, &sexo, nome, &idade, delim);
        if (res != 4) break; // acabou?
        printf( "%c %s %d\n", sexo, nome, idade );
        lidas += 1;
        if ( lidas >= N ) break;
    };  // while()
    fclose(E);
    printf("Lidas %d linhas de '%s'\n",
        lidas, arquivo);

    return 0;
}

 

Postado
2 minutos atrás, devair1010 disse:

seu código é muito melhor e mais fácil , mesmo ,  tive que montar as partes e deu erro de sintaxe , pois havia um reclamação do compilador , que dizia alguma coisa sobre stray

 

Hey não é o "meu código". É a maneira comum de escrever essas coisas. 

 

Mas entenda que como escreveu está ERRADO. Não pode comparar com EOF o retorno de scanf() e achar que é suficiente. Tem que testar com o número de especificadores que tem na máscara. Só isso. Se usou "%s %d %d %d %d" por exemplo, pode ler de 0 a 5 em cada chamada então tem que testar com 5. Só isso. Raramente vem EOF. Muitas vezes vem menos que 5. Você tem que testar o caso não-5. Entendeu?
 

8 minutos atrás, devair1010 disse:

pois havia um reclamação do compilador , que dizia alguma coisa sobre stray , e demorei para descobrir que esse erro estranho era , e simplesmente um TiL na palavra "padrão"


Esse lance de stray geralmente acontece quando por exemplo copia código daqui de um trecho de um post que não colocou o código dentro do tal bloco <code> ou de algum texto HTML e acaba copiando algo que não é válido para o fonte C. Alguns IDE conseguem mostrar o caracter no editor. No seu caso foi o '~'

 

9 minutos atrás, devair1010 disse:

e achei curioso o fgets para pegar o restante da primeira linha , pois sem ele o fscanf não funciona , o que acontece nessa parte do código ?

 

O fgets() permite que você escreva mais coisas na linha, como comentários para você mesmo no arquivo que digitou. É uma técnica comum. Muitas vezes você quer ler uma série de parâmetros do arquivo e só vai ler o número. Algo assim:
 

200 altura
1500 profundidade
400 largura
800 600 tamanho da tela
120 testes nesse ciclo. seguem valores de teste
1233 45565 6.2 343 343.4 44
1233 45565 6.2 343 343.4 44
1233 45565 6.2 343 343.4 44
1233 45565 6.2 343 343.4 44
1233 45565 6.2 343 343.4 44
1233 45565 6.2 343 343.4 44

 

Você vai gostar de daqui a 60 dias estar escrito no arquivo o que vem primeiro nos parâmetros., e vai gostar de saber que fgets(0 vai ler só o número. Seu arquivo de teste tem as informações para VOCÊ saber a ordem, e para o programa tanto faz.

 

É o mesmo caso de ignorar linhas que começam por ';' ou '#': isso pode ser sua salvação ao ler o arquivo mais tarde. Em especial se não tiver mais a fonte do programa para procurar a ordem das coisas...

 

Em casos de dados tabulares, como era absolutamente comum no passado, tem umas linhas no começo que dizem o que vem depois. Você não quer ler os dados com fscanf() na verdade, porque eles não são tabulares: você só quer usar fscanf() para ler os pontos de dados, que podem ser dezenas de milhares. O começo você lê com fgets() que garante consumir a linha toda e deixar inclusive o '\n' e até colocar um se não tiver na última linha do arquivo.
 

Então você pode ler a linha toda e usar só o início para o dado que está lendo. E em caso de menus por exemplo pode se defender de o usuário digitar mais do que pretendia ao teclar a opção: usa fgets() para ler em um char[nnn] e retorna apenas a primeira letra no menu...

 

Use esse arquivo para testar antes de seguir adiante:

 

10 pessoas no teste
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Maria 32
M Carlos 27

 

14 minutos atrás, devair1010 disse:

e com um arquivo criado no compilador funciona bem , mas um digitado no bloco de notas , não mostra todos os dados do arquivo

 

poste o código todo.

 

A razão mais comum de erro é falta de cuidado ao digitar os dados: é um programa que está lendo o arquivo. Não pode por exemplo ter espaços entre a idade e o '\n' ... Verifique

 

Rode o programa que mostrei com o arquivo que mostrei e será uma referência, já que eu postei a saída também.

 

 

Postado

@arfneto    esse é o código que consegui montar usando as partes que você postou acima , 

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

int main(int argc, char** argv)
{
    // os valores
    char        sexo = 0;
    char        nome[50];
    unsigned    idade = 0;
    char delim[4]; // para 'comer' o fim de linha

    const char* padrão = "ent5.txt";
    char        arquivo[80];

    if( argc>1)
        strcpy(arquivo, argv[1]);
    else
        strcpy(arquivo,padrão);

    printf("Vai abrir -: \"%s\"\n", arquivo);
    FILE*  E = fopen(arquivo, "r");
    if ( E == 0 ) return -1; // nao abriu

    char        linha[80];
    const char* mascara = "%c%s%d%c"; // para scanf()
    unsigned    N = 0; // total esperado de linhas
    unsigned    lidas = 0;
    int         res = 0;

    res = fscanf(E, "%d", &N);
    if ( res != 1) return -2; // bobagem: nao veio o número de linhas

    printf("Esperadas + %d linhas no arquivo '%s'\n", N, arquivo);
    fgets(linha,80,E); // para ler o resto da primeira linha
    // o arquivo foi criado como parte do exercicio
    // entao nao importa muito se tem ou nao as N linhas
    while ( ! feof(E) )
    {
        res = fscanf( E, mascara, &sexo, nome, &idade, delim);
        if (res != 4) break; // acabou?
        printf( "%c %s %d\n", sexo, nome, idade );
        lidas += 1;
        if ( lidas >= N ) break;
    };  // while()
    fclose(E);
    printf("Lidas %d linhas de '%s'\n",
        lidas, arquivo);

    return 0;
}

 

e com esses dados de nove Linhas , só mostrou oito 

 

224193906_progArfneto.thumb.jpg.08ace9f0dcc10a939db34909e75e20e0.jpg

você pode postar o código completo ?      para que eu não precise juntar as partes e ver se funciona .

Postado
2 minutos atrás, devair1010 disse:

você pode postar o código completo ?      para que eu não precise juntar as partes e ver se funciona

 

❓

Mas eu em geral posto primeiro  a saída, depois algum trecho importante e no final o código completo do exemplo...

Nesse caso como eu já havia mostrado em parte s o programa todo deixei o conteúdo 
 

image.png.852197f0d51c6f53a53c0944800ad181.pngDENTRO desse bloco. Só para não ficar repetido. Mas o programa que está lá mostra o resultado que eu postei, usando o arquivo que eu postei.

 

Em seu caso, confirmou que na última linha tem um ENTER?

 

 

 

 

 

Entendeu o que eu expliquei aqui:

 

1 hora atrás, arfneto disse:

A razão mais comum de erro é falta de cuidado ao digitar os dados: é um programa que está lendo o arquivo. Não pode por exemplo ter espaços entre a idade e o '\n' ... Verifique

 

 

Coloque uma linha a mais no arquivo e teste se o seu problema é sempre na última linha...

Coloque a última linha no lugar da primeira e veja se muda algo...

Teste...

 

Entenda que eu não me preocupei em contar as linhas, desde que tenha no máximo o que foi dito. Para facilitar os testes. Não é importante se o cara anunciou 20 linhas no arquivo e só tem 3. Assim facilita para ir testando caso a caso.

Clique no botão lá mostrar conteúdo oculto.

10 minutos atrás, devair1010 disse:

const char* padrão = "ent5.txt"; char arquivo[80];

 

Você entendeu que pode entrar com o nome do arquivo na linha de comando?

 

  • Curtir 1
Postado

@devair1010 🤔

 

Veja o protótipo de main():
 

	int main( int argc, char** argv );

 

Isso vale para outras linguagens, para Linux, Unix, Windows ou MacOS. Quando você roda um programa você faz isso digitando o nome dele. No Windows pode digitar 

 

    gcc ex08.c 

 

e eventualmente chamar o compilador gcc e compilar o programa ex08.c que está no diretório corrente.

 

no windows pode rodar um comando DIR e ver algo como
 

C:\Users\toninho\AppData\Local\Temp\pasta1>dir
 O volume na unidade C não tem nome.
 O Número de Série do Volume é 7E52-1BF2

 Pasta de C:\Users\toninho\AppData\Local\Temp\pasta1

29/11/2020  23:46    <DIR>          .
29/11/2020  23:46    <DIR>          ..
29/11/2020  23:46                90 azul.txt
29/11/2020  23:18               165 entrada.txt
29/11/2020  23:18             1.470 ex08.c
29/11/2020  23:47                82 outro.txt
29/11/2020  23:45    <DIR>          pasta2
29/11/2020  23:45    <DIR>          pasta3
               4 arquivo(s)          1.807 bytes
               4 pasta(s)   101.207.711.744 bytes disponíveis

C:\Users\toninho\AppData\Local\Temp\pasta1>


 

Veja que na pasta pasta1 tem duas pastas: pasta2 e pasta3.


Se você digitar dir pasta2 ou dir pasta3 vai listar o conteúdo dessas pastas.


Desculpe se parece muito óbvio mas eu tenho um plano.


Veja:

 

ex.png.b63ad423f0d90f2724725ce662f2aa51.pngEntão você vê que se digitar apenas dir vai listar o conteúdo  do diretório corrente, mas se digitar o nome de uma pasta o sistema vai tentar listar o conteúdo de uma pasta com esse nome no diretório corrente.

 

Isso são argumentos para o DIR. Sem argumentos o padrão é usar claro o diretório corrente. 

 

Se tiver um argumento é o nome da pasta. Mas pode ter mais de um argumento. O tradicional é pode ter mais opções. 

 

Por exemplo o compilador gcc que você usa. Imagine que na pasta tenha um programa ex08.c que seja esse de que estamos discutindo.

 

E na pasta a gente tenha uns arquivos de teste para o programa, com dados diferentes de teste usando condições tipo uma linha só, disse que tem 10 nomes e tem 20, ou 8, sei lá.

 

Então na pasta tem esses arquivos e mais o programa fonte.

 

 

 

 

 

 

Usando DIR poderia ver


 

 

 

C:\Users\toninho\AppData\Local\Temp\pasta1>dir
 O volume na unidade C não tem nome.
 O Número de Série do Volume é 7E52-1BF2

 Pasta de C:\Users\toninho\AppData\Local\Temp\pasta1

30/11/2020  00:01    <DIR>          .
30/11/2020  00:01    <DIR>          ..
29/11/2020  23:46                90 azul.txt
29/11/2020  23:18               165 entrada.txt
29/11/2020  23:18             1.470 ex08.c
29/11/2020  23:47                82 outro.txt
29/11/2020  23:45    <DIR>          pasta2
29/11/2020  23:45    <DIR>          pasta3
               4 arquivo(s)          1.807 bytes
               4 pasta(s)   101.204.164.608 bytes disponíveis

C:\Users\toninho\AppData\Local\Temp\pasta1>

 

E lá está o fonte ex08.c e lá estão os arquivos de teste.

 

Um comando possível seria
 

image.png.dea9d64720cc49cd9f132b4a812e294c.png

 

Esse comando chamaria o compilador e iria compilar o programa e gerar o arquivo ex08.exe que você poderia usar em qualquer máquina. O comando para o gcc tem uns argumentos, certo? 5 nesse caso

  • gcc: o nome do programa, no caso o compilador C
  • -o : indica que o próximo argumento é o nome do programa executável a ser gerado
  • ex08.exe: o nome do executável
  • -Wall: indica que é para gerar warnings sobre todo tipo de erro de compilação, avisos sobre não conformidades, variável declarada e não usada, tudo. É o padrão nas empresas e escolas
  • -std=c17: indica o nível do padrão da linguagem. c17 vai usar uma versão recente da linguagem C. c89 vai usar aquela do Dev-C++, de 1989
  • ex08.c: indica o nome do arquivo a ser compilado

Isso é a linha de comando. Imagine se o gcc tivesse embutido dentro dele o nome do programa a compilar. Ou mesmo que ele perguntasse na tela: "Que programa você deseja compilar?" Acha que daria certo? Não, não daria.

 

A linha de comando e o programa de teste
 

int main()
{
    gravararquivo();
    lerarquivo();
    return 128;
}

 

Coisas assim são muito comuns, mas são um desastre. Vai gravar qual arquivo? Vai ler qual arquivo? E o nome está fixo dentro do programa em dois lugares diferentes. É um pesadelo. Se mudar um tem que mudar o outro. E tem que gerar outro executável. Tem que ter outro jeito.

 

Voltando à pasta1

 

Depois de compilar o programa, um novo dir
 

C:\Users\toninho\AppData\Local\Temp\pasta1>dir
 O volume na unidade C não tem nome.
 O Número de Série do Volume é 7E52-1BF2

 Pasta de C:\Users\toninho\AppData\Local\Temp\pasta1

30/11/2020  00:02    <DIR>          .
30/11/2020  00:02    <DIR>          ..
29/11/2020  23:46                90 azul.txt
29/11/2020  23:18               165 entrada.txt
29/11/2020  23:18             1.470 ex08.c
30/11/2020  00:02           121.622 ex08.exe
29/11/2020  23:47                82 outro.txt
29/11/2020  23:45    <DIR>          pasta2
29/11/2020  23:45    <DIR>          pasta3
               5 arquivo(s)        123.429 bytes
               4 pasta(s)   101.202.071.552 bytes disponíveis

C:\Users\toninho\AppData\Local\Temp\pasta1>          

 

Lá está o ex08.exe. E lá estão os 3 arquivos de teste: entrada.txt, azul.txt e outro.txt. Como poderiam estar outros 500 arquivos  e como eu poderia pegar esse ex08.exe e levar para rodar em outra máquina onde nem tem o gcc para compilar de novo. Como vai fazer pra usar? O simples: você quer fazer como em TODOS os programs: digitar

 

ex08 entrada.txt
ex08 azul.txt
ex08 outro.txt

 

e o programa abrir esse arquivo. E se digitar apenas ex08? Ou usa um nome de arquivo padrão ou dá uma mensagem tipo: "faltou o nome do programa!". Nada mais. No caso a gente usou um padrão.

Isso é usar a linha de comando.

 

C e a linha de comando

 

Todo programa em C ao iniciar recebe a linha de comando, com todos esses parâmetros separados e arrumadinhos em argv. Quantos argumentos? argc argumentos. Só isso.

Por definição o primeiro argumento sempre existe e é o nome do programa, o nome todinho dele. Entenda que se argc é 3 vieram mais 2 argumentos, por exemplo. O primeiro é garantido.

 

argv é char** então argv[0] é char*, certo? C. E assim os argv[] são strings com os parâmetros todos na ordem de entrada. É assim que funciona. E foi isso que eu expliquei acima nos tópicos anteriores. É o que se espera de um programa em C, de um BAT, de um script. de um programa em java, qualquer coisa que rode no terminal. Desde sempre.

 

Esse devia ser o segundo programa em C que TODO estudante faz. Mas nunca é. Pra mim não foi. Por alguma razão mesmo os autores não explicam isso. Uma b0b@g3m na minha opinião.

 

O exemplo de novo

 

    const char* padrão = "entrada.txt";
    char        arquivo[80];

    if( argc>1)
        strcpy(arquivo, argv[1]);
    else
        strcpy(arquivo,padrão);

 

Pois é:

  • o padrão é: "entrada.txt".
  • se veio ao menos um argumento é o nome do arquivo de entrada e o programa vai usar esse
  • Não precisa testar nada mais. É um programa para estudantes.
  • O primeiro argumento, argv[1], é o nome do arquivo de entrada. O sistema cuida disso. Então no if() você testa: veio argv[1] ? então copia para o nome do arquivo. Se não veio copia o padrão. O trivial.

E pode testar seu programa sem stress: em arquivo está o nome do arquivo a ser aberto

 

Eis a saída:
 

C:\Users\toninho\AppData\Local\Temp\pasta1>ex08
Vai abrir "entrada.txt"
Esperadas + 10 linhas no arquivo 'entrada.txt'
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
Lidas 10 linhas de 'entrada.txt'

 

E é claro que usou "entrada.txt". E para fazer o simples o programa mostra o nome do arquivo na saída. Não vai esperar para conferir, certo?

 

C:\Users\toninho\AppData\Local\Temp\pasta1>ex08 azul.txt
Vai abrir "azul.txt"
Esperadas + 10 linhas no arquivo 'azul.txt'
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Maria 32
M Fim 27
Lidas 6 linhas de 'azul.txt'

C:\Users\toninho\AppData\Local\Temp\pasta1>       

 

Pois é: azul.txt é assim
 

10 pessoas no teste
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Maria 32
M Fim 27

 

Eu disse que eram 10 mas digitei só 6. E deixei o nome como "Fim" para ajudar a esse que vos escreve: uma marca. então está ok. Não é assim importante que não tenham todos os 10. Estamos testando. Mas é bom saber que o programa contou certinho. E mostrou na tela porque é claro que eu vou querer saber isso enquanto estou testando o código.

 

E outro.txt?
 


C:\Users\toninho\AppData\Local\Temp\pasta1>ex08 outro.txt
Vai abrir "outro.txt"
Esperadas + 5 linhas no arquivo 'outro.txt'
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Final 320
Lidas 5 linhas de 'outro.txt'

C:\Users\toninho\AppData\Local\Temp\pasta1>   

 

Dessa vez leu 5 mesmo. O arquivo era
 

5 pessoas no teste
M Joao 40
F Maria 32
M Carlos 27
M Joao 40
F Final 320

 

E se fosse um errado?
 

C:\Users\toninho\AppData\Local\Temp\pasta1>ex08 coisa
Vai abrir "coisa"
C:\Users\toninho\AppData\Local\Temp\pasta1>

 

Pois é: depois que estiver testado a gente pode usar uma mensagem melhor. Mas deu pra ver que não leu p0rr@ nenhuma. E a gente sabe como parou e é só o que importa quando você está testando.
 

Citação

Tem que chegar logo a algum resultado ou não vai receber, ou não vai ter sua nota, ou não ter seu prootótipo antes do seu concorrente...


Veja:
.

C:\Users\toninho\AppData\Local\Temp\pasta1>ex08 coisa
Vai abrir "coisa"

C:\Users\toninho\AppData\Local\Temp\pasta1>echo %ERRORLEVEL%
-1

C:\Users\toninho\AppData\Local\Temp\pasta1>

 

Isso no Windows. No Unix e derivados é $? e não ERRORLEVEL.

 

De onde veio o "-1"?
 

    printf("Vai abrir \"%s\"\n", arquivo);
    FILE* E = fopen(arquivo, "r");
    if (E == 0) return -1; // nao abriu
   

 

Pois é.

 

Estou me dando ao trabalho de escrever isso tudo porque não vejo um único programa assim aqui. E acho que agora entendeu como é o simples e versátil d efazer. Para não dizer que é a única maneira...

 

eis o programa de teste, de novo
 

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

int main(int argc, char** argv)
{
    // os valores
    char        sexo = 0;
    char        nome[50];
    unsigned    idade = 0;
    char delim[4]; // para 'comer' o fim de linha

    const char* padrão = "entrada.txt";
    char        arquivo[80];

    if (argc > 1)
        strcpy(arquivo, argv[1]);
    else
        strcpy(arquivo, padrão);

    printf("Vai abrir \"%s\"\n", arquivo);
    FILE* E = fopen(arquivo, "r");
    if (E == 0) return -1; // nao abriu

    char        linha[80];
    const char* mascara = "%c%s%d%c"; // para scanf()
    unsigned    N = 0; // total esperado de linhas
    unsigned    lidas = 0;
    int         res = 0;

    res = fscanf(E, "%d", &N);
    if (res != 1) return -2; // bobagem: nao veio o número de linhas

    printf("Esperadas + %d linhas no arquivo '%s'\n", N, arquivo);
    fgets(linha, 80, E); // para ler o resto da primeira linha
    // o arquivo foi criado como parte do exercicio
    // entao nao importa muito se tem ou nao as N linhas

    while (!feof(E))
    {
        res = fscanf(E, mascara, &sexo, nome, &idade, delim);
        if (res != 4) break; // acabou?
        printf("%c %s %d\n",
            sexo, nome, idade
        );
        lidas += 1;
        if (lidas >= N) break;
    };  // while()
    fclose(E);
    printf("Lidas %d linhas de '%s'\n",
        lidas, arquivo);
    return 0;
}

 

Essa não é a solução do programa do autor. Apenas um exemplo de como consumir os dados.

 

  • Obrigado 1

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...