Problema com arquiuvo [RESOLVIDO]

1. Problema com arquiuvo [RESOLVIDO]

Matheus de Castro Oliveira
Matheus10772

(usa Linux Mint)

Enviado em 02/07/2019 - 14:30h

Olá, estou aprendendo a programar em c, e estou tendo um problema com a criação do arquivo "zum3.txt"

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

void main () {
//char *arqname1 = (char*) malloc(50* sizeof(char*));
//char *arqname2 = (char*) malloc(50* sizeof(char*));
char arqname1[30];
char arqname2[30];

printf("\nDigite o nome do primeiro arquivo\n");
scanf("%s",arqname1);
printf("\nDigite o nome do segundo arquivo\n");
scanf("%s",arqname2);
FILE *arq1 = fopen(arqname1,"r");
if(arq1==NULL){printf("\nErro ao abrir o arquiuvo\n");}
FILE *arq2 = fopen(arqname2,"r");
if(arq2==NULL){printf("\nErro ao abrir o arquiuvo\n");}
FILE *arq3 = fopen("final.txt","w+");
if(arq3==NULL){printf("\nErro ao abrir o arquiuvo\n");}
char i;
int c;
int v;
while ((i=fgetc(arq1))!=EOF)
{
c++;
printf("%c", i);
}
while ((i=fgetc(arq2))!=EOF)
{
v++;
printf("%c", i);
}

char *leitura1 = (char*) malloc(c*sizeof(char*));
fread(leitura1, sizeof(char), c, arq1);
char *leitura2 = (char*) malloc(v*sizeof(char*));
fread(leitura2, sizeof(char), v, arq2);
strcat(leitura2,leitura1);
fwrite(leitura2,sizeof(char), c+v, arq3);
while((i=fgetc(arq3))!=EOF){
printf("%c", i);
}
fclose(arq1);
fclose(arq2);
fclose(arq3);
exit(1);
}


O programa compila normalmente, o arquivo zum3.txt não está recebendo o que está em zum1.txt e zum2.txt


  


2. Re: Problema com arquiuvo

Paulo
paulo1205

(usa Ubuntu)

Enviado em 03/07/2019 - 19:41h

Matheus10772 escreveu:

Olá, estou aprendendo a programar em c, e estou tendo um problema com a criação do arquivo "zum3.txt"

O programa compila normalmente, o arquivo zum3.txt não está recebendo o que está em zum1.txt e zum2.txt


Compilar normalmente significa apenas que seu programa não tem erros de sintaxe, mas não garante que ele esteja semanticamente correto.

Mas mesmo que esteja sintaticamente correto, existe pelo menos um problema sobre o qual o compilador poderia ter lhe avisado, se você tivesse habilitado opções de diagnóstico corretas: o tipo de retorno da função main, de acordo com o padrão do C, tem de ser int. No caso do GCC e do CLANG, eu recomendo as seguintes opções de diagnóstico: “-Wall -Werror -O2 -pedantic-errors”.

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

