I have this struct
struct _recipe
{
char name[50];
char** ingredients;
char diff[12];
int time;
int calories;
char** procedure;
} recipe;
And I want to copy all the data inside a binary file.
First of all I've allocated dynamically the memory for both the ingredients and procedure and I have written all I needed. But then I need to write all in a binary file. I know that they are both pointers, so this mean that if I use
fwrite(&recipe,sizeof(recipe),1,fbr);
I'll write inside the file the address and not the actually value which is I need. I've tried writing every single field of the struct inside the file this way
fwrite(recipe.name,sizeof(recipe.name),1,fbr);
fgets(recipe.ingredients[j],30,stdin);
strcpy(buff,recipe.ingredients[j]);
len = strlen(buff);
fwrite(buff,sizeof(recipe.ingredients[0]),len,fbr);
fwrite(recipe.diff,sizeof(recipe.diff),1,fbr);
fwrite(&recipe.time,sizeof(recipe.time),1,fbr);
fwrite(&recipe.calories,sizeof(recipe.calories),1,fbr);
fgets(recipe.procedure[i],1000,stdin);
strcpy(buff,recipe.procedure[i]);
len = strlen(buff);
fwrite(buff,sizeof(recipe.procedure[0]),len,fbr);
I'm not sure it's the right way but I tried putting the string inside an other one and then copying it on the file. The problem is, I'm not sure it worked because I don't know what kind of command should I put for reading all the values I stored. With the name of course it worked, I didn't have problems with that but then when I was about to read the ingredients I blocked myself because I wrote the value inside an other string and I don't know what lenght should I put in reading. Maybe I'm missing something, probably I'm messing with writing in the first place but I don't know what to do at this point.
You want to do something like this to write out your recipe:
fwrite(recipe.name,sizeof(recipe.name),1,fbr);
for (int i = 0; i < num_ingredients; i++) {
int len = strlen(recipe.ingredients[i]) + 1;
int num_elements = fwrite(recipe.ingredients[i],sizeof(char),len,fbr);
printf("wrote %d elements for %s\n", num_elements, recipe.ingredients[i]);
}
fwrite(recipe.diff,sizeof(recipe.diff),1,fbr);
for (int i = 0; i < num_procedures; i++) {
int len = strlen(recipe.procedure[i]) + 1;
int num_elements = fwrite(recipe.procedure[i],sizeof(char),len,fbr);
printf("wrote %d elements for %s\n", num_elements, recipe.procedure[i]);
}
You also need to write out the calories and time of course. I would also do this before reading anything into the struct:
memset(&recipe, 0, sizeof(struct _recipe));
If you use fprintf to write out the calories and time with a \n for the separator, your recipe file would look like this:
$ od -c recipe.bin
0000000 m u f f i n s \0 \0 \0 \0 \0 \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0000060 \0 \0 f l o u r \0 s u g a r \0 e g
0000100 g s \0 v a n i l l a \0 n u t s \0
0000120 n o d i f f \0 \0 \0 \0 \0 m i x \0
0000140 b a k e \0 r e s t \0 6 0 \n 2 5 0
0000160 \n
You can design a simple storage protocol for writing/reading.
For example:
| struct recipe | t l v | t l v | ... | t l v | struct recipe | t l v | ... | t l v |
|<- a complete struct recipe data ->|
| description | t(ype) | l(ength) | v(alue)|
| ingredient | i | strlen() | string |
| procedure | p | strlen() | string |
| data end | e | 0 | <null> |
Read a recipe data:
1. Read a struct recipe.
2. Read a t, t=='i' -- go (3.), t=='p' -- go (4.), t=='e' -- go (5.).
3. Read a l, then read a string whose length is l as an ingredient. Go (2.).
4. Read a l, then read a string whose length is l as a procedure. Go (2.).
5. Finish.
Write a recipe data:
1. Write a struct recipe.
2. Write ingredients to `i strlen(ingredient) ingredient` one by one.
3. Write procedures to `p strlen(procedure) procedure` one by one.
4. Write `e 0`.
5. Finish.
Pseudo code: (just a demo, please design your own protol-)
void write_recipe(recipe *r, FILE *fp)
{
fwrite(r, sizeof(recipe), 1, fp);
// ...
for ()
{
sprintf(buff, "i %5d %s", strlen(r->ingredients[i]), r->ingredients[i]);
fwrite(buff, strlen(buff), 1, fp);
}
sprintf(buff, "e 0");
fwrite(buff, strlen(buff), 1, fp);
//...
for ()
{
sprintf(buff, "p %5d %s", strlen(r->procedure[i]), r->procedure[i]);
fwrite(buff, strlen(buff), 1, fp);
}
sprintf(buff, "e 0");
fwrite(buff, strlen(buff), 1, fp);
// ...
}
void read_recipe(FILE *r, FILE *fp)
{
fread(r, sizeof(recipe), 1, fp);
// ...
while (true)
{
fread(&t, sizeof(char), 1, fp);
if (t == 'e')
{
// ...
break;
}
fread(buff, sizeof(char), 7, fp);
sscanf("%d", &len);
fread(buff, sizeof(char), len, fp);
buff[len] = 0;
ingredient = malloc(sizeof(char) * (len + 1));
strcpy(ingredient, buff);
// ...
}
// ...
while (true)
{
fread(&t, sizeof(char), 1, fp);
if (t == 'e')
{
// ...
break;
}
fread(buff, sizeof(char), 7, fp);
sscanf("%d", &len);
fread(buff, sizeof(char), len, fp);
buff[len] = 0;
procedure = malloc(sizeof(char) * (len + 1));
strcpy(procedure, buff);
// ...
}
// ...
}
Related
I have a file and I want to read some random input from file, I don't want to use getline or some things like that, the scanning works but it reads some random stuff, like null or different characters. I think the problem could be when i am reading a single character and that might destroy all this.
Here is some code to see what I did:
#include <stdio.h>
#include <stdlib.h>
typedef struct queries
{
char type;
int node;
char *addr;
} Queries;
int main()
{
int i;
FILE *f;
f = fopen("queries.in", "r");
if (!f)
{
fprintf(stderr, "File queries.in was not opened correctly.\n");
exit(1);
}
int n_queries;
fscanf(f, "%d\n", &n_queries);
Queries *q = (Queries*)malloc(n_queries*sizeof(struct queries));
for (i = 0; i < n_queries; ++i)
{
fscanf(f, "%c ", &q[i].type);
if(q[i].type == 'q') fscanf(f, "%d %s\n", &q[i].node, q[i].addr);
else fscanf(f, "%d\n", &q[i].node);
}
for (i = 0; i < n_queries; ++i)
{
printf("%d %c ", i, q[i].type);
if(q[i].type == 'q') printf("%d %s\n", q[i].node, q[i].addr);
else printf("%d\n", q[i].node);
}
fclose(f);
}
And here is the input:
8
q 0 addr2
q 0 addr1
q 0 addr1
q 1 addr4
q 1 addr1
q 1 addr2
f 4
q 1 addr4
Well expected output:
8
q 0 addr2
q 0 addr1
q 0 addr1
q 1 addr4
q 1 addr1
q 1 addr2
f 4
q 1 addr4
Actual output:
0 q 0 (null)
1 a 0
2 d 0
3 d 0
4 r 2
5 q 0 (null)
6 a 0
7 d 0
I have no idea what's going on
When you fscanf into q[i].addr, there has not yet any memory been allocated to q[i].addr. Now anything can happen because the string is placed in memory that is not yours.
You should also check the return value of fscanf to be suer the data was properly read.
I'd like to send a message over a socket that looks like so: "USER anonymous\r\n".
In order to create a formatted string instead of a constant string, I used snprintf(). Unfortunately, it does not seem to copy the newline character \n, but just the carriage return \r.
#define USERNAME_ANONYMOUS "anonymous"
[...]
// Inside a function.
int sz = snprintf(NULL, 0, "USER %s\r\n", USERNAME_ANONYMOUS);
char* username = NULL;
if ((username = calloc(sz + 1, sizeof(char))) == NULL) {
perror("Could not allocate memory");
return;
}
snprintf(username, sz, "USER %s\r\n", USERNAME_ANONYMOUS);
for (int i = 0; i <= sz; i++) {
printf("%c %d\n", username[i], username[i]);
}
The output:
U 85
S 83
E 69
R 82
32
a 97
n 110
o 111
n 110
y 121
m 109
o 111
u 117
s 115
13
0
0
From this snprintf (and family) reference:
At most bufsz - 1 characters are written.
The size you provide must be including the terminator. You need to use sz + 1 to print the full string.
Well, if you allocate a buffer of size sz + 1 then pass that buffer size to the second snprintf call, which is sz + 1 and not sz, which is the number you pass.
The second call to snprintf should read:
snprintf(username, sz + 1, "USER %s\r\n", USERNAME_ANONYMOUS);
or it will overwrite the last \n with the null terminator, because it has not enough space for the whole string you want to print.
I'm trying to construct a string - for every 80 chars in the loop, to add 7 tabs to the beginning of the line, and a new line at the end.
It should print out 7 tabs, then 80 chars, then 1 new line and so on.
However, something strange is happening. It's printing a new line straight after the first 2 chars and then everything is skewed from then on.
I'm also not sure why I need % 40 rather than % 80 - is it because there are 2 bytes?
I think generally im getting confused by 2 bytes.
void do_file(FILE *in, FILE *out, OPTIONS *options)
{
char ch;
int loop = 0;
int sz1,sz2,sz3;
int seeker = offsetof(struct myStruct, contents.datas);
//find total length of file
fseek(in, 0L, SEEK_END);
sz1 = ftell(in);
//find length from beggining to struct beginning and minus that from total length
fseek(in, seeker, SEEK_SET);
sz2 = sz1 - ftell(in);
int tabs = (sz2 / 80) * 8;// Total size / size of chunk * 8 - 7 tabs and 1 new line char
sz3 = ((sz2 + 1 + tabs) * 2); //Total size + nuls + tabs * 2 for 2 bytes
char buffer[sz3];
char *p = buffer;
buffer[0] = '\0';
while (loop < sz2)
{
if(loop % 40 == 0){
//Add 7 tabs to the beginning of each new line
p += sprintf(p, "%s", "\t\t\t\t\t\t\t");
}
fread(&ch, 1, 1, in);
//print hex char
p += sprintf(p, "%02X", (ch & 0x00FF));
if(loop % 40 == 0){
//Add a new line every 80 chars
p += sprintf(p, "%s", "\n");
}
strcat(buffer, p);
loop++;
}
printf("%s", buffer);
}
However, something strange is happening. It's printing a new line straight after the first 2 chars and then everything is skewed from then on.
It's because of the initial value of loop, try with int loop = 1;
I'm also not sure why I need % 40 rather than % 80 - is it because there are 2 bytes?
I think generally im getting confused by 2 bytes.
The point is, for each character you read in input file, you write two characters in buffer, because you decided to print characters as two bytes (%02X).
Now what is your need:
insert LF and tabs each 80 characters of the input file? (Is that your expecting?)
insert LF and tabs each 80 characters of the output? (That is what you coded)
I'm going through a programming book and I'm wondering what a line of code does. There are no comments in the book or explanations about what it's supposed to do.
Below is a function that takes a character array and prints it backwards.
void print_reverse (char *s)
{
size_t len = strlen(s);
char *t = s + len -1; // I don't understand what this line is doing
while (t >= s) {
printf("%c", *t);
t = t - 1;
}
puts("");
}
Let's say s is the string "PaxDiablo", stored in memory at location 1 thus:
s
|
V
+---+---+---+---+---+---+---+---+---+----+
| P | a | x | D | i | a | b | l | o | \0 |
+---+---+---+---+---+---+---+---+---+----+
Address: 1 2 3 4 5 6 7 8 9 10
The expression t = s + len - 1 (where len is 9 in this case) sets t to eight characters past s.
s t
| |
V V
+---+---+---+---+---+---+---+---+---+----+
| P | a | x | D | i | a | b | l | o | \0 |
+---+---+---+---+---+---+---+---+---+----+
Address: 1 2 3 4 5 6 7 8 9 10
In other words, it gives you the address of the last character in the string.
The rest of the code then iterates over the string in a backwards direction, by decrementing t until it passes s.
Technically, this is undefined behaviour since you're only every supposed to compare pointers where they point to the same array on one character beyond (here we are comparing t where it's one character before the array), but you'd struggle to find a system on which this didn't work.
As Potatoswatter (I absolutely love some of the names people choose here on SO) points out in a comment, you can avoid that comparison by using the do {} while construct rather than while {}:
#include <stdio.h>
#include <string.h>
void printReverse (char *str) {
size_t len = strlen (str);
if (len != 0) {
char *pStr = str + len;
do {
putchar (*(--pStr));
} while (pStr > str);
}
putchar ('\n');
}
Let's say s points to the string "Hello". In memory, it looks like:
s --> [ H | e | l | l | o | \0 ]
Now, char *t declares a new pointer variable to a char value, that is initialized with s + len - 1, which is:
s ------ ------- s + 5 // (len is 5 since strlen("Hello") is 5)
| |
[ H | e | l | l | o | \0 ]
|
------------ finally, t = s + len - 1
Code is from
char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";
int
main(int argc, char *argv[])
{
int fd;
if((fd = creat("file.hole", 0777)) < 0)
perror("creat error");
if(write(fd, buf1, 10) != 10)
perror("buf1 write error");
if(lseek(fd, 04000, SEEK_SET) == -1)
perror("lseek error");
if(write(fd, buf2, 10) != 10)
perror("buf2 write error");
exit(EXIT_SUCCESS);
}
read file by
od -c file.hole
and Output:
0000000 a b c d e f g h i j \0 \0 \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0004000 A B C D E F G H I J
0004012
if i remove lseek , and creat file.nohole, the output will be
0000000 a b c d e f g h i j A B C D E F
0000020 G H I J
0000024
There are two problem make me confused.
> 1.At output1, why there are 30 * '\0' after j
> 2.output1: why file ends with 0004012 not 0004010
1.At output1, why there are 30 * '\0' after j
Not 30, but almost 04000. od truncated the output because it was huge and repetitive. Why are there zeroes? Because you told your program to insert them. lseek() padded the file.
why file ends with 0004012 not 0004010
It doesn't - it's again od prints addresses in octal, not in hexadecimal (as you may have expected).