C Programming - File - fwrite - c

I've got a question regarding programming and files.
while(current!=NULL)
{
if(current->Id_Doctor!='\0')
{
current=current->next;
id_doc=(current->Id_Doctor);
}
if(current->Id_Doctor=='\0')
{
id_doc=id_doc+1;
printf("%d", id_doc);
break;
}
}
fwrite(&id_doc, sizeof(char), 1, Archivo);
I dont know why but it aint writing the value of id_doc on the binary file called 'Archivo'...what could be the problem?
I added a printf of id_doc and the value was printed..I really dont know
Ok, heres the full code(more-less):
struct Medico
{
int Id_Doctor;
int Estado;
char Nombre[60];
char Clave_Acceso[20];
char Especialidad[40];
struct Medico *next;
};
void Dar_Alta_Med (int estado);
void MenuPrincipal(char enta);
int main(void)
{
char enta;
MenuPrincipal(enta);
}
void Dar_Alta_Med(int estado)
{
struct Medico * head = NULL;
struct Medico * prev, *current;
char nombre_doc[60], especialida[40], password[20];
int id_doc=0, estado_doc=1;
FILE *Archivo;
const char *md1="\n<md>\n";
const char *id_doc1="<id_doctor> ";
Archivo=fopen("md.dat", "ab+");
fwrite(md1, 1, strlen(md1), Archivo);
fwrite(id_doc1, 1, strlen(id_doc1), Archivo);
current = (struct Medico *) malloc (sizeof(struct Medico));
current->Id_Doctor=id_doc;
while(current!=NULL)
{
if(current->Id_Doctor!='\0')
{
current=current->next;
id_doc=(current->Id_Doctor);
}
else
{
id_doc=id_doc+1;
printf("%d", id_doc);
break;
}
}
fwrite(&id_doc, sizeof(id_doc), 1, Archivo);
printf("Ingresa el nombre del Doctor a dar de alta: ");
fclose(Archivo);
}
Im dying here, please help :/

Try adding fflush(Archivo); to force a write of all buffered data.
Also, this statement: if(current->Id_Doctor=='\0') really ought to be an else since there is no other thing it can be but '\0'

Three things:
Make sure your fopen is successful.
Archivo=fopen("md.dat", "ab+");
if (Archivo == NULL)
{
perror("Failed to open file Archivo");
...
}
Make sure you are checking the success of your fwrite's.
if (fwrite(&id_doc, sizeof(id_doc), 1, Archivo) < 1)
{
perror("Failed to write to file Archivo");
...
}
Make sure you have a fclose to close the file properly.
if (fclose(Archivo) != 0)
{
perror("Failed to close file Archivo");
...
}
Now that you've post a full sample of your code I guess I should ask if error checking is just left out for brevity? If not, you should think about adding it.
If you're expecting the value of id_doc to be in display format in the output file you'll have to convert the int to a string (using snprintf or similar) and write the string to the output file instead.

fwrite(&id_doc, sizeof(char), 1, Archivo);
If you defined id_doc as anything other than a char it will write \0 to the file.
Much cleaner would be:
fwrite(&id_doc, sizeof(id_doc), 1, Archivo);

If your first current is an Id_Doctor you have an endless loop.
If there is no current after your last current that is not an Id_Doctor, you get an illegal pointer derefenciation.
For your Problem:
try the flush() family.

You're passing a pointer to a FOUR-BYTE INT, but only writing ONE BYTE (the wrong byte)!
Solution: declare id_doc as "char", not "int".

You have previously written the strings "\n<md>\n" and"<id_doctor> " to the file Archivo, which seems to indicate that it is not a binary file at all, but rather an XML-style file.
In this case, what you almost certainly want is:
fprintf(Archivo, "%d", id_doc);

Related

Passing a reference of a member in a struct to modify it