void main () {


Como já mencionado acima, o tipo de retorno de main está errado. Mas também esses parênteses vazios, cujo sentido é o de que a função pode receber uma quantidade qualquer de argumentos de quaisquer tipos, estão em desacordo com o padrão do C, que diz que você deve escolher se a função main não vai receber argumento nenhum (caso em que ela deve ser declarada com “int main(void)”, em que a palavra-chave void explicita uma lista de argumentos vazia) ou se ela deve receber como argumentos na frma de um array de strings (caso em que ela deve ser declarada numa forma compatível com “int main(int argc, char **argv)”, em que argc indica quantos elementos argv possui e argv contém ponteiros para cada uma das strings).

    //char *arqname1 = (char*) malloc(50* sizeof(char*));
//char *arqname2 = (char*) malloc(50* sizeof(char*));


Está como comentado, mas você usa a mesma construção efetivamente mais abaixo, então vou explicar logo aqui.

A melhor forma de fazer alocação dinâmica em C é do seguinte modo.
ponteiro = malloc(n_elementos * sizeof ponteiro[0]); 

Ou seja, você não precisa converter explicitamente o resultado da chamada a malloc(), e o tipo do elemento pode ser inferido pelo próprio compilador a partir do tipo de um elemento qualquer do array ao qual aquele ponteiro se referirá (no caso acima, escolhemos o primeiro elemento, “ponteiro[0]”).

Fazendo desse modo, você só precisa escrever o nome do tipo uma vez, que é no momento da declaração do ponteiro que vai apontar para o array, o que é uma grande vantagem do ponto de vista de manutenção de código.

Mas o problema maior com a forma como você escreveu não é nem a eficiência, mas o fato de que a etapa de dizer o tamanho de cada elemento está errada, pois você errou em pedir “sizeof(char*)”. Se arqname1 e arqname2 são ponteiros para char, então os elementos para os quais eles apontam têm tipo char, não char*. Então, ou você deveria ter dito “sizeof(char)”, ou deveria ter usado a forma que eu mostrei acima, em que você faz com que o próprio compilador calcule o tamanho, de acordo com tipo do elemento que aquele ponteiro é destinado a apontar.

Uma terceira opção, que se aplica apenas a ponteiros para char, é lembrar que sizeof(char), por definição, é sempre igual a 1, e omitir essa multiplicação, deixando apenas o tamanho desejado.

    char arqname1[30];
char arqname2[30];

printf("\nDigite o nome do primeiro arquivo\n");
scanf("%s",arqname1);


Seria bom você limitar a quantidade máxima de caracteres que scanf() pode receber, de modo a não exceder a capacidade alocada do array arqname1. Além disso seria bom você confirmar que a leitura funcionou, antes de prosseguir com a execução do programa.

Uma forma mais segura de fazer seria a seguinte (levando em consideração o tamanho alocado de 30 caracteres).
int c1=0, c2=0;  // Contadores de caracteres consumidos durante a leitura (para pseudoconversão "%n").
if(scanf("%29[^\n]%n%1*[\n]%n", arqname1, &c1, &c2)<1 || c2<=c1){
if(c1>=29)
fprintf(stderr, "Tamanho máximo do nome do arquivo excedido.\n");
else
fprintf(stderr, "Erro de leitura ao ler nome do arquivo.\n");
exit(1);
}
// Se chegou neste ponto, a leitura foi bem sucedida.


O problema de fazer assim é que você fica com uma amarração de tamanho dentro da string de formatação de scanf(), e com um valor fixo que nem mesmo é igual ao valor usado na declaração, mas tem de ser um a menos, e em forma de string (no caso acima, 30 e "29", respectivamente), o que não facilita nem um pouco a manutenção do código.

Uma solução melhor ainda, caso necessária, teria de ser construída com uma quantidade maior de funções padronizadas, ou requereria o uso de funções não-padronizadas ou padronizadas apenas em alguns contextos (por exemplo, a função getline() que existe no POSIX, mas não no C padrão) e formas diferentes de se trabalhar (a mesma getline(), por exemplo, exige o uso de ponteiros, não podendo trabalhar com arrays nativos).

    printf("\nDigite o nome do segundo arquivo\n");
scanf("%s",arqname2);
FILE *arq1 = fopen(arqname1,"r");
if(arq1==NULL){printf("\nErro ao abrir o arquiuvo\n");}
FILE *arq2 = fopen(arqname2,"r");
if(arq2==NULL){printf("\nErro ao abrir o arquiuvo\n");}
FILE *arq3 = fopen("final.txt","w+");
if(arq3==NULL){printf("\nErro ao abrir o arquiuvo\n");}
char i;


Aqui você comete um erro comum (mas que continua sendo erro, mesmo que muitas pessoas também o cometam): como você vai usar i para recebr o valor retornado por fgetc(), então o tipo deveria se int, porque fgetc() pode retornar valores fora da faixa representável por char (na verdade, por unsigned char). Veja o que diz sua documentação (de acordo com a manpage que acompanha o Ubuntu 18.04; “C89” e “C99” são as versões de 1989/1990 e 1999 do padrão do C, respectivamente).

int fgetc(FILE *stream);
[...]
fgetc() reads the next character from
stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.
[...]
CONFORMING TO POSIX.1-2001, POSIX.1-2008, C89, C99.


    int c;
int v;
while ((i=fgetc(arq1))!=EOF)
{
c++;
printf("%c", i);
}
while ((i=fgetc(arq2))!=EOF)
{
v++;
printf("%c", i);
}


Se eu entendi bem, sua intenção com os dois laços de repetição acima é medir o tamanho de cada um dos dois arquivos, fazendo com que eles sejam lidos caráter a caráter e contando a quantidade de caracteres total em ambos os casos.

Além do erro de tipo associado à variável que recebe o valor retornado por fgetc(), outra coisa que eu achei estranha é para que você faz apenas essa medição, sem aproveitar para já fazer logo de uma vez a cópia para o arquivo de destino.

Porém, para além disso, esse código infelizmente está errado (tendo em vista que você vai, depois, usar esses tamanhos medidos para tentar fazer uma leitura dos mesmos arquivos com fread()), porque você abriu os arquivos em modo texto, o que significa que a quantidade de caracteres lógicos, usados por funções de leitura e escrita orientadas a texto, tais como fgetc(), fgets(), scanf()/fscanf(), fputc(), fputs() e printf()/sprintf(), entre outras, pode ser diferente do tamanho do arquivo em caracteres físicos (ou bytes), que são usados por funções orientadas a blocos dados ou que considerem arquivos como simples correntes de bytes, tais como fread(), fwrite(), ftell() e fseek(). Na verdade, é temerário usar funções orientadas a byte se o arquivo não tiver sido aberto com o modificador "b" presente no modo de abertura com a função fopen() (por exemplo: com "rb" em lugar de "r", ou "wb+" em lugar de "w+").

Alguns sistemas, incluindo UNIX e Linux, tratam arquivos de texto de maneira idêntica a arquivos binários, de modo que o problema que eu apresento no parágrafo anterior não ocorre nesses sistemas. Contudo, você não nos disse qual sistema está usando — pode ser que seja Windows, por exemplo, em que arquivos de texto usam tradução do caráter que indica o fim de uma linha, '\n', usando dois bytes físicos no dispositivo (disco ou teclado) para um único caráter lógico na memória. E mesmo que você esteja usando Linux, convém escrever programas de um modo em que ele corra risco mínimo de adquirir um comportamento errado apenas porque foi movido para outro computador. Ainda mais quando esse tipo de erro não tem como ser detectado de modo simples pelo compilador, pois não é um erro sintático, mas semântico.

    
char *leitura1 = (char*) malloc(c*sizeof(char*));


Aqui, de novo, o problema de alocação feita com tamanho errado.


fread(leitura1, sizeof(char), c, arq1);


E aqui você tenta reler de uma só vez o arquivo inteiro para a memória. Só que você já chegou ao fim do arquivo num dos laços acima de medição do tamanho, e a leitura vai falhar imediatamente porque não há mais dados a ler. Para poder reler, você teria de voltar ao início (por exemplo, usando rewind() ou lseek()). E mesmo fazendo isso, ainda teria de ajustar a forma de calcular o tamanho.

    char *leitura2 = (char*) malloc(v*sizeof(char*));
fread(leitura2, sizeof(char), v, arq2);


Idem.

    strcat(leitura2,leitura1); 


Epa! Você alocou espaço para v elementos e já os ocupou, mas agora está querendo concatenar mais c elementos, sem aumentar o espaço?

    fwrite(leitura2,sizeof(char), c+v, arq3); 


Aqui, também, você deveria chamar rewind() ou fseek() para voltar ao início do arquivo, antes de começar a ler.

    while((i=fgetc(arq3))!=EOF){
printf("%c", i);
}
fclose(arq1);
fclose(arq2);
fclose(arq3);
exit(1);
}


