Reputation: 169
I know there are too many questions about this algorithm but I couldn't really find a good answer for compressing the bytes. I am kind of a newbie in C. I have the following code:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
//compress function here...
int main(int argc, char **argv) {
if(argc != 2){
fprintf(stderr, "Wrong argument number\n");
exit(1);
}
FILE *source = fopen(argv[1], "rb");
if(source == NULL){
fprintf(stderr, "Cannot open the file to be read\n");
exit(1);
}
FILE *destination;
char name = printf("%s.rle", argv[1]);
while((destination = fopen(&name, "wb")) == NULL){
fprintf(stderr, "Can't create the file to be written\n");
exit(1);
}
compress_file(source, destination);
int error;
error = fclose(source);
if(error != 0){
fprintf(stderr, "Error: fclose failed for source file\n");
}
error = fclose(destination);
if(error != 0){
fprintf(stderr, "Error: fclose failed for destination file\n");
}
}
If this is test.c and the executable is test. I need to make this work on terminal/command prompt as "./test file.txt". My file.txt includes something like (bytes):
20 21 20 20 8F 8F 21 21 64 60 70 20 21 90 90
and the desired output is:
01 20 01 21 02 20 02 8F 02 21 01 64 01 60 01 70 01 20 01 21 02 90
My code create a file and that file includes:
0b00 0000 0106 0000 0000 0000 0000 0000 0000 0000 0a
instead what I want. What do I miss?
Also I want my file to be named as file.txt.rle but it has no name.
EDIT:
char name[30];
sprintf(name, "%s.rle", argv[1]);
solved the problem for naming.
Upvotes: 1
Views: 759
Reputation: 44329
Also I want my file to be named as file.txt.rle but it has no name.
Well, this code
char name = printf("%s.rle", argv[1]);
while((destination = fopen(&name, "wb")) == NULL){
doesn't give you a string like "file.txt.rle". Instead try something like:
size_t len = strlen(argv[1]) + 4 + 1;
char name[len];
sprintf(name, "%s.rle", argv[1]);
while((destination = fopen(name, "wb")) == NULL){
instead what I want. What do I miss?
Well, you miss that you need to put data into str
This code
char str[BUF_SIZE];
fwrite(str, sizeof(str), 1, destination);
just writes an uninitialized variable to the file.
I'll not give you a complete solution but here is something that you can start with and then figure the rest out yourself.
void compress_file(FILE *source, FILE *destination){
char str[BUF_SIZE];
int index = 0;
int repeat_count = 0;
int previous_character = EOF;
int current_character;
while((current_character = fgetc(source)) != EOF){
if(current_character != previous_character) {
if (previous_character != EOF) {
// Save the values to str
str[index++] = repeat_count;
str[index++] = previous_character;
}
previous_character = current_character;
repeat_count = 1;
}
else{
repeat_count++;
}
}
if (repeat_count != 0)
{
str[index++] = repeat_count;
str[index++] = previous_character;
}
fwrite(str, index, 1, destination);
}
EXAMPLE 1:
Let's say a file.txt is:
ABBCCC
On linux it can be displayed hexadecimal like this:
# hexdump -C file.txt
00000000 41 42 42 43 43 43 |ABBCCC|
After running the program, you have:
hexdump -C file.txt.rle
00000000 01 41 02 42 03 43 |.A.B.C|
EXAMPLE 2:
Let's say that file.txt is like
# hexdump -C file.txt
00000000 20 21 20 20 8f 8f 21 21 64 60 70 20 21 90 90 | ! ..!!d`p !..|
the result will be
# hexdump -C file.txt.rle
00000000 01 20 01 21 02 20 02 8f 02 21 01 64 01 60 01 70 |. .!. ...!.d.`.p|
00000010 01 20 01 21 02 90 |. .!..|
Upvotes: 2
Reputation: 9649
As pointed in comments, you have two problems:
printf
instead of sprintf
,char name = printf("%s.rle", argv[1]);
destination = fopen(&name, "wb");
The first line will store the number of characters in argv[1]
plus 4 into name
. Since, from man printf
:
Upon successful return, these functions return the number of characters printed (excluding the null byte used to end output to strings).
The second line is more problematic : you ask fopen
to open a file giving it a pointer to char instead of a read string.
One correct way to do what you want is:
/* reserve memory to store file name
NOTE: 256 here might not large enough*/
char name[256];
/* fill name array with original name + '.rle'
The return of sprintf is tested to assert that its size was enough */
if (snprintf(name, sizeof name, "%s.rle", argv[1]) >= sizeof name)
{
fprintf(stderr, "name variable is not big enough to store destination filename");
}
The code
char str[BUF_SIZE];
fwrite(str, sizeof(str), 1, destination);
reserve a big array, and writes it to file, without initializing it. To do what you want, you can have this approach:
Let's look at :
void write_char_to_file(FILE *f, int count, char car)
{
/* char array to be stored in file */
char str[2];
/* number of repeating characters */
str[0] = count;
/* the character */
str[1] = car;
/* write it to file */
fwrite(str, sizeof str, 1, f);
}
This function has two potential problems:
char
overflow (what if count
is over 256?),fwrite
.Then, when this function should be called, when the current character changes:
EOF A A B C C EOF
In this example, we have 4 characters changes, but we want only 3 writting in the file, so:
0 (char)EOF
at file starting),while
loop since, when the last reading gives EOF
, we still have 2 C
to write to file.Let's look at the code:
while((current_character = fgetc(source)) != EOF) {
if(current_character != previous_character) {
/* ignore initial change */
if (previous_character != EOF) {
write_char_to_file(destination, repeat_count, previous_character);
}
previous_character = current_character;
repeat_count = 1;
} else {
repeat_count++;
}
}
/* write last change */
write_char_to_file(destination, repeat_count, previous_character);
This code have a problem too: what if the input file is empty? (first read gives EOF
)
The complete code:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#define BUF_SIZE 5096
void write_char_to_file(FILE *f, int count, char car)
{
/* char array to be stored in file */
char str[2];
/* number of repeating characters */
str[0] = count;
/* the character */
str[1] = car;
/* write it to file */
fwrite(str, sizeof str, 1, f);
}
void compress_file(FILE *source, FILE *destination)
{
int repeat_count = 0;
int previous_character = EOF;
int current_character;
while((current_character = fgetc(source)) != EOF) {
if(current_character != previous_character) {
if (previous_character != EOF) {
write_char_to_file(destination, repeat_count, previous_character);
}
previous_character = current_character;
repeat_count = 1;
} else {
repeat_count++;
}
}
write_char_to_file(destination, repeat_count, previous_character);
}
int main(int argc, char **argv) {
if(argc != 2) {
fprintf(stderr, "Wrong argument number\n");
exit(1);
}
FILE *source = fopen(argv[1], "rb");
if(source == NULL) {
fprintf(stderr, "Cannot open the file to be read\n");
exit(1);
}
FILE *destination;
/* reserve memory to store file name
NOTE: 256 here might not large enough*/
char name[256];
/* fill name array with original name + '.rle'
The return of sprintf is tested to assert that its size was enough */
if (snprintf(name, sizeof name, "%s.rle", argv[1]) >= sizeof name)
{
fprintf(stderr, "name variable is not big enough to store destination filename");
}
/* while is not needed here, if do the job */
if((destination = fopen(name, "wb")) == NULL) {
fprintf(stderr, "Can't create the file to be written\n");
exit(1);
}
compress_file(source, destination);
int error;
error = fclose(source);
if(error != 0) {
fprintf(stderr, "Error: fclose failed for source file\n");
}
error = fclose(destination);
if(error != 0) {
fprintf(stderr, "Error: fclose failed for destination file\n");
}
/* main must return a integer */
return 0;
}
Upvotes: 1