I'm doing a program for a university project. currently I'm doing the user configuration for being able to change both the password and the user name. I was thinking a way to use a single function for both process instead of creating two separated ones.
My goal was to pass a reference to the struct that holds the user data, and a reference to the corresponding member of the struct I want to edit, both being a char[64]. So after I edit the member I can just rewrite the whole line in the file with the updated data.
void change_user_data(char *input, struct user ***logged_user,
char *data_to_alter, FILE **user_registry) {
// making sure the returned input is valid.
if (strcmp(input, "..."))
return;
struct user find_user;
fseek(*user_registry, 0, SEEK_SET);
while (!feof(*user_registry)) {
fread(&find_user, sizeof(struct user), 1, *user_registry);
if (0 != strcmp(find_user.user_name, (**logged_user)->user_name))
continue;
strcpy(data_to_alter, input);
fseek(*user_registry, 0, SEEK_CUR - 1);
fwrite((**logged_user), sizeof(struct user), 1, *user_registry);
break;
}
return;
}
This is the function that should handle the change of both the struct and the file.
change_user_data(change_password(input), &logged_user, (*logged_user)->password, &user_id);
and here was my attempt to pass the arguments.
There was no error message, but there wasn't any change neither for the struct or the file. I guess that what is happening is that it's only creating a copy of the member, I tried to change the way I passed the member but none of them worked.
Minimal Reproductable Example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLEN 64
struct user {
char user_name[MAXLEN];
char password[MAXLEN];
};
void placeholder_function(struct user *logged_user);
void change_user_data(char *input, struct user **logged_user,
char *data_to_alter, FILE **user_registry);
int main() {
struct user logged_user;
placeholder_function(&logged_user);
return 0;
}
void placeholder_function(struct user *logged_user) {
FILE *user_registry;
if (!(user_registry = fopen("user_registry.bin", "w+b"))) {
printf("Couldn't open file\n");
exit(1);
}
strcpy(logged_user->user_name, "Admin\0");
strcpy(logged_user->password, "Admin\0");
fseek(user_registry, 0, SEEK_SET);
fwrite(logged_user, sizeof(struct user), 1, user_registry);
// expected output: Admin Admin
printf("%s %s\n", logged_user->user_name, logged_user->password);
// rest of program here...
change_user_data("1234\0", &logged_user, logged_user->password,
&user_registry);
printf("%s %s\n", logged_user->user_name, logged_user->password);
// wanted output: Admin 1234
}
void change_user_data(char *input, struct user **logged_user,
char *data_to_alter, FILE **user_registry) {
struct user find_user;
fseek(*user_registry, 0, SEEK_SET);
while (!feof(*user_registry)) {
fread(&find_user, sizeof(struct user), 1, *user_registry);
if (0 != strcmp(find_user.user_name, (*logged_user)->user_name))
continue;
strcpy(data_to_alter, input);
fseek(*user_registry, 0, SEEK_CUR - 1);
fwrite((*logged_user), sizeof(struct user), 1, *user_registry);
break;
}
}
I'm not 100 % sure what's going wrong in your specific example.
But you only have to pass the address of the struct or the address of the element, not both. In this case:
struct user ***logged_user
Then you can modify the element with:
strcpy((**logged_user)->password, input);
Now if you would only care about the password and not the username in this function, you could have passed in a pointer to only the password, and not the container struct. But that's your choice.
Now there are some other concerns with this code. You need to make sure the copy from input doesn't cause a buffer overrun in (**logged_user)->password. You can either do this by making sure the input is small enough, or using the strncpy function instead. Make sure destination becomes null terminated, typically with (**logger_user)->password[pass_len - 1] = '\0'
This line
fseek(*user_registry, 0, SEEK_CUR - 1);
Is weird, SEEK_CUR - 1 will evaluate to SEEK_SET, which I doubt you want to do.
Well, It works now. Just changed the edit of the struct to the function change_password(). And edited the exact member i wanted to edit.
bool change_password(char *new_password, struct user ***logged_user) {
char repeat_password[MAXLEN];
system(CLEAR);
printf("\nInput the new password: ");
trim_line(get_input(new_password, MAXLEN));
printf("\nRepeat the new password: ");
trim_line(get_input(repeat_password, MAXLEN));
if (0 != strcmp(new_password, repeat_password)) {
fprintf(stderr, "\nNew password mismatch!\n");
printf(KEY_PRESS);
free_buffer();
return false;
}
strcpy((**logged_user)->data.password, new_password);
return true;
}
After that I pass the struct to the change_user_data() function and done
void change_user_data(bool is_valid, struct user ***logged_user,
FILE **user_registry) {
// making sure the returned input is valid.
if (!is_valid)
return;
struct user find_user;
fseek(*user_registry, 0, SEEK_SET);
while (!feof(*user_registry)) {
fread(&find_user, sizeof(struct user), 1, *user_registry);
if (0 != strcmp(find_user.data.user_name, (**logged_user)->data.user_name))
continue;
fseek(*user_registry, 0, SEEK_CUR - 1);
fwrite((**logged_user), sizeof(struct user), 1, *user_registry);
break;
}
return;
}
I still need to implement the other advice I was given, like not using feof(), and placing myself correctly in the file, but it's a start.