No fim das contas, se seu objetivo for apenas concatenar dois arquivos num terceiro arquivo, daria para fazer de um modo muito mais simples: abra os três em modo binário, copie todo o conteúdo (um pouco de cada vez, se for muito grande) do primeiro arquivo para o destino, e, sem interrupção, copie todo o conteúdo do segundo arquivo para o destino, fechando os três arquivos.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)


3. Re: Problema com arquiuvo [RESOLVIDO]

Matheus de Castro Oliveira
Matheus10772

(usa Linux Mint)

Enviado em 19/07/2019 - 00:08h

paulo1205 escreveu:

Matheus10772 escreveu:

Olá, estou aprendendo a programar em c, e estou tendo um problema com a criação do arquivo "zum3.txt"

O programa compila normalmente, o arquivo zum3.txt não está recebendo o que está em zum1.txt e zum2.txt


Compilar normalmente significa apenas que seu programa não tem erros de sintaxe, mas não garante que ele esteja semanticamente correto.

Mas mesmo que esteja sintaticamente correto, existe pelo menos um problema sobre o qual o compilador poderia ter lhe avisado, se você tivesse habilitado opções de diagnóstico corretas: o tipo de retorno da função main, de acordo com o padrão do C, tem de ser int. No caso do GCC e do CLANG, eu recomendo as seguintes opções de diagnóstico: “-Wall -Werror -O2 -pedantic-errors”.

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

