Hex file reading in C, some data corrupted? - c

Iam having a problem with my school project. When i try to read in C program some hex data from a binary file i get some data filled with F's like this
00 64 61 56 03 00 00 00 09 00 00 00 73 00 00 00
6A 69 75 FFFFFFE8 FFFFFFD1 5B FFFFFF8C FFFFFF93 73 FFFFFFAF 19 FFFFFF87 FFFFFFC1 4D FFFFFFFD FFFFFF93
FFFFFFDF FFFFFFBE FFFFFFA0 09 FFFFFFBE FFFFFFD4 FFFFFFEB FFFFFFDE FFFFFFD3 FFFFFFF9 FFFFFFBD FFFFFFE6 FFFFFFFA FFFFFFAA 7E 29
FFFFFFD3 FFFFFFE2 13 FFFFFFA5 3F FFFFFFA0 3A FFFFFFB2 50 53 3B 12 FFFFFFA1 39 FFFFFFA6 FFFFFF82
FFFFFFF7
While the original file in hexdump looks like this:
0000000 6400 5661 0003 0000 0009 0000 0073 0000
0000010 696a e875 5bd1 938c af73 8719 4dc1 93fd
0000020 bedf 09a0 d4be deeb f9d3 e6bd aafa 297e
0000030 e2d3 a513 a03f b23a 5350 123b 39a1 82a6
0000040 00f7
Iam not quiet sure, why is this happening.
I tested it using this code:
int main()
{
char ch, file_name[25];
FILE *fp;
printf("Enter the name of file you wish to see\n");
fgets(file_name, sizeof(file_name), stdin);
file_name[strlen(file_name)-1] = 0x00;
fp = fopen(file_name,"rb"); // read binary mode
if( fp == NULL ) //error checking
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
printf("The contents of %s file are :\n", file_name);
int i;
while( ( ch = fgetc(fp) ) != EOF )
{
printf("%02X ",ch);
if( !(++i % 16) ) putc('\n', stdout);
}
fclose(fp);
putc('\n', stdout);
return 0;
}

You just tripped on the sign extension done by the processor when expanding a signed integer value to a larger integer type:
The type char is only one byte wide, while the printf() function expects int arguments (four bytes on your system). Since char is signed on your system, the other 24 bits are filled with copies of the sign bit, producing the FFFFFF pattern that you see in your output. Note that your output is correct, except for the FFFFFF-prefixes to all bytes that start with a hex digit greater or equal to 8.
You can avoid this by simply declaring ch as unsigned char. That will be zero extended to an int, and your output will be correct.

If you make ch an unsigned character you won't see the sign extension (the FFs).

It may not be a good idea to open a file as a binary file, and trying reading it by fgetc(), which deals with text files. Instead, you can use fread() to read binary files.
Here is my solution:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
int main()
{
char file_name[25];
puts("Enter the name of file you wish to see");
fgets(file_name, sizeof (file_name), stdin);
file_name[strlen(file_name) - 1] = '\0';
FILE *fp;
fp = fopen(file_name, "rb"); // read binary mode
if(!fp) //error checking
{
perror("fopen()");
exit(EXIT_FAILURE);
}
fseek (fp, 0, SEEK_END); // non-portable
long fSize = ftell(fp);
rewind(fp);
printf("The contents of %s file are :\n", file_name);
uint8_t *ch = malloc(fSize); // assuming CHAR_BIT == 8
fread(ch, 1, fSize, fp);
int i;
for(i = 0; i < fSize; i++)
{
printf("%.2x ", ch[i]);
if(!((i + 1) % 16))
putchar('\n');
}
fclose(fp);
putchar('\n');
return EXIT_SUCCESS;
}
This code has been tested on my machine, and its output is identical to that of hex dump. Feel free to ask me about the details.

make ch an unsigned char as it is treating the binary value as signed and the MSB as sign bit.

Related

Write PPM file header