I can't process numbers in a file in C

When I execute this code, it works fine but the output in "kredi.txt" doesn't appear correctly. For example, when I enter 1 for "hesapNo", stone for "soyisim", white for "isim", 20.50 for "bakiye", the output in "kredi.txt" looks like this:
[ stone white ÌÌÌÌÌÌ €4#]
Why? This is my code:
#include<stdio.h>
#include<conio.h>
struct musteriVerisi
{
int hesapNo;
char soyisim[15];
char isim[15];
double bakiye;
};
int main() {
FILE *cfPtr;
struct musteriVerisi musteri = { 0," "," ",0.0 };
if ((cfPtr = fopen("kredi.txt", "r+")) == NULL)
printf("DosyaAcilamadi\n");
else {
printf("HesapNo girin:"
"(1 den 100 e kadar cikis icin 0 girin)");
scanf("%d", &musteri.hesapNo);
while (musteri.hesapNo != 0) {
printf("soyisim,isim,bakiye giriniz:\n?");
fscanf(stdin, "%s%s%lf", musteri.soyisim, musteri.isim, &musteri.bakiye);
fseek(cfPtr, (musteri.hesapNo - 1) * sizeof(struct musteriVerisi), SEEK_SET);
fwrite(&musteri, sizeof(struct musteriVerisi), 1, cfPtr);
printf("HesapNo girin:\n?");
scanf("%d", &musteri.hesapNo);
}
fclose(cfPtr);
}
getchar();
return 0;
}
The topic of the chapter containing this example in the book I study from is "Writing data to random-access file".
fwrite(&musteri, sizeof(struct musteriVerisi), 1, cfPtr);
That line of code makes no sense. You're just writing some binary garbage in memory to a file. There's no reason to expect that to make any sense when the program stops running -- who knows what internal format the system uses to store information.
For example:
char soyisim[15];
Say that contains a two character string with a terminating zero byte. What do the other 13 bytes contain? Do you know? I don't either. So why are you writing unknown garbage to a file?!

C - Reading from a file and saving the info on a variable

im having a problem reading from these 2 files bellow, file1 and file2, and then saving their information in 2 variables.
From the file1 i want to save the name of channels, and from the file2 i want to save the name of the users and the channel where they are signed.
I was thinking of creating 2 typedef struct(shown bellow) and then create 2 variables(shown bellow) and open the files and put the info on those lists.
I also know of another way to do it which is making a 2D array like this char[100][100], the only problem with both these solutions is that I have to impose an upper limit on the amount of the channels the list/array has.
Im not sure if these are the best ways to do it or if there is a better and easier way to do it, could you guys help?
If you guys need any more information just say so, Thanks!
Edit1: i've added the read from the file1 code that i have right now and i think it is working or so it seems but my problem/question was more of is it the right way to save the information to a variable or is there a better/easier way to do it? thanks.
Channel channels[MAX_CHANNELS];
Registration registrations[MAX_REGISTRATIONS];
typedef struct{
char name_channel[20];
int id;
} Channel;
typedef struct{
char username[50];
char name_channel[20];
} Registration;
File1:
General
SO
PCD
FBD
File2:
2016-09-26 14:00:01 paul General
2016-09-26 14:01:11 mary SO
2016-09-27 10:33:17 paul SO
2016-09-27 13:32:10 rachel General
2016-09-27 13:32:12 rachel FBD
code to read the file(i have only done the file1 yet).
File *file1 = fopen("channels.txt", "r");
if(file1==NULL){ perror("Reading error: "); exit(1); } ;
char line[100];
int i = 0;
int w=0;
for(w;w<MAX_CHANNELS;w++){
channels[w].id=-1;
strcpy(channels[w].name, "n");
}
while(fgets(line, 100, file1) != NULL){
printf("Line read: %s", line);
line[ strlen(line) -1 ] = 0;
Channel a;
strcpy(a.name , line);
a.id=1;
channels[i]=a;
i++;
}
fclose(canais);
int k;
for(k=0; k<MAX_CHANNELS; k++){
if(channels[k].id!=-1)
printf("testing var with channels: %s\n", channels[k].name);
}
Just a few tips that might help(in the code comments) :) I think its fine the way you are doing it. I think this is extensible as well since you can add a new member to a struct if you want to enrich you data further. I have seen strtok used to parse through data quite a bit. Strtok should eliminate the need for you to overwrite the newline due to the way it works.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MYSTRINGLEN (50) // In general "magic numbers"
//in code makes it hard to read, though these are fairly apparent,
//but try using #define where you can
typedef struct {
char name[MYSTRINGLEN];
// int id;
}Channel;
typedef struct {
char username[MYSTRINGLEN];
char name[MYSTRINGLEN];
} Registration;
int main(int argc, char *argv[]){
int i = 0;
//int w = 0; not needed
int k = 0;
char line[100];
Channel channels[BUFSIZ] = {{{0}}}; // num of brackets depends on num of nested data structure
Registration registrations[BUFSIZ] = {{{0}}};
/* init all to zero instead w/ bracket syntax
for (w = 0; w < BUFSIZ; w++){
channels[w].id = -1;
strcpy(channels[w].name, "n");
}
*/
FILE * file1 = fopen("channels.txt", "r");
//Many people use strtok to get done what you are doing here if you are interested
while(fgets(line,100,file1)){ // do not need to explicitly state NULL
printf("Line read %s\n", line);
line[strlen(line) -1] = 0;
//Channel a; You have already initialized a whole array of struct, just use them
strcpy(channels[i].name, line);
//a.id = 1;
//channels[i]=a;
i++;
}
fclose(file1);
for(k = 0; k < BUFSIZ; k++){
if (0 != channels[k].name[0]){ //can test if string was populated, dont need id flag
printf("testing var with channels: %s\n", channels[k].name);
}
}
return 0;
}
de here