void main () {


Como já mencionado acima, o tipo de retorno de main está errado. Mas também esses parênteses vazios, cujo sentido é o de que a função pode receber uma quantidade qualquer de argumentos de quaisquer tipos, estão em desacordo com o padrão do C, que diz que você deve escolher se a função main não vai receber argumento nenhum (caso em que ela deve ser declarada com “int main(void)”, em que a palavra-chave void explicita uma lista de argumentos vazia) ou se ela deve receber como argumentos na frma de um array de strings (caso em que ela deve ser declarada numa forma compatível com “int main(int argc, char **argv)”, em que argc indica quantos elementos argv possui e argv contém ponteiros para cada uma das strings).

    //char *arqname1 = (char*) malloc(50* sizeof(char*));
//char *arqname2 = (char*) malloc(50* sizeof(char*));


Está como comentado, mas você usa a mesma construção efetivamente mais abaixo, então vou explicar logo aqui.

A melhor forma de fazer alocação dinâmica em C é do seguinte modo.
ponteiro = malloc(n_elementos * sizeof ponteiro[0]); 

Ou seja, você não precisa converter explicitamente o resultado da chamada a malloc(), e o tipo do elemento pode ser inferido pelo próprio compilador a partir do tipo de um elemento qualquer do array ao qual aquele ponteiro se referirá (no caso acima, escolhemos o primeiro elemento, “ponteiro[0]”).

Fazendo desse modo, você só precisa escrever o nome do tipo uma vez, que é no momento da declaração do ponteiro que vai apontar para o array, o que é uma grande vantagem do ponto de vista de manutenção de código.

Mas o problema maior com a forma como você escreveu não é nem a eficiência, mas o fato de que a etapa de dizer o tamanho de cada elemento está errada, pois você errou em pedir “sizeof(char*)”. Se arqname1 e arqname2 são ponteiros para char, então os elementos para os quais eles apontam têm tipo char, não char*. Então, ou você deveria ter dito “sizeof(char)”, ou deveria ter usado a forma que eu mostrei acima, em que você faz com que o próprio compilador calcule o tamanho, de acordo com tipo do elemento que aquele ponteiro é destinado a apontar.

Uma terceira opção, que se aplica apenas a ponteiros para char, é lembrar que sizeof(char), por definição, é sempre igual a 1, e omitir essa multiplicação, deixando apenas o tamanho desejado.

    char arqname1[30];
char arqname2[30];

printf("\nDigite o nome do primeiro arquivo\n");
scanf("%s",arqname1);


Seria bom você limitar a quantidade máxima de caracteres que scanf() pode receber, de modo a não exceder a capacidade alocada do array arqname1. Além disso seria bom você confirmar que a leitura funcionou, antes de prosseguir com a execução do programa.

Uma forma mais segura de fazer seria a seguinte (levando em consideração o tamanho alocado de 30 caracteres).
int c1=0, c2=0;  // Contadores de caracteres consumidos durante a leitura (para pseudoconversão "%n").
if(scanf("%29[^\n]%n%1*[\n]%n", arqname1, &c1, &c2)<1 || c2<=c1){
if(c1>=29)
fprintf(stderr, "Tamanho máximo do nome do arquivo excedido.\n");
else
fprintf(stderr, "Erro de leitura ao ler nome do arquivo.\n");
exit(1);
}
// Se chegou neste ponto, a leitura foi bem sucedida.


O problema de fazer assim é que você fica com uma amarração de tamanho dentro da string de formatação de scanf(), e com um valor fixo que nem mesmo é igual ao valor usado na declaração, mas tem de ser um a menos, e em forma de string (no caso acima, 30 e "29", respectivamente), o que não facilita nem um pouco a manutenção do código.

Uma solução melhor ainda, caso necessária, teria de ser construída com uma quantidade maior de funções padronizadas, ou requereria o uso de funções não-padronizadas ou padronizadas apenas em alguns contextos (por exemplo, a função getline() que existe no POSIX, mas não no C padrão) e formas diferentes de se trabalhar (a mesma getline(), por exemplo, exige o uso de ponteiros, não podendo trabalhar com arrays nativos).

    printf("\nDigite o nome do segundo arquivo\n");
scanf("%s",arqname2);
FILE *arq1 = fopen(arqname1,"r");
if(arq1==NULL){printf("\nErro ao abrir o arquiuvo\n");}
FILE *arq2 = fopen(arqname2,"r");
if(arq2==NULL){printf("\nErro ao abrir o arquiuvo\n");}
FILE *arq3 = fopen("final.txt","w+");
if(arq3==NULL){printf("\nErro ao abrir o arquiuvo\n");}
char i;


Aqui você comete um erro comum (mas que continua sendo erro, mesmo que muitas pessoas também o cometam): como você vai usar i para recebr o valor retornado por fgetc(), então o tipo deveria se int, porque fgetc() pode retornar valores fora da faixa representável por char (na verdade, por unsigned char). Veja o que diz sua documentação (de acordo com a manpage que acompanha o Ubuntu 18.04; “C89” e “C99” são as versões de 1989/1990 e 1999 do padrão do C, respectivamente).

int fgetc(FILE *stream);
[...]
fgetc() reads the next character from
stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.
[...]
CONFORMING TO POSIX.1-2001, POSIX.1-2008, C89, C99.


    int c;
int v;
while ((i=fgetc(arq1))!=EOF)
{
c++;
printf("%c", i);
}
while ((i=fgetc(arq2))!=EOF)
{
v++;
printf("%c", i);
}


Se eu entendi bem, sua intenção com os dois laços de repetição acima é medir o tamanho de cada um dos dois arquivos, fazendo com que eles sejam lidos caráter a caráter e contando a quantidade de caracteres total em ambos os casos.

Além do erro de tipo associado à variável que recebe o valor retornado por fgetc(), outra coisa que eu achei estranha é para que você faz apenas essa medição, sem aproveitar para já fazer logo de uma vez a cópia para o arquivo de destino.

Porém, para além disso, esse código infelizmente está errado (tendo em vista que você vai, depois, usar esses tamanhos medidos para tentar fazer uma leitura dos mesmos arquivos com fread()), porque você abriu os arquivos em modo texto, o que significa que a quantidade de caracteres lógicos, usados por funções de leitura e escrita orientadas a texto, tais como fgetc(), fgets(), scanf()/fscanf(), fputc(), fputs() e printf()/sprintf(), entre outras, pode ser diferente do tamanho do arquivo em caracteres físicos (ou bytes), que são usados por funções orientadas a blocos dados ou que considerem arquivos como simples correntes de bytes, tais como fread(), fwrite(), ftell() e fseek(). Na verdade, é temerário usar funções orientadas a byte se o arquivo não tiver sido aberto com o modificador "b" presente no modo de abertura com a função fopen() (por exemplo: com "rb" em lugar de "r", ou "wb+" em lugar de "w+").

Alguns sistemas, incluindo UNIX e Linux, tratam arquivos de texto de maneira idêntica a arquivos binários, de modo que o problema que eu apresento no parágrafo anterior não ocorre nesses sistemas. Contudo, você não nos disse qual sistema está usando — pode ser que seja Windows, por exemplo, em que arquivos de texto usam tradução do caráter que indica o fim de uma linha, '\n', usando dois bytes físicos no dispositivo (disco ou teclado) para um único caráter lógico na memória. E mesmo que você esteja usando Linux, convém escrever programas de um modo em que ele corra risco mínimo de adquirir um comportamento errado apenas porque foi movido para outro computador. Ainda mais quando esse tipo de erro não tem como ser detectado de modo simples pelo compilador, pois não é um erro sintático, mas semântico.

    
char *leitura1 = (char*) malloc(c*sizeof(char*));


Aqui, de novo, o problema de alocação feita com tamanho errado.


fread(leitura1, sizeof(char), c, arq1);


E aqui você tenta reler de uma só vez o arquivo inteiro para a memória. Só que você já chegou ao fim do arquivo num dos laços acima de medição do tamanho, e a leitura vai falhar imediatamente porque não há mais dados a ler. Para poder reler, você teria de voltar ao início (por exemplo, usando rewind() ou lseek()). E mesmo fazendo isso, ainda teria de ajustar a forma de calcular o tamanho.

    char *leitura2 = (char*) malloc(v*sizeof(char*));
fread(leitura2, sizeof(char), v, arq2);


Idem.

    strcat(leitura2,leitura1); 


Epa! Você alocou espaço para v elementos e já os ocupou, mas agora está querendo concatenar mais c elementos, sem aumentar o espaço?

    fwrite(leitura2,sizeof(char), c+v, arq3); 


Aqui, também, você deveria chamar rewind() ou fseek() para voltar ao início do arquivo, antes de começar a ler.

    while((i=fgetc(arq3))!=EOF){
printf("%c", i);
}
fclose(arq1);
fclose(arq2);
fclose(arq3);
exit(1);
}


