Estava lendo o EP3 e na parte "Requisitos" no tópico 3 diz: "Não é possível examinar o conteúdo de um arquivo binário diretamente num editor de textos..."
Gostaria de saber se o programa deve salvar o arquivo comprimido em binário, e como que faz para salvá-lo em binário.
Quando você for abrir o arquivo comprimido para escrita, passe à função fopen o modo "wb":
FILE *saida;
saida = fopen(nome_arq_comprimido, "wb");
if (saida == NULL) {
printf("Erro na abertura do arquivo de saída\n");
exit(1);
}FILE *saida;
saida = fopen(nome_arq_comprimido, "wb");
if (saida == NULL) {
printf("Erro na abertura do arquivo de saída\n");
exit(1);
O "b" no "wb" é para escrita de arquivo binário. É interessante saber que o "b" não faz diferença alguma no Unix e em sistemas derivados do Unix, como o Linux, o FreeBSD e o Mac OS X. Nos sistemas da Microsoft, entretanto, o "b" é estritamente necessário, pois é neles que existe a diferença entre escrita/leitura em modo texto e em modo binário. Num sistema da Microsoft, quando um programa C escreve um arquivo em modo texto, a biblioteca do C faz automaticamente a conversão entre o caractere terminador de linhas '\n', visto pelo programa C, e o par de caracteres CR LF (carriage return e line feed) usado como terminador de linhas pelo sistema da Microsoft. Quando o programa escreve um '\n' (via fprintf(saida, "%c", c) ou via fputc(c, saida)), o que realmente vai para o arquivo é um par CR LF. O "b" simplesmente desliga essa conversão automática. Isso é que é "escrita em modo binário"!
Para criar programas portáteis (que podem ser recompilados e rodar em qualquer sistema) é uma boa idéia colocar o "b", mesmo que no Linux a gente não precise dele.
Para criar programas portáteis (que podem ser recompilados e rodar em qualquer sistema) é uma boa idéia colocar o "b", mesmo que no Linux a gente não precise dele.
Obrigado!
Ah! Eu tenho outra duvida na gravação.
Eu estou fazendo o programa, e na gravação dos pares se colocamos no formato (x,y), com todos os parenteses e virgula, o arquivo de texto fica maior do que o original. Estou gravando os pares no formato %c%c tudo junto, está certo?
Ah! Eu tenho outra duvida na gravação.
Eu estou fazendo o programa, e na gravação dos pares se colocamos no formato (x,y), com todos os parenteses e virgula, o arquivo de texto fica maior do que o original. Estou gravando os pares no formato %c%c tudo junto, está certo?
Sim. Usar o formato (x,y) custa 6 caracteres ( "(","x",",","y",")"," "). Enquanto no outro formato %c%c custa apenas 3 ("x","y"," ").
Por que 3 bytes? Não há necessidade de colocar nenhum caracter separador (como o caracter espaço) entre as referências (pares). Se você usar "%c%c" para escrever cada referência e se não colocar nada para separar uma referência da referência anterior, então o custo será 2 bytes por referência.
Fui olhar o enunciado, que mostra um exemplo de arquivo comprimido, e entendi o motivo da confusão. No exemplo do enunciado os pares de fato parecem estar separados por espaços. Esses espaços não existem de verdade, eles aparecem no enunciado apenas por legibilidade!
O arquivo comprimido deve conter somente a seqüência de pares de bytes, sem nada separando um par do outro.
Notem que as referências são pares de bytes (números entre 0 e 255). Se o primeiro número de um par for zero, então o segundo número será interpretado como o código de um caractere. (Nesse caso o exemplo do enunciado mostra o caractere, em fonte de comprimento fixo, em vez do número.) Se o primeiro número de um par for diferente de zero, então o segundo número será interpretado como o comprimento da seqüência referenciada.
O arquivo comprimido deve conter somente a seqüência de pares de bytes, sem nada separando um par do outro.
Notem que as referências são pares de bytes (números entre 0 e 255). Se o primeiro número de um par for zero, então o segundo número será interpretado como o código de um caractere. (Nesse caso o exemplo do enunciado mostra o caractere, em fonte de comprimento fixo, em vez do número.) Se o primeiro número de um par for diferente de zero, então o segundo número será interpretado como o comprimento da seqüência referenciada.
Certo, então o exemplo do enunciado: 0l 0e 0= 00 0; 0w 0h 0i 8 2 0( 11 3 12 2 0) 0{ 12 2 18 9 11 3 18 7
Vai ficar: 0l0e0=000;0w0h0i820(1131220)0{122189113187
Mas isso não pode dar problema nos números, por exemplo nos pares 11 3 e 12 1, não pode acontecer do programa quando for ler 113121 (sequencia do arquivo compactado, sem espaços) acabar lendo pares como 1 1, 3 1, 2 1 ?
Vai ficar: 0l0e0=000;0w0h0i820(1131220)0{122189113187
Mas isso não pode dar problema nos números, por exemplo nos pares 11 3 e 12 1, não pode acontecer do programa quando for ler 113121 (sequencia do arquivo compactado, sem espaços) acabar lendo pares como 1 1, 3 1, 2 1 ?
No começo eu tive esse problema também. Mas eu consegui arrumar transformando numeros com mais de um dígito em um caractere. Pois o número máximo da distãncia nao vai ultrapassar o limite da variável char. E lembrei da aula que o professor falou que pode tratar chars como int.
Não sei se pode fazer isso, mas deu certo =)
Não sei se pode fazer isso, mas deu certo =)
Pode fazer sim. A idéia é gravar números no intervalo [0,255] como chars.
Me disseram que conversaram com o senhor e você disse para o programa imprimir na verdade 2 arquivos:
Um de texto contendo os pares da forma como o exemplo colocado no pdf (0e 0u 22 34, para "eueueueu", etc) e outro contendo o arquivo comprimido em formato binário, é isso mesmo?
Senão, é necessário apenas um tipo deles (o binário)?
Obrigado
Sim, a compressão deve produzir dois arquivos: o arquivo comprimido e um arquivo de texto contendo os pares que foram gerados. Esses pares não precisam necessáriamente aparecer como no exemplo do enunciado. Eles podem, por exemplo, ser apresentados num formato como o da saída do programa xxd, que é melhor para dados binários.
Lembre-se que cada um desses números não será gravado como um seqüência de dígitos decimais e sim como um byte (char) contendo a representação binária do número. O programa não vai ler 113121, pois o que ele verá é esta seqüência de bytes: (byte contendo 11) (byte contendo 3) (byte contendo 12) (byte contendo 1).
Repare que todos os números que seu programa precisará gravar estão no intervalo de 0 a 255, ou seja, cabem em um byte.
Repare que todos os números que seu programa precisará gravar estão no intervalo de 0 a 255, ou seja, cabem em um byte.
Eu tenho uma duvida sobre o tipo de char a ser usado.
No caso do nosso Ep faz diferença na hora da impressão usar unsigned char ou char? eu estou usando o comando fputc, o que na verdade ele imprimi? e tambám gostaria de perguntar o que é o EOF, é um caracter ou uma resposta do getchar e do fgetc para sinalizar que não foi possivel pegar nenhum caracter?
E agora eu fiquei em duvida: a opção wd, para imprimir em binario, muda alguma coisa no resultado da impressao? O que muda, de fato, na portabilidade do programa com a opção wd, ou eu preciso fazer essa opção de forma condicional?
No caso do nosso Ep faz diferença na hora da impressão usar unsigned char ou char? eu estou usando o comando fputc, o que na verdade ele imprimi? e tambám gostaria de perguntar o que é o EOF, é um caracter ou uma resposta do getchar e do fgetc para sinalizar que não foi possivel pegar nenhum caracter?
E agora eu fiquei em duvida: a opção wd, para imprimir em binario, muda alguma coisa no resultado da impressao? O que muda, de fato, na portabilidade do programa com a opção wd, ou eu preciso fazer essa opção de forma condicional?
No caso do nosso Ep faz diferença na hora da impressão usar unsigned char ou char?
Na impressão não faz diferença nenhuma. Faz diferença quando você fizer alguma operação aritmética com caracteres ou quando atribuir caracteres a inteiros. Por exemplo:
char c;
unsigned char uc;
int i, j;
c = uc = 255;
i = uc;
j = c;
printf("%d %d\n", i, j);
A saída, nesse caso, é
255 -1
eu estou usando o comando fputc, o que na verdade ele imprimi?
Ele imprime o caractere passado no primeiro parâmetro. Note que o protótipo da função fputc é
int fputc(int c, FILE *stream);
Embora o tipo do parâmetro c seja int, a função fputc, internamente, converte c para unsigned char antes de imprimi-lo. "Converter c para unsigned char" significa "pegar os 8 bits menos significativos de c e descartar os demais".
e tambám gostaria de perguntar o que é o EOF, é um caracter ou uma resposta do getchar e do fgetc para sinalizar que não foi possivel pegar nenhum caracter?
É uma resposta do getchar/getc/fgetc para indicar que não pode pegar mais um caracter porque chegou ao final do arquivo. Não existe um caractere EOF!
a opção wd, para imprimir em binario, muda alguma coisa no resultado da impressao?
A opção que deve ser passada ao fopen para escrita em modo binário é "wb" e não "wd".
No Linux essa opção não faz coisa nenhuma, ela não muda nada no resultado da escrita.
No Windows, ela desliga a conversão de caracteres LF em pares CR LF. Ou seja, sem o "b", quando um programa rodando no Windows escrever (via fputc(c, saida)ou via fprintf(saida, "%c, c)) um byte com valor 10 (que é o código do caractere LF ou '\n'), o que vai para o arquivo de saída são dois bytes: o primeiro com valor 13 (que é o código do caractere CR) e o segundo com valor 10. Com o "b", só um byte (contendo 10) vai para o arquivo de saída. (Esse é sempre o comportamento do Linux, independentemente de se usar o "b" ou não.)
O que muda, de fato, na portabilidade do programa com a opção wd, ou eu preciso fazer essa opção de forma condicional?
O recomendável é usar "wb" para que o programa seja portátil, ou seja, para que ele possa ser compilado e executado (e apresente o mesmo comportamento) tanto no Linux como no Windows. Se você não colocar o "b" o programa funcionará corretamente no Linux mas não no Windows. Ele precisará ser alterado para funcionar corretamente no Windows, pois no Windows o "b" é necessário.
Na impressão não faz diferença nenhuma. Faz diferença quando você fizer alguma operação aritmética com caracteres ou quando atribuir caracteres a inteiros. Por exemplo:
char c;
unsigned char uc;
int i, j;
c = uc = 255;
i = uc;
j = c;
printf("%d %d\n", i, j);
A saída, nesse caso, é
255 -1
eu estou usando o comando fputc, o que na verdade ele imprimi?
Ele imprime o caractere passado no primeiro parâmetro. Note que o protótipo da função fputc é
int fputc(int c, FILE *stream);
Embora o tipo do parâmetro c seja int, a função fputc, internamente, converte c para unsigned char antes de imprimi-lo. "Converter c para unsigned char" significa "pegar os 8 bits menos significativos de c e descartar os demais".
e tambám gostaria de perguntar o que é o EOF, é um caracter ou uma resposta do getchar e do fgetc para sinalizar que não foi possivel pegar nenhum caracter?
É uma resposta do getchar/getc/fgetc para indicar que não pode pegar mais um caracter porque chegou ao final do arquivo. Não existe um caractere EOF!
a opção wd, para imprimir em binario, muda alguma coisa no resultado da impressao?
A opção que deve ser passada ao fopen para escrita em modo binário é "wb" e não "wd".
No Linux essa opção não faz coisa nenhuma, ela não muda nada no resultado da escrita.
No Windows, ela desliga a conversão de caracteres LF em pares CR LF. Ou seja, sem o "b", quando um programa rodando no Windows escrever (via fputc(c, saida)ou via fprintf(saida, "%c, c)) um byte com valor 10 (que é o código do caractere LF ou '\n'), o que vai para o arquivo de saída são dois bytes: o primeiro com valor 13 (que é o código do caractere CR) e o segundo com valor 10. Com o "b", só um byte (contendo 10) vai para o arquivo de saída. (Esse é sempre o comportamento do Linux, independentemente de se usar o "b" ou não.)
O que muda, de fato, na portabilidade do programa com a opção wd, ou eu preciso fazer essa opção de forma condicional?
O recomendável é usar "wb" para que o programa seja portátil, ou seja, para que ele possa ser compilado e executado (e apresente o mesmo comportamento) tanto no Linux como no Windows. Se você não colocar o "b" o programa funcionará corretamente no Linux mas não no Windows. Ele precisará ser alterado para funcionar corretamente no Windows, pois no Windows o "b" é necessário.
Eu estava testando o meu ep quando percebi que quando abria um arquivo sem o "b" (entrada = fopen(nome_arq_entrada, "r")) e o comprimia, o arquivo comprimido era menor do que o arquivo comprimido utilizando o "b" (entrada = fopen(nome_arq_entrada, "rb")).
Porém, conversando com o Henrique, ele me disse que no Linux isso não fazia diferença (a propósito, estou realizando os testes no Windows, já que não tenho o Linux em casa). Não sei se isto é um problema com o meu ep ou se é normal isso acontecer... Se alguém puder me ajudar eu agradeço.
Porém, conversando com o Henrique, ele me disse que no Linux isso não fazia diferença (a propósito, estou realizando os testes no Windows, já que não tenho o Linux em casa). Não sei se isto é um problema com o meu ep ou se é normal isso acontecer... Se alguém puder me ajudar eu agradeço.
Esqueci de dizer que nos dois casos o arquivo é descomprimido corretamente.
Use o "b" tanto para abrir o arquivo de entrada ("rb") como o de saída ("wb").
Quanto ao fato do arquivo comprimido ficar menor quando você abre o arquivo de entrada sem o "b", isso me parece um erro! Você não contou como está abrindo o arquivo de saída, se está usando "wb" ou só "w"... Suponho que você tenha comparado os tamanhos dos arquivos de saída nestes dois casos:
(1) abrindo o arquivo de entrada com "rb" e o de saída com "wb";
(2) abrindo o arquivo de entrada só com "r" e o de saída com "wb".
Se foram esses os casos que você comparou, então faz sentido que o arquivo de saída fique menor no caso (2). Você está rodando no Windows, que usa o par CR LF como terminador de linhas. Como o arquivo de entrada foi aberto sem o "b", cada par CR LF será convertido, na leitura, para um único caractere '\n', que é o LF. Como o arquivo de saída foi aberto com o "b", não será feita a conversão inversa quando o programa escrever caracteres '\n'. O resultado é que cada par CR LF presente no arquivo de entrada será escrito no arquivo de saída como um caractere LF. É por isso que o arquivo comprimido gerado no caso (2) tenderá a ser menor que o gerado no caso (1). O problema é que isso está errado, pois estraga a compressão de arquivos que não são de texto. Suponha que um arquivo executável contém, por acaso, dois bytes consecutivos cujos valores são os códigos dos caracteres CR e LF. No caso (2), o primeiro desse bytes vai desaparecer!
Quanto ao fato do arquivo comprimido ficar menor quando você abre o arquivo de entrada sem o "b", isso me parece um erro! Você não contou como está abrindo o arquivo de saída, se está usando "wb" ou só "w"... Suponho que você tenha comparado os tamanhos dos arquivos de saída nestes dois casos:
(1) abrindo o arquivo de entrada com "rb" e o de saída com "wb";
(2) abrindo o arquivo de entrada só com "r" e o de saída com "wb".
Se foram esses os casos que você comparou, então faz sentido que o arquivo de saída fique menor no caso (2). Você está rodando no Windows, que usa o par CR LF como terminador de linhas. Como o arquivo de entrada foi aberto sem o "b", cada par CR LF será convertido, na leitura, para um único caractere '\n', que é o LF. Como o arquivo de saída foi aberto com o "b", não será feita a conversão inversa quando o programa escrever caracteres '\n'. O resultado é que cada par CR LF presente no arquivo de entrada será escrito no arquivo de saída como um caractere LF. É por isso que o arquivo comprimido gerado no caso (2) tenderá a ser menor que o gerado no caso (1). O problema é que isso está errado, pois estraga a compressão de arquivos que não são de texto. Suponha que um arquivo executável contém, por acaso, dois bytes consecutivos cujos valores são os códigos dos caracteres CR e LF. No caso (2), o primeiro desse bytes vai desaparecer!
Obrigado!
Arrumei meu ep.. agora aparentemente funciona para qualquer tipo de arquivo.
Arrumei meu ep.. agora aparentemente funciona para qualquer tipo de arquivo.
Descobri um erro novo!!!
No Devc++ (Windows), variáveis char gravam números até 255, mas aqui no Linux só vai até 127.
Alguém poderia me explicar por que isso ocorre, por favor??
Obrigado!!
No Devc++ (Windows), variáveis char gravam números até 255, mas aqui no Linux só vai até 127.
Alguém poderia me explicar por que isso ocorre, por favor??
Obrigado!!

Isso ocorre pois o char soh guarda 255 numeros (de -127 a 127). Ntão eh soh usar o unsigned char que guarda valores de 0 a 255.
errrr.... é verdade, esqueci de observar este detalhe!!! hehe tb, tanta coisa pra conferir...
vlw!!!
vlw!!!

Um unsigned char guarda inteiros de 0 a 255. Um signed char guarda inteiros de -128 a 127. O tipo char é sinônimo de unsigned char ou de signed char, dependendo do ambiente (compilador e computador) no qual o programa for executado. Em outras palavras, num compilador/computador o char pode guardar inteiros de 0 a 255 e em outro ele pode guardar inteiros de -128 a 127.
Para ter certeza que a sua variável guarda números no intervalo que você precisa, declare-a como
unsigned char c;
ou como
signed char c;
Para ter certeza que a sua variável guarda números no intervalo que você precisa, declare-a como
unsigned char c;
ou como
signed char c;