Coredump in selfmade arrayList

i'm current working on a homework assesment where i'm programming a program ment to stitch textfiles with a piece of an ascii image to create a complete image of all the pieces. The way i intended to write the code is having a while loop looking through a directory finding the parts and adding them to an array. However in my AddFile method(or when i call it to be precise) i get a coredump.. I just started working with c so i dont know if it is very obvious to some of you why i get a coredump or more complicated. Also, i originaly wrote the addFile method to use and accept int's instead of the FILE type, at that point it worked perfectly without any coredumps so i suspect (but hey i might be wrong) that it went wrong when i tried to implement it with the FILE type.
#include <stdio.h>
#include <stdlib.h>
typedef struct{
int listSize;
int listCapacity;
FILE *fileStream;
}FileList;
void addFile(FileList* list, FILE file)
{
if((*list).listSize<(*list).listCapacity)
{
(*list).fileStream[(*list).listSize]=file;
(*list).listSize+=1;
}
else
{
FILE *tempArray = calloc((*list).listSize,sizeof(FILE));
for(int i=0; i<(*list).listSize; i++)
{
tempArray[i]=(*list).fileStream[i];
}
//Do something with array...
free((*list).fileStream);
(*list).listCapacity=((*list).listCapacity)*2;
(*list).fileStream=calloc((*list).listCapacity,sizeof(FILE));
for(int i=0; i<(*list).listSize; i++)
{
(*list).fileStream[i]=tempArray[i];
}
(*list).fileStream[(*list).listSize]=file;
(*list).listSize+=1;
free(tempArray);
}
}
int main()
{
FileList intList;
intList.listSize=0;
intList.listCapacity=1;
intList.fileStream=calloc(intList.listCapacity,sizeof(int));
int fileYcoord=0;
int fileXcoord=0;
while(1)
{
char fileName [100];
int fileNameLength=sprintf(fileName,"part_%02d-%02d",fileXcoord,fileYcoord);
FILE * pFile = fopen (fileName,"r");
if(pFile!=NULL)
{
printf("- ! found file: %s - name length : %d \n",fileName,fileNameLength);
addFile(&intList,*pFile);
fclose(pFile);
fileXcoord++;
}
else
{
if(fileXcoord!=0)
{
fileYcoord+=1;
fileXcoord=0;
}
else
{
break;
}
}
}
printf("size %d , %d",fileXcoord, fileYcoord);
free(intList.fileStream);
return 0;
}
The call to addFile() is dereferencing a FILE *, producing a value of type FILE. This is wrong, this is an opaque type and should always be handled by pointers.

