Tipo do arquivo gravado

Tipo do arquivo gravado

por Alexandre Ouno Atoji -
Número de respostas: 21
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.
Em resposta à Alexandre Ouno Atoji

Re: Tipo do arquivo gravado

por Francisco Reverbel -
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);
    }

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.
Em resposta à Francisco Reverbel

Re: Tipo do arquivo gravado

por Alexandre Ouno Atoji -
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?
Em resposta à Alexandre Ouno Atoji

Re: Tipo do arquivo gravado

por Rodrigo Luiz Marques Flores -
Sim. Usar o formato  (x,y) custa 6 caracteres ( "(","x",",","y",")"," "). Enquanto no outro formato %c%c custa apenas 3 ("x","y"," ").
Em resposta à Rodrigo Luiz Marques Flores

Re: Tipo do arquivo gravado

por Francisco Reverbel -
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.

Em resposta à Rodrigo Luiz Marques Flores

Re: Tipo do arquivo gravado

por Francisco Reverbel -
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.
Em resposta à Francisco Reverbel

Re: Tipo do arquivo gravado

por Caio Braz -
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 ?
Em resposta à Caio Braz

Re: Tipo do arquivo gravado

por Alexandre Ouno Atoji -
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 =)
Em resposta à Alexandre Ouno Atoji

Re: Tipo do arquivo gravado

por Francisco Reverbel -
Pode fazer sim. A idéia é gravar números no intervalo [0,255] como chars.

Em resposta à Francisco Reverbel

Re: Tipo do arquivo gravado

por Rodrigo Cordeiro Godoy -

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

Em resposta à Rodrigo Cordeiro Godoy

Re: Tipo do arquivo gravado

por Francisco Reverbel -
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.
Em resposta à Caio Braz

Re: Tipo do arquivo gravado

por Francisco Reverbel -
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.
Em resposta à Francisco Reverbel

Re: Tipo do arquivo gravado

por José David Fernández Curado -
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?
Em resposta à José David Fernández Curado

Re: Tipo do arquivo gravado

por Francisco Reverbel -
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.

Em resposta à Alexandre Ouno Atoji

Re: Tipo do arquivo gravado

por Andrew Kurauchi -
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.
Em resposta à Andrew Kurauchi

Re: Tipo do arquivo gravado

por Francisco Reverbel -
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!
Em resposta à Francisco Reverbel

Re: Tipo do arquivo gravado

por Andrew Kurauchi -
Obrigado!
Arrumei meu ep.. agora aparentemente funciona para qualquer tipo de arquivo.
Em resposta à Alexandre Ouno Atoji

Re: Tipo do arquivo gravado

por Fillipe Resina -
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!! smile
Em resposta à Fillipe Resina

Re: Tipo do arquivo gravado

por Rafael Obara -
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.
Em resposta à Rafael Obara

Re: Tipo do arquivo gravado

por Fillipe Resina -
errrr.... é verdade, esqueci de observar este detalhe!!! hehe tb, tanta coisa pra conferir...
vlw!!! wink
Em resposta à Rafael Obara

Re: Tipo do arquivo gravado

por Francisco Reverbel -
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;