I am trying to write a PPM file and am having trouble with the header specifically.
I have a function taking in a file pointer and header struct containing my header information.
void writeHeader(FILE* file, struct Header* header){
char space[] = " ";
char newLine[] = "\n";
fwrite(&header->magicNum[0], sizeof(char), 1, file);
fwrite(&header->magicNum[1], sizeof(char), 1, file);
fwrite(newLine, sizeof(char), 1, file);
fwrite(&(header->width), sizeof(int), 1, file);
fwrite(space, sizeof(char), 1, file);
fwrite(&header->height, sizeof(int), 1, file);
fwrite(newLine, sizeof(char), 1, file);
fwrite(&header->maxColor, sizeof(int), 1, file);
fwrite(newLine, sizeof(char), 1, file);
fwrite(space, sizeof(char), 1, file);
}
When I call this I end up only writing the first two values "P6" to file and then blanks after (or dots in hex editor). I have also tried removing the space and newline characters but I still don't get anything after P6. How can I write the rest of my header to file along with the necessary space and newline characters where needed?
As the PPM header is stored in ascii strings, I would say something like:
#include <stdio.h>
#include <string.h>
#define OUTFILE "file.ppm"
struct Header {
char magicNum[3];
int width;
int height;
int maxColor;
};
void writeHeader(FILE* file, struct Header* header)
{
fprintf(file, "%s\n", header->magicNum);
fprintf(file, "%d %d\n", header->width, header->height);
fprintf(file, "%d\n", header->maxColor);
}
int main()
{
struct Header header;
FILE *fp;
strcpy(header.magicNum, "P6");
header.width = 1920;
header.height = 1080;
header.maxColor = 255;
fp = fopen(OUTFILE, "w");
if (fp == NULL) {
perror(OUTFILE);
exit(1);
}
writeHeader(fp, &header);
// write the ppm data to the file
return 0;
}
If you look at the PPM header example here on Wikipedia, you will see that the width and height numbers are stored as ASCII text in the file (similar to how you correctly stored the number 6 in "P6" as text).
But in the code that follows, you appear to be writing actual integers (for width and height, etc) from memory to the file, which depends on your system's architecture and is not the same as writing the ASCII characters that represent the integers. Here I have annotated a hexdump from my system (with an "image" of width 5, height 4, maxColor 3), where my integers are stored across 4 bytes with the least-significant byte first:
(ch = character; sp = space)
ch ch ch --integer-- ch --integer-- ch --intege
P 6 \n |width:5--| sp |height:4-| \n |maxCol:
00000000 50 36 0a 05 00 00 00 20 04 00 00 00 0a 03 00 00 |P6..... ........|
00000010 00 0a 20 |.. |
3| \n sp
r- ch ch
So programmatically your code seems to work, but I don't think it is achieving what you intend.
This is probably what you want to end up with:
ch ch ch ch ch ch ch ch ch ch
P 6 \n 5 sp 4 \n 3 \n sp
00000000 50 36 0a 35 20 34 0a 33 0a 20 |P6.5 4.3. |

Read PNG file with c

I want to read a PNG image file with C without any library. From PNG (Portable Network Graphics) Specification Version 1.0 any PNG file has a signature that distinguishes it from other image formats. The signature is the first 8 bytes of the image.
Some sources like the above RFC mentioned the signature as:
137 80 78 71 13 10 26 10 (decimal)
Or like Not able to read IHDR chunk of a PNG file mentioned the signature as:
89 50 4E 47 0D 0A 1A 0A (ASCii)
So, I write a simple code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE (8)
int main(int argc, char **argv){
if(argc != 2) {
printf("Usage: %s <png file>\n", argv[0]);
return 1;
}
char *buf = (char *)malloc(MAX_SIZE);
if(!buf) {
fprintf(stderr, "Couldn't allocate memory\n");
return 1;
}
FILE *f = fopen(argv[1], "r");
if(!f) {
perror("fopen");
printf("Invalid file\n");
free(buf);
return 1;
}
int size = fread(buf, 1, MAX_SIZE, f);
printf(%c\n", buf[1]);
printf(%c\n", buf[2]);
printf(%c\n", buf[3]);
printf(%c\n", buf[4]);
printf(%c\n", buf[5]);
printf(%c\n", buf[6]);
printf(%c\n", buf[7]);
printf(%c\n", buf[8]);
fclose(f);
free(buf);
system("pause");
return 0;
}
When I print the bytes by printf, the output is not like the above.
This is what it shows:
ëPNG→►v#,
Can someone describe what happened and what can I do to modify it?
You need to print each value with the correct format specifier. Here we want numerical representations, not character ones.
From the documentation on printf:
%c writes a single character
%d converts a signed integer into decimal representation
%x converts an unsigned integer into hexadecimal representation
%02X prints a hexadecimal with a minimum width of two characters, padding with leading zeroes, using ABCDEF (instead of abcdef).
See also implicit conversions.
An example:
#include <stdio.h>
#define SIZE 8
int main(int argc, char **argv) {
unsigned char magic[SIZE];
FILE *file = fopen(argv[1], "rb");
if (!file || fread(magic, 1, SIZE, file) != SIZE) {
fprintf(stderr, "Failure to read file magic.\n");
return 1;
}
/* Decimal */
for (size_t i = 0; i < SIZE; i++)
printf("%d ", magic[i]);
printf("\n");
/* Hexadecimal */
for (size_t i = 0; i < SIZE; i++)
printf("%02X ", magic[i]);
printf("\n");
fclose(file);
}
Output:
137 80 78 71 13 10 26 10
89 50 4E 47 0D 0A 1A 0A

Problem when reading a binary file in C. The reading stops at the beginning when a binary value of 00 is found

When I open and read a text file it works. However, when I open and try to read a binary file it stops at the first byte equal to 00.
For example, I try to read a png file, with the below first line in hexdump:
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR|
But, I end up with a string that includes only these:
00000000 89 50 4e 47 0d 0a 1a 0a 0a |.PNG.....|
This is my code.
int main(int argc, char *argv[]) {
int num = 5;
char *filename;
char *myfile;
FILE *fp;
filename = argv[1];
if((fp = fopen(filename,"rb")) == NULL) {
printf("error open file\n");
exit(1);
}
myfile = (char *) malloc(num*sizeof(char));
if(myfile==NULL) {
printf("error memory\n");
exit(1);
}
int i=0;
while(!feof(fp)) {
fread(myfile+i,sizeof(char),1,fp);
if(i==num) {
myfile = (char *) realloc(myfile,sizeof(char)*(num*2));
if(myfile==NULL) {
printf("error memory\n");
exit(1);
}
num = num*2;
}
i++;
}
fclose(fp);
printf("%s\n",myfile);
return(0);
}
You're reading the entire file correctly. Instead, the problem lies here:
printf("%s\n",myfile);
printf doesn't know when your myfile string ends, and so assumes it's a cstring, which is terminated by the first null character.
You'd want to use fwrite instead:
fwrite(myfile, num, 1, stdout);
This isn't a complete working alternative, though -- from what I see in your code, num is likely to be higher unless your file's size is an exact power of two. You'd want to rewrite your program so that you know the exact size of the data you've read, and then replace num with that in fwrite.

RLE algorithm should compress bytes in c

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.
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 |. .!..|
As pointed in comments, you have two problems:
Usage of printf instead of sprintf,
Writting to file what you've counted.
Name Creation
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");
}
Writting to file
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:
make a function that will only write two characters in file: the number of character found and the character itself
call this function each time needed (at character changing, but not when one of character is EOF...)
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:
It doesn't handle char overflow (what if count is over 256?),
It doesn't test the return of 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:
Character changing when previous is EOF must be ignored (else we would write something like 0 (char)EOF at file starting),
One writting must be added after 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;
}

Reading a file 16 bytes at a time in C

I am trying to read a file parsed from a command line argument 16 bytes at a time. I am storing the bytes in an unsigned char array. I am then trying to print the elements in their HEX format and then, if they are printable characters, I am printing them and if not I am printing a dot "." I also would like to print the offset of the byte from the beggining of the file on each new line but I want to get the rest working before I start working on that. The problem I am having is the file I am reading from is not printing so I don't think I am doing it right. I started out using fread() but I think I may be needing to use fseek() but I am unsure. If anyone could point me in the right direction or tell me if I'm doing anything wrong I'd appreciate it.
Code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
int i, j;
unsigned char buffer[17];
fp = fopen(argv[1], "r");
while(!feof(fp))
{
while(fread(buffer, 16, 1, fp) !=0)
{
for(i=0; i < 16; i++)
{
printf("%X ", buffer[i]);
}
for(j = 0; j < 16; j++)
{
if(isprint(buffer[j]))
printf("%s", buffer[j]);
else
printf("." );
}
printf("\n");
}
}
fclose(fp);
return 0;
}
Expected output:
0001: 40 4F 28 37 0B B8 FF 9D 81 5E 2E 73 B5 CC 6A 70 #O(7.....^.s..jp
0010: 0F 30 9C 6E 7E F7 53 E0 C1 5E 9A 38 C5 02 F2 33 .0.n .S..^.8...3
EDIT: Fixed problem with suggestions. Was opening file in text mode instead of binary. Changed read mode to "rb" and code is working correctly now.
You need to open the file in binary mode, using fseek/ftell on a file in text mode gives erratic values.
So open the file with
fp = fopen(argv[1], "rb");
also good practice is to always check if a function succeeded by checking the return value, in this case if (fp != NULL) ...
Instead of reading 16 byte chunks read 1 byte 16 times, otherwise if the file length is not evenly dividable by 16 you miss the last bytes
if ( fp != NULL )
{
int read = 0;
while((read = fread(buffer, 1, 16, fp)) > 0)
{
for(i=0; i < read; i++)
{
...
}
fclose(fp);
}
IOW no need to for the outer while (feof(fp)) ...
in
printf("%s", buffer[j]);
you are not using the correct format specifier, buffer[j] is a character, not a string so write
printf("%c", buffer[j]);
EDIT: if you are not in some embedded environment with minimal stack reading 16 bytes at a time is not so effective, you can just as might read 2k or some bigger size to read faster.

Resources