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)