I've been working on a small program to dump the hex values of a file, similar to od or hexdump, and I've ran into an issue when printing the values. For certain hex values, mostly ones with alphabetical characters as their second digit, it prints with 6 extra f digits before printing the actual number, instead of just printing a 2-width value like I had specified. I have confirmed that the values themselves are not unexpected in any way, it's the printing that's messing it up.
Code:
int main(int argc, char* argv[]) {
FILE* dataFile = fopen(argv[1], "rb");
int byteCount = 0;
char currentByte = fgetc(dataFile);
while (currentByte != EOF) {
printf("%08d", byteCount);
do {
//print as hex
printf(" %02x ", currentByte);
//set up for next loop
currentByte = fgetc(dataFile);
byteCount++;
} while (currentByte != EOF && (byteCount) % 16 != 0);
printf("\n");
}
printf("%08d\n", byteCount);
}
Output:
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
00000016 03 00 3e 00 01 00 00 00 10 6b 00 00 00 00 00 00
00000032 40 00 00 00 00 00 00 00 08 23 02 00 00 00 00 00
00000048 00 00 00 00 40 00 38 00 0d 00 40 00 1f 00 1e 00
00000064 06 00 00 00 04 00 00 00 40 00 00 00 00 00 00 00
00000080 40 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
00000096 ffffffd8 02 00 00 00 00 00 00 ffffffd8 02 00 00 00 00 00 00
00000112 08 00 00 00 00 00 00 00 03 00 00 00 04 00 00 00
00000128 18 03 00 00 00 00 00 00 18 03 00 00 00 00 00 00
00000144 18 03 00 00 00 00 00 00 1c 00 00 00 00 00 00 00
00000160 1c 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
00000176 01 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00
00000192 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000208 fffffff8 34 00 00 00 00 00 00 fffffff8 34 00 00 00 00 00 00
Does anyone know what's going on here?
There are several problems here.
char is entirely unsuitable to hold raw binary data, because it has implementation-defined signedness and it often a signed type, see Is char signed or unsigned by default?. In fact char should only ever be used for text strings. Instead, you would normally use uint8_t. However... :
The EOF constant is of type int, so if you intend to compare something against EOF, it must be type int. fgetc is guaranteed to return characters as if they were unsigned char but converted to int - as it happens, int is large enough to hold all values of an unsigned char so int can be used here.
%x specifier for printf expects an unsigned int.
Fixes:
char currentByte -> int currentByte
printf(" %02x ", currentByte); -> printf(" %02x ", (unsigned int)currentByte);.
You must always check if fopen was successful and also fclose the file pointer when done. Also note that calling fopen directly on argv with no input sanitation is not acceptable in a professional program. You don't even know if argv[1] exists - the program needs a whole lot more error handling overall.
I have an assignment that I need to create a program to read (in binary mode) a file and then display them out on the screen. I run it in the command line mode. Here is my source code:
#include <stdio.h>
#include <stdlib.h>
#define MAXWORD 130
typedef struct Node node;
struct Node
{
int stt;
char name[50];
char phonenumber[20];
char email[50];
};
int checktoread(FILE *datain)
{
return (datain == NULL);
}
int readfile(FILE *datain, node *item)
{
int i = 0;
while(fread(&item[i], sizeof(node), 1, datain)>0)
{
i++;
}
return i;
}
void display(node *item, int max)
{
int i = 0;
for(; i < max; i++)
{
printf("%-3d%-30s%-20s%-30s\n", item[i].stt, item[i].name, item[i].phonenumber, item[i].email);
}
}
int main(int argv, char *argc[])
{
if (argv != 2)
{
printf("Wrong syntax\nCorrect Syntax: readfile <source file>\n");
exit(1);
}
FILE *fp = fopen(argc[1], "rb");
if(checktoread(fp))
{
printf("The source file is unable to be opened\n");
exit(1);
}
node *person = (node*) malloc(30*sizeof(person));
int maxline = readfile(fp, person);
printf("%d\n", maxline);
display(person, maxline);
fclose(fp);
free(person);
return 0;
}
My problem is this file do not return or display anything. There is no syntax error here, I confirm. I don't know where I get wrong. Actually, I think it get wrong in the step that convert data from a file into an array.
The file starts like this
0 1 2 3 4 5 6 7 8 9 A B C D E F
0000:0000 01 00 00 00 4A 6F 65 79 4D 61 74 68 65 6C 00 00
0000:0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:0060 00 00 00 00 00 00 00 00 30 33 38 35 37 38 38 30
0000:0070 36 30 00 00 00 00 00 00 00 00 00 00 6A 6F 65 79
0000:0080 6D 61 74 68 65 6C 40 67 6D 61 69 6C 2E 63 6F 6D
0000:0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:00A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:00B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:00C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:00D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000:00E0 02 00 00 00 53 63 6F 74 74 79 4A 61 63 6B 00 00
0000:00F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
I am current writing a file system, and init the disk. I am trying to write into the disk block.
int main(){
char name[] = "disk";
create_disk(name); // fd already define in global var;
open_disk(name);
write(fd,"one",16);
write(fd,"two",16);
write(fd,"three",16);
close(fd);
}
Here is the outcome in the file
IN HEX
Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000: 6F 6E 65 00 74 77 6F 00 74 68 72 65 65 00 00 00 one.two.three...
00000010: 74 77 6F 00 74 68 72 65 65 00 00 00 00 14 00 00 two.three.......
00000020: 74 68 72 65 65 00 00 00 00 14 00 00 00 00 00 00 three...........
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
My question: I only write "one", "two", "three" once each, why it giving me multiple copies of "three", "two".
How can I make my outcome looks like this?
one.............
two.............
three...........
................
................
You're writing 16 bytes from a much shorter string (i.e. reading memory you should not be). That's undefinable behavior.
Beware the nasal demons.
What you really want is something like this:
char buffer[16] = { 0 };
strcat(buffer, "one");
write(fd,buffer,16);
Now you are writing 16 chars out of a 16 char buffer which is perfectly ok.
I am trying to read the contents of a binary file into a struct, but each time I print out the contents of my struct, I get a pretty strange output. Below is the struct I am trying to set:
struct student {
char name[32];
unsigned int age;
SEX sex;
float gpa;
struct course *courses;
struct student *next;
}
With the course struct defined as:
struct course {
char grade;
unsigned int number;
struct course *next;
}
And SEX defined as:
typedef enum _SEX {MALE = 'M', FEMALE = 'F', OTHER = 'O'} SEX;
Right now, my function is as follows:
void read_bin(char *filename){
FILE *file;
struct student myStudent;
file = fopen(filename, "rb");
if(file == NULL){
printf("Unable to open file!");
return;
}
fread(&myStudent, sizeof(struct student), 1, file);
printf("\nName: %s, Age: %d", myStudent.name, myStudent.age);
fclose(file);
}
But I keep on getting this strange output:
Age: 0
With name not even showing up and age set to an incorrect number. I thought this might be due to padding, so I tried using
fread(&myStudent.name, sizeof myStudent.name, 1, file);
fread(&myStudent.age, sizeof myStudent.age, 1, file);
So that it reads each individual element and pads as needed. However, I get the same output. When I ran hexdump to see what exactly I was dealing with, this is what came out:
f0 0d 03 00 00 00 46 72 65 64 00 00 00 00 00 00 |......Fred......|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00 00 00 00 00 00 1d 00 00 00 4d 00 00 00 cd cc |..........M.....|
4c 40 01 00 00 00 44 02 00 00 41 4a 6f 65 00 00 |L#....D...AJoe..|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 4d |...............M|
00 00 00 33 33 73 40 03 00 00 00 6e 00 00 00 42 |...33s#....n...B|
dc 00 00 00 41 54 01 00 00 41 53 61 72 61 68 00 |....AT...ASarah.|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00 00 00 00 00 00 00 00 00 00 16 00 00 00 46 00 |..............F.|
00 00 00 00 40 40 03 00 00 00 78 00 00 00 42 dc |....##....x...B.|
00 00 00 41 4a 01 00 00 43 |...AJ...C|
Any help would be greatly appreciated -- I've been stuck on this function for awhile.
Depending on the filetype the first few bytes can be a fileheader, here information can be stored like version numbers, length of the data etc.
As for the pointers, that won't work like others pointed out since pointers are adresses, not actual relevant data.
You want to fseek to sizeof(fileheader), before you start reading the file.
Also be wary that structs are padded in C and that the order in which you declared the elements in the struct might not be the order the compiler decided for it.
I have tried to look this up in multiple places and I cannot understand why fwrite doesn't work.
If I had a structure with 100 fields I would not want to use fprintf with 100 format specifiers.
struct emp
{
char name[15];
int age;
int salary;
char address[30];
};
int main()
{
char str[60];
struct emp emp1[5] = {{"Yoda",23,45000,"Asia"},{"Darth",34,2344,"NAmerica"},{"Jabba",22,5566,"Africa"},{"Luke",33,3399,"SAmerica"},{"Laya",44,6677,"Europe"}};
FILE *fp;
fp = fopen("C:/.../sampleText.txt","w");`
int i=0;
for(i=0; i<5; i++)
{
fwrite(&emp1[i],sizeof(emp1[i]),1,fp);
//fprintf(fp,"%s, %d, %d, %s\n",&emp1[i].name,emp1[i].age,emp1[i].salary,emp1[i].address);
}
fclose(fp);
getch();
}
There are two answers:
It does work, if everything is set correctly and porting the written data to other machines is not an issue.
It doesn't work if you have any of a large number of common features in data structures, or if you need to move the data from one type of machine (say an Intel machine) to another type (say PowerPC or SPARC).
In your example structure, you have no pointers, so you could write the structure verbatim to a file, and then in another invocation of the program running on the same (type of) machine, you could read it back in, and you would see the same data.
However, if your structure contained pointers, you could not meaningfully write the structure to disk. The pointers in one invocation of the program need not have any significance in another invocation of the program. If you needed to port the data between a little-endian (Intel) and big-endian (PowerPC, SPARC) machine, you'd have to use a platform-neutral way of accessing the data; simply writing the data to disk would not work.
So, where portability is not an issue, this code should work — Unix or Windows. It uses the "wb" and "rb" arguments to fopen() because the data is binary data, not plain text. The b is optional but harmless on Unix; it is crucial on Windows. The code also fixes the file name to sampledata.bin so it can be run on either platform, writing in the current directory. It writes the data; it then reads the data; it then compares the read data with the written data, reporting any problems. If the program says nothing, all is OK.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct emp
{
char name[15];
int age;
int salary;
char address[30];
};
int main(void)
{
char const filename[] = "sampledata.bin";
struct emp emp1[5] =
{
{ "Yoda", 23, 45000, "Asia" },
{ "Darth", 34, 2344, "N America" },
{ "Jabba", 22, 5566, "Africa" },
{ "Luke", 33, 3399, "S America" },
{ "Leia", 44, 6677, "Europe" },
};
struct emp emp2[5];
FILE *ifp;
FILE *ofp;
int i;
ofp = fopen(filename, "wb");
if (ofp != 0)
{
if (fwrite(emp1, sizeof(emp1), 1, ofp) != 1)
{
fprintf(stderr, "Failed to write to %s\n", filename);
exit(1);
}
fclose(ofp);
}
ifp = fopen(filename, "rb");
if (ifp != 0)
{
if (fread(emp2, sizeof(emp2), 1, ifp) != 1)
{
fprintf(stderr, "Failed to read from %s\n", filename);
exit(1);
}
fclose(ifp);
}
for (i = 0; i < 5; i++)
{
if (emp1[i].age != emp2[i].age ||
emp1[i].salary != emp2[i].salary ||
strcmp(emp1[i].name, emp2[i].name) != 0 ||
strcmp(emp1[i].address, emp2[i].address) != 0)
printf("Difference in record %d\n", i);
}
return 0;
}
Content of the file sampledata.bin:
0x0000: 59 6F 64 61 00 00 00 00 00 00 00 00 00 00 00 00 Yoda............
0x0010: 17 00 00 00 C8 AF 00 00 41 73 69 61 00 00 00 00 ........Asia....
0x0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0030: 00 00 00 00 00 00 00 00 44 61 72 74 68 00 00 00 ........Darth...
0x0040: 00 00 00 00 00 00 00 00 22 00 00 00 28 09 00 00 ........"...(...
0x0050: 4E 20 41 6D 65 72 69 63 61 00 00 00 00 00 00 00 N America.......
0x0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0070: 4A 61 62 62 61 00 00 00 00 00 00 00 00 00 00 00 Jabba...........
0x0080: 16 00 00 00 BE 15 00 00 41 66 72 69 63 61 00 00 ........Africa..
0x0090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00A0: 00 00 00 00 00 00 00 00 4C 75 6B 65 00 00 00 00 ........Luke....
0x00B0: 00 00 00 00 00 00 00 00 21 00 00 00 47 0D 00 00 ........!...G...
0x00C0: 53 20 41 6D 65 72 69 63 61 00 00 00 00 00 00 00 S America.......
0x00D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00E0: 4C 65 69 61 00 00 00 00 00 00 00 00 00 00 00 00 Leia............
0x00F0: 2C 00 00 00 15 1A 00 00 45 75 72 6F 70 65 00 00 ,.......Europe..
0x0100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0110: 00 00 00 00 00 00 00 00 ........
0x0118:
You don't specify what you mean by fwrite doesn't work, but I'll assume you're working on Windows, in which case you need to specify "wb" to fopen. By default on Windows, it's writing in text mode (i.e. "wt").
not a good idea to write struct to file or sockets as it is. It is inviting complex to solve problems. the best approach is to use serialization before writing. Also, as Jim pointed out above, make sure to open the file in binary.
Take a look in this question and the answers. there is a pretty good answer and explanation for your question.
Passing a structure through Sockets in C
Data serialization is a non-trivial task. As some others have pointed out, it is possible in some cases to write the contents of your struct to disk as binary data. It's the simplest to write, but it is unlikely to be stable. Each time you recompile your code, it can potentially change the format the data is written and read in.
Your best option is to use a standard data interchange format, such as CSV, XML, or JSON. There are many existing tools to utilize these formats, so you should look into using one of them.