Strange characters when returning char array from struct in C

I am having some problems when returning with printf of a char array from a struct in C.
struct q_entry
{
long mtype;
char mtext[MAXLENGTH + 1];
};
The long mtype from the struct is returning fine, but the string is just returning some weird characters.
int proc_obj(struct q_entry *msg)
{
printf("\npriority: %ld name: %s\n", msg->mtype, msg->mtext);
}
It just returns some strange characters like "priority: 1 name: ▒▒(" and not "priority: 1 name: hello"
I am populating the struct using the following code
int enter(char *objname, int priority)
{
...
strncpy(s_entry.mtext, objname, sizeof(s_entry.mtext) - 1);
s_entry.mtype = priority;
// Send the message
if (msgsnd(s_qid, &s_entry, len, 0) == -1)
{
printf("error: msgsnd failed\n");
return(-1);
}
else
{
return(0);
}
}
I don't have much experience with C, so I don't know too much about using structs. Please let me know if more context or parts of the code is needed. Any kind of help would be very helpful.
I have added a little more code in enter above, and here is more code of the when enter and proc_obj are called
main(int argc, char **argv)
{
int priority;
if (argc != 3)
{
printf("error: incorrect number of arguments\n");
exit(1);
}
else
{
priority = atoi(argv[2]);
}
if (enter(argv[1], priority) < 0)
{
printf("error: message entering failed\n");
exit(1);
}
exit(0);
}
This is in a different file from enter and above code
int server(void)
{
int mlen, r_qid;
struct q_entry r_entry;
// Initialize queue
if ((r_qid = init_queue()) == -1)
return(-1);
for (;;)
{
if ((mlen = msgrcv(r_qid, &r_entry, MAXLENGTH, (-1 * MAXPRIOR), MSG_NOERROR)) == -1)
{
printf("error: msgrcv failed\n");
exit(1);
}
else
{
proc_obj(&r_entry);
}
}
}
The only obvious error in your code is that you should explicitly fill in a zero at s_entry.mtext[MAXLENGTH] such that the string will still be zero terminated if strncpy() hits the limit. But if that were the problem, you would see "hello" followed by strange characters. Are you sure that objname points to the text you're expecting it to point to?
Also, it looks a bit strange that proc_obj() is declared to return an int but actually does not return anything. Your compiler ought to complain about that.
Answer before adding more code
It looks like the s_entry structure object is local, and enter works on the local variable. How are you calling enter and returning the structure after you have done initializing it ? note that you have int as the return type of the enter function. If you are doing return s_entry; then the output you are getting is possible, as only the first word of the structure, ie the lower sizeof (int) part of mtype is considered.
If you are using enter function like this as i described above then make the return type of enter to struct s_entry
You should check the size of len when sending the message.
You don't show the message queue call but my guess is you are somehow miscalling the API and putting garbage into the queue (and then printing garbage in the server).

Resources