Tem muitos problemas com seu programa, e ele nem faz o esperado.
Acompanhe:
- o programa lê apenas um máximo de 50 caracteres do arquivo de entrada. Mas o enunciado fala em converter um arquivo em outro.
- o programa usa
strlen
para achar o tamanho de uma suposta string de entrada, mas não está garantido em lugar algum que a entrada seja uma string. Se testar o programa com um arquivo que tenha apenas 50 'a' esperando ver na saida 50 'A' já imagina o que vai acontecer...
scanf
é usada para ler o nome dos arquivos. scanf
é um scanner, dá pra ver pelo nome. Foi escrita com outro propósito e não para ler nomes do teclado. Se o usuário digitar "Jhonny Rivers.txt" acha que vai funcionar? Não vai.
- para
scanf
, um scanner, é absolutamente normal não ler nada. Por isso um int
é retornado dizendo quantos items conseguiu ler. Só que o programa não testa e segue em frente.
- inicialize todas as variáveis e declare uma por linha. É grátis. Especialmente ponteiros não devem ficar sem um valor conhecido.
Prefira:
FILE* arquivo = fopen( nome, modo);
- o programa usa
fputs()
para gravar na saída, uma escolha errada porque vai mudar o arquivo. Essa não é a noção de converter. fputs()
acrescenta um '\n' ao final de cada linha. Vai mudar o tamanho e criar linhas onde nem tinha.
Seu programa só deve mudar as letras, de minúsculas para maiúsculas. Um arquivo de 100 letras vai virar um arquivo de 100 letras. Se tiver alguma letra. Se não tiver nenhuma seu arquivo de saída tem que ser igualzinho ao de entrada. Essa é a noção de converter.
pode até chamar perror
para mostrar uma mensagem de erro mais significativa, mas chamar exit()
em main
tem pouco sentido, porque main
é `main e retornar dela vai terminar o programa de qualquer maneira.
se deu erro ao abrir o segundo arquivo pode ser preciosismo mas deveria fechar o primeiro antes de encerrar.
não use esses comentários /* */
isso dá muito erro e é um porre pra acertar. Levou uns anos para colocarem //
em C
mas foi logo nos anos 80 então faz tempo.
não use acentos em comentários: podem não sair na tela nem serem impressos em computadores que não o seu.
ao declarar algo em C
se associa um nome a um tipo. Por exemplo em FILE *arq1, *arq2
seu compilador vai prontamente te dizer que esá declarando arq1
como FILE*
e arq2
como FILE*
. E é claro que se arq
é FILE*
então *arq
é FILE. Isso é C
. Mas não é a declaração. Sua religião pode recomendar declarar int a, *b, c;
mas a declaração é int a; int* b; int c;
, o que seu compilador pode confirmar. Prefira tipo nome
;
Sobre a conversão
Entenda que pode concluir seu programa assim como está tentando, lendo um número máximo de letras por vez e gravando na saída depois de converter.
Mas o problema com essa idéia é que de fato vai chamar uma função toupper()
para testar cada letra em um loop. Para um milhão de letras um milhão de chamadas de função, divididas em grupos pelo loop externo. Tudo para fazer a mesma coisa. Todo a
vai virar A
por exemplo. É como testar sempre se é uma minúscula para depois converter.
Em geral nesses casos se usa uma tabela, e não se testa nada.
A idéia é que cada byte da saída passa pelo programa e sai do outro igualzinho, ou alterado se for uma letra minúscula.
considere uma tabela
Veja esse trecho
int idx[256];
for (int i = 0; i < 256; i += 1) idx[i] = i;
for (int i = 'a'; i <= 'z'; i += 1)
idx[i] = i - ('a' - 'A');
ou mesmo
int idx[256];
for (int i = 0; i < 'a'; i += 1) idx[i] = i;
for (int i = 'a'; i <= 'z'; i += 1)
idx[i] = i - ('a' - 'A');
for (int i = 'z' + 1; i < 256; i += 1) idx[i] = i;
Então essa tabela é o programa: se for minúscula converte, se não for apenas copia. E os computadores são extremamente rápidos em fazer isso.
Então com os arquivos abertos seu programa poderia ser de uma linha só:
while ( (ch=fgetc(in)) >= 0) putc(idx[ch], out);
Onde ch
é int
porque fgetc
e fputc
operam com int
.
Exemplo
O programa abaixo faz a conversão. E permite que o usuário digite os nomes dos arquivos já na linha de comando se preferir. Você vai preferir, por exemplo, porque ao rodar o programa já se sabe que vai passar os nomes dos dois arquivos então é um porre ficar esperando dois prompts e digitar dois nomes e teclar dois ENTER a toa...
SOpt> ./p.exe a.txt b.txt
De "a.txt" para "b.txt"
SOpt>
Mas se não digitar, ok, espera os chatos prompts e tal
SOpt> ./p
Entrada: a.txt
Saida: b.txt
De "a.txt" para "b.txt"
SOpt>
Eis o código
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv)
{
char entrada[256] = {0};
char* p = NULL;
char saida[256] = {0};
int idx[256];
for (int i = 0; i < 'a'; i += 1) idx[i] = i;
for (int i = 'a'; i <= 'z'; i += 1)
idx[i] = i - ('a' - 'A');
for (int i = 'z' + 1; i < 256; i += 1) idx[i] = i;
if (argc > 2)
{
strcpy(entrada, argv[1]);
strcpy(saida, argv[2]);
}
else
{
printf("Entrada: ");
p = fgets(entrada, sizeof(entrada), stdin);
if (p == NULL) return -1; // nao leu nada
printf(" Saida: ");
p = fgets(saida, sizeof(saida), stdin);
if (p == NULL) return -2; // nao leu nada
}
// fgets preserva o \n se couber no vetor
if (entrada[strlen(entrada) - 1] == '\n')
entrada[strlen(entrada) - 1] = 0;
if (saida[strlen(saida) - 1] == '\n')
saida[strlen(saida) - 1] = 0;
fprintf(
stderr, " De \"%s\" para \"%s\"", entrada,
saida);
// abre os arquivos
FILE* in = fopen(entrada, "r");
if (in == NULL) return -3;
FILE* out = fopen(saida, "w");
if (out == NULL)
{
fclose(in);
return 3;
}
// converte
int ch = 0;
while ((ch = fgetc(in)) >= 0) putc(idx[ch], out);
fclose(in);
fclose(out);
return 0;
}
a tabela é sempre a mesma
Só que essa tabela idx
é
int idx[256];
for (int i = 0; i < 'a'; i += 1) idx[i] = i;
for (int i = 'a'; i <= 'z'; i += 1)
idx[i] = i - ('a' - 'A');
for (int i = 'z' + 1; i < 256; i += 1) idx[i] = i;
Isso roda então para montar a tabela: toda vez que o programa é executado vai gerar exatamente a mesma coisa. Claro que não é esperto, são apenas 256 valores que pode ter em cada byte do arquivo. idx
vai ficar sempre igualzinho, assim:
const int idx[256] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
};
Então é claro que isso já podia estar no código.
Meio chatinho de digitar, mas podia ser gerado, certo? Claro.
Esse programa
#include "stdio.h"
int main(int argc, char** argv)
{
FILE* saida = stdout;
int disco = 0; // gerou arquivo?
if (argc > 1)
{
saida = fopen(argv[1], "w");
disco = 1;
};
int out = 1;
fprintf(saida, "\nconst int idx[256] =\n{\n\n");
for (int i = 0; i < 'a'; i += 1)
{ // nao muda nada
fprintf(saida, "%3d, ", i);
if (out % 16 == 0) fprintf(saida, "\n");
out += 1;
};
for (int i = 'a'; i <= 'z'; i += 1)
{
fprintf(saida, "'%c', ", i - ('a' - 'A'));
if (out % 16 == 0) fprintf(saida, "\n");
out += 1;
};
// agora o intervalo entre '9'e 'A', 58..64
for (int i = 'z' + 1 ; i < 255; i += 1)
{
fprintf(saida, "%3d, ", i);
if (out % 16 == 0) fprintf(saida, "\n");
out += 1;
};
fprintf(saida, "255\n\n}; // ARFNeto '23\n");
if (disco)
{
fprintf(
stderr, "\nGerado trecho de codigo em '%s'\n",
argv[1]);
};
fclose(saida);
return 0;
}; // main()
faz isso, a vida toda. E dá pra escolher o nome do arquivo ou ver na saída padrão mesmo...
exemplo com a tabela estática
#include <stdio.h>
#include <string.h>
#include "tabela.h"
int main(int argc, char** argv)
{
char entrada[256] = {0};
char* p = NULL;
char saida[256] = {0};
if (argc > 2)
{
strcpy(entrada, argv[1]);
strcpy(saida, argv[2]);
}
else
{
printf("Entrada: ");
p = fgets(entrada, sizeof(entrada), stdin);
if (p == NULL) return -1; // nao leu nada
printf(" Saida: ");
p = fgets(saida, sizeof(saida), stdin);
if (p == NULL) return -2; // nao leu nada
}
// fgets preserva o \n se couber no vetor
if (entrada[strlen(entrada)-1] == '\n')
entrada[strlen(entrada)-1] = 0;
if (saida[strlen(saida)-1] == '\n')
saida[strlen(saida)-1] = 0;
fprintf(stderr, " De \"%s\" para \"%s\"", entrada, saida);
// abre os arquivos
FILE* in = fopen(entrada, "r");
if (in == NULL) return -3;
FILE* out = fopen(saida, "w");
if (out == NULL)
{
fclose(in);
return 3;
}
// converte
int ch = 0;
while ( (ch=fgetc(in)) >= 0) putc(idx[ch], out);
fclose(in);
fclose(out);
return 0;
}
E tabela.h
pode ser gerado pelo programa anterior, apenas usando
./gen tabela.h
// ou
./gen > tabela.h
Considere