No fim das contas, se seu objetivo for apenas concatenar dois arquivos num terceiro arquivo, daria para fazer de um modo muito mais simples: abra os três em modo binário, copie todo o conteúdo (um pouco de cada vez, se for muito grande) do primeiro arquivo para o destino, e, sem interrupção, copie todo o conteúdo do segundo arquivo para o destino, fechando os três arquivos.


... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)



olá, desculpa a demora para responder mas eu resolvi o problemas da seguinte forma:


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

int main(void) {
char arqnome1[30], arqnome2[30], arqnome3[30];

printf("\nDigite o nome de dois arquivos para concatenar\n");
scanf("%s %s", arqnome1, arqnome2);
FILE *arq1 = fopen(arqnome1, "r+");
FILE *arq2 = fopen(arqnome2, "r+");

setbuf(stdin, NULL);
if (arq1 == NULL || arq2 == NULL) { printf("\nFalha ao abrir um dos arquivos"); exit(1); }

printf("\nDigite o nome do arquivo que recebera a juncao\n");
scanf("%s", arqnome3);
setbuf(stdin, NULL);
FILE *arq3 = fopen(arqnome3, "r+");
if (arq3 == NULL) { printf("\nFalha ao abrir um dos arquivos"); exit(1); }

char c;
while ((c = fgetc(arq1)) != EOF)
{
fputc(c, arq3);
}


fseek(arq3, 0, SEEK_END);
char temp = {' '};
fputc(temp, arq3);
char b;


while ((b = fgetc(arq2)) != EOF)
{
fputc(b, arq3);
}

printf("\nConcatenacao feita com sucesso\n");


fclose(arq1);
fclose(arq2);
fclose(arq3);

return 0;
}







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts