Adding structs to an array in C - c

I am having a problem in adding structs to an array in C-language. I think it's stemming from the fact that I might not be using correctly the pointers.
I have some struct with the following syntax:
struct account
{
int num;
char* fname;
char* lname;
char* pin;
double bal;
};
and in my main function I want to have a for loop that adds a struct to an array after going through and setting the variables of my struct to something. This is my full code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct account
{
int num;
char* fname;
char* lname;
char* pin;
double bal;
};
void split_str (struct account *a,char *line)
{
int i = 0; //incrementer to tell what we are looking at: num, fname, lname, pin, or bal
int acc_num; //temp vals to store acc_num casting and balance casting
double bal;
char * str;
str = strtok(line, " ");
while (str!= NULL){
switch(i){
case 0:
acc_num = atoi(str);
a->num = acc_num;
acc_num = 0;
break;
case 1:
a->fname = str;
break;
case 2:
a->lname = str;
break;
case 3:
a->pin = str;
break;
case 4:
bal = atof(str);
a->bal = bal;
bal = 0;
break;
}
str = strtok (NULL, " ");
i++; //increment because we want to look through all 5 possibilities of vars
}
}
int main()
{
FILE *fh = fopen("account_info.txt", "r");
struct account accts[100];
if (fh != NULL)
{
char line[256];
for (int i=0; fgets(line, sizeof line, fh) != NULL; i++)
{
split_str(&accts[i], line);
}
}
fclose(fh);
for (int i=0; i<3; i++)
{
printf("%s %s index: %i\n", accts[i].fname, accts[i].lname, i);
}
}
This code is used with an account_info.txt file. All that file contains is one "account" per line delimited with a space " " char. Example:
123456 Jane Doe 1234 250.50
123457 John Smith 2222 12.34
123458 Sally Jones 9999 321.79
The problem with my current code is that it does seem to enter a struct in to the array, but it does it incorrectly. Here is what my code spits out at me when I run it with the print statement in the second for loop of my code:
Sally Jones index: 0
Sally index: 1
Sally Jones index: 2
Any help at all for why this is acting this way would be much appreciated.

In split_str() you are assigning pointers (locations) to
the local strings in the accounts that are dangling after
the function has been left (because the str pointer in
split_str is de-allocated and possibly reused at any
other point in the code later on...).
The make sure that the information gathered in the
split_str survives the function calls, you need
to allocate memory to fname (or lname or pin or any
other pointer) in the function, and then use some memcpy
or other methods to copy the information that str is pointing
to into the structure pointed to by a.

The problem is that there is only one instance of line in main so all the array pointers are pointing to that one line and whatever it contains after the last fgets.
sscanf could be used to parse the integer and double. Using the %n specifier, the starting and stopping indexes for names and pin can be captured. The %*s specifier will read and discard the strings as their length is not yet known. Once the start and stop indexes are known, memory can be allocated and the strings copied using memcpy.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct account
{
int num;
char* fname;
char* lname;
char* pin;
double bal;
};
int split_str (struct account *a,char *line)
{
int startfn = 0;
int stopfn = 0;
int startln = 0;
int stopln = 0;
int startpin = 0;
int stoppin = 0;
if ( ( 2 == sscanf ( line, "%d %n%*s%n %n%*s%n %n%*s%n %lf"
, &(a->num)
, &startfn
, &stopfn
, &startln
, &stopln
, &startpin
, &stoppin
, &(a->bal)))) {
if ( ( a->fname = malloc ( ( stopfn - startfn) + 1)) == NULL) {
fprintf ( stderr, "problem malloc\n");
exit ( 1);
}
memcpy ( a->fname, &line[startfn], stopfn - startfn);
a->fname[stopfn - startfn] = '\0';
if ( ( a->lname = malloc ( ( stopln - startln) + 1)) == NULL) {
fprintf ( stderr, "problem malloc\n");
exit ( 1);
}
memcpy ( a->lname, &line[startln], stopln - startln);
a->lname[stopln - startln] = '\0';
if ( ( a->pin = malloc ( ( stoppin - startpin) + 1)) == NULL) {
fprintf ( stderr, "problem malloc\n");
exit ( 1);
}
memcpy ( a->pin, &line[startpin], stoppin - startpin);
a->pin[stoppin - startpin] = '\0';
return 1;
}
return 0;
}
int main()
{
FILE *fh = fopen("account_info.txt", "r");
struct account accts[100];
int loaded = 0;
if (fh != NULL)
{
char line[256];
for (int i=0; fgets(line, sizeof line, fh) != NULL; i++)
{
if ( split_str(&accts[i], line)) {
loaded++;
}
}
}
fclose(fh);
for (int i=0; i<loaded; i++)
{
printf("%s %s index: %i\n", accts[i].fname, accts[i].lname, i);
}
for (int i=0; i<loaded; i++)//free allocated memory
{
free ( accts[i].fname);
free ( accts[i].lname);
free ( accts[i].pin);
}
}

Related

How to get whole structure from function?

This is my code:
#include <stdio.h>
typedef struct
{
char name[100];
char number[100];
} contact_t;
void empty_array(char *line)
{
for (int j = 0; line[j] != '\0'; j++)
{
line[j] = '\0';
}
}
void read_text(contact_t *contact)
{
int c, cnt = 0;
int i = 0;
char line[100];
do
{
c = getchar();
if ( (c == '\n') || (c == EOF))
{
if( cnt % 2 == 0)
{
for(int j = 0; line[j] != '\0'; j++)
contact -> name[j] = line[j];
}
else
{
for(int j = 0; line[j] != '\0'; j++)
contact -> number[j] = line[j];
}
empty_array(line);
i = 0;
cnt++;
}
line [i] = c;
i++;
} while (c != EOF);
}
int main()
{
contact_t contact = {"x", "0"};
int *j_ptr;
read_text(&contact);
printf("%s", contact.name);
printf("%s", contact.number);
return 0;
}
I am reading a text file(6 lines, name and number, name and number...) from standard input. Then I assign every second line(starting from the first) from that text file to structure contact.name and the rest are I assign to contact.number. So I have several 3 contact structures. I managed to pass to main only the last one, because I don't know how to get acces to int cnt and again make a for cycle.
This is what last prints give me:
John Green
254454556
UPDATE:
I am sorry for not being clear enough as I was writing this question in a hurry. This code is a part of school project and we are not allowed to work with dynamically allocated memory or use fscanf, fopen, qsort, lsearch, bsearch and hsearch etc. Basically, I would just like to use pointers to index of array line and then in main function use a for cycle again to pass all structures from the function read_text to main function of the program.
A few issues ...
main only provides space for one contact entry
read_text needs to use a dynamic array (vs. overwriting the same entry)
read_text needs to return the list pointer and the count to the caller (e.g. main)
The method used in read_text is a bit convoluted.
Style fixes:
contact -> name --> contact->name
list [i] --> list[i]
Here is the refactored code. It is annotated:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[100];
char number[100];
} contact_t;
int
read_text(contact_t **listp)
{
char buf[1000];
contact_t *list = NULL;
char *cp = NULL;
int cnt = 0;
// loop on input until EOF
while (fgets(buf,sizeof(buf),stdin) != NULL) {
// increase size of list
++cnt;
list = realloc(list,sizeof(*list) * cnt);
// handle error
if (list == NULL) {
perror("realloc/increase");
exit(1);
}
// point to current record
contact_t *contact = &list[cnt - 1];
// get first name
contact->name[0] = 0;
cp = strtok(buf," \n");
if (cp == NULL)
break;
strcat(contact->name,cp);
// add separater
strcat(contact->name," ");
// get last name
cp = strtok(NULL," \n");
if (cp == NULL)
break;
strcat(contact->name,cp);
// get number
cp = strtok(NULL," \n");
if (cp == NULL)
break;
strcpy(contact->number,cp);
}
// trim to actual amount stored (if error)
if ((cp == NULL) && (cnt > 0)) {
--cnt;
list = realloc(list,sizeof(*list) * cnt);
if (list == NULL) {
perror("realloc/trim");
exit(1);
}
}
// give caller the list pointer
*listp = list;
return cnt;
}
int
main(void)
{
int cnt;
contact_t *list;
cnt = read_text(&list);
// print all entries read in
for (int idx = 0; idx < cnt; ++idx) {
contact_t *contact = &list[idx];
printf("'%s' '%s'\n",contact->name,contact->number);
}
return 0;
}
Here is the test input I used:
John Green 254454556
Fred Smith 8765309
Bob Jones 99728967341
Mary Gallagher 4329268757
Here is the program output:
'John Green' '254454556'
'Fred Smith' '8765309'
'Bob Jones' '99728967341'
'Mary Gallagher' '4329268757'
UPDATE:
I am sorry, I should have clarified that I cannot use dynamically allocated memory. Malloc, calloc or also fsangf is not available –
gregalz
Okay, no malloc et. al. Ironically, I was going to use a predefined fixed size array. But, decided to use a dynamic array instead ;-)
Not sure what fsangf is. So, I'll assume that's fscanf. If you're heavily restricted, maybe you should edit your question and post what you can and can not use.
Here's the code that uses just a fixed array:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[100];
char number[100];
} contact_t;
#define NLIST 1000
contact_t list[NLIST];
int
read_text(contact_t *list,int max)
{
char buf[1000];
char *cp = NULL;
int cnt = 0;
// loop on input until EOF
while (fgets(buf,sizeof(buf),stdin) != NULL) {
// don't overflow the max size
if (cnt >= max)
break;
// point to current record and increase list count
contact_t *contact = &list[cnt++];
// get first name
contact->name[0] = 0;
cp = strtok(buf," \n");
if (cp == NULL)
break;
strcat(contact->name,cp);
// add separater
strcat(contact->name," ");
// get last name
cp = strtok(NULL," \n");
if (cp == NULL)
break;
strcat(contact->name,cp);
// get number
cp = strtok(NULL," \n");
if (cp == NULL)
break;
strcpy(contact->number,cp);
}
// trim to actual amount stored (if error)
if ((cp == NULL) && (cnt > 0))
--cnt;
return cnt;
}
int
main(void)
{
int cnt;
cnt = read_text(list,NLIST);
// print all entries read in
for (int idx = 0; idx < cnt; ++idx) {
contact_t *contact = &list[idx];
printf("'%s' '%s'\n",contact->name,contact->number);
}
return 0;
}

Read a CSV file into a dynamic array of structs C

I'm fairly new to C. I'm trying to read a .CSV file, then parse each line, then store the data in a dynamic array of pointers to structs. Unfortunately I've gone wrong somewhere in my implementation which is resulting in an infinite loop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct dataSet {
char ID;
char postcode;
int population;
char contact;
double x;
double y;
}data;
int main(int argc, char* argv[]) {
char line[100] = "";
int count = 0;
int each = 0;
data *allData = NULL;
data *temp = NULL;
FILE *file = fopen("dataset.csv", "r");
if (file == NULL)
{
printf("Error! File null");
return 1;
}
while (fgets(line, sizeof line, file))
{
if(NULL == (temp = realloc(allData, sizeof(*allData) * (count + 1))))
{
fprintf(stderr, "realloc problem\n");
fclose(file);
free(allData);
return 0;
}
allData = temp;
if (6 == scanf(line, "%s, %s, %d, %s, %lf, %lf",
&allData[count].ID,
&allData[count].postcode,
&allData[count].population,
&allData[count].contact,
&allData[count].x,
&allData[count].y)) {
count++;
}
else {
printf("Problem with data\n");
}
}
fclose(file);
for (each = 0; each < count; each++)
{
printf("%s, %s, %d, %s, %lf, %lf\n",
&allData[count].ID,
&allData[count].postcode,
&allData[count].population,
&allData[count].contact,
&allData[count].x,
&allData[count].y);
}
free(allData);
return 0;
}
Any help or tips would be greatly appreciated.
[s]scanf() is a nasty function. You don't have enough control once it fails. Problem is: there are too many conditions: the input can be incorrect, or the destination is not large enough. Even reading complete lines with fgets(), and parsing them afterwards, will only allow you to skip complete lines; also: the line buffer is mostly fixed sized, and fgets() could read incomplete lines. A way to keep complete control is to read character-based. This might imply a Finite State machine.
A simpler reader (using a zero-state machine) could be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct omg {
char o;
int m;
char g[11];
};
struct wtf {
unsigned size;
unsigned used;
struct omg *array;
};
#define INITIAL_SIZE 7
struct wtf read_stuff(char *name)
{
FILE *fp;
unsigned icol,irec,len;
char buff[123];
struct wtf this = {0,0,NULL};
fp = fopen(name, "rb" );
if (!fp) return this;
for (icol=irec=len=0; ; ) {
int ch;
if (this.used >= this.size) {
size_t newsize;
struct omg *tmp;
newsize = this.size? this.size*2: INITIAL_SIZE;
fprintf(stderr, "Realloc(%zu)\n", newsize);
tmp = realloc(this.array, sizeof *this.array * newsize);
this.array = tmp;
this.size = newsize;
}
ch = getc(fp);
switch(ch) {
case '\r' : continue;
/* End of field or record: terminate buffer */
#if 0
case ',' :
#else
case '\t' :
#endif
case '\n' :
buff[len] = 0;
break;
case EOF :
goto done;
/* Normal character: assign to buffer
** You may want to report too long fields here
*/
default:
if (len >= sizeof buff -2) continue;
buff[len++] = ch;
continue;
}
/* When we arrive here, we have a new field. Let's process it ...*/
switch (icol) {
case 0: /* Assign first field here from buff[], (dont forget to check len!) */
this.array[this.used].o = buff[0];
break;
case 1: /* Assign second field from buff[], this may need some additional checks
** You may want to avoid sscanf() here ...
*/
sscanf(buff, "%d", &this.array[this.used].m );
break;
case 2: /* Assign third field from buff[] */
if (len >= sizeof this.array[this.used].g)
len = sizeof this.array[this.used].g -1;
memcpy (this.array[this.used].g, buff, len);
this.array[this.used].g[len] = 0;
break;
default: /* Ignore excess fields
** You may want to report hem.
*/
break;
}
/* Do some bookkeeping */
len = 0;
if(ch == '\n') {
/* You may want to check if icol==2, here */
icol=0; irec++; this.used++;
}
else icol++;
}
done:
fclose(fp);
/* You could do a final realloc() here */
return this;
}
int main(int argc, char **argv)
{
struct wtf result;
unsigned idx;
result = read_stuff(argv[1] );
fprintf(stderr, "Result=%u/%u\n", result.used,result.size);
for (idx=0; idx < result.used; idx++) {
printf("%c %d %s\n"
, result.array[idx].o
, result.array[idx].m
, result.array[idx].g);
if (idx >= 10) break;
}
return 0;
}
You ask for tips...
1 - your struct is wrong if your plan was to use dynamic memory. The char members should be pointers to char, ( char * not char ) as shown below. But to reduce complexity, use char arrays instead of forcing dynamic allocation for struct members: i.e. do not use this:
typedef struct dataSet {
char *ID;
char *postcode;
int population;
char *contact;
double x;
double y;
}data;
Rather use this:
typedef struct dataSet {
char ID[80];
char postcode[11];
int population;
char contact[80];
double x;
double y;
}data;
If the lengths are not right, then make them bigger, but this will reduce calls to calloc() and free().
2 - suggested steps:
Count lines in file. (example here). This will essentially open the file, count the lines and close the file.
Use the count to allocate memory for that number of instances of data (i.e. data *records = malloc(sizeof(*records)*countOfLines); )
Open the file again. If file != NULL, then...
Begin to read file line by line in a loop, such as the fgets(...) loop you have.
In this loop, suggest replacing scanf() with a series of calls to strtok() making the appropriate conversion one-by-one. Its a few more lines of code, but is easier in the long run to see what parsing problems you might run into.
The following pseudo code illustrates...
data *record = malloc(CountOfLines*sizeof(*record));
if(record)
{
int i = 0;
while(fgets(line, sizeof line, file))
{
tok = strtok(line, ",");
if(tok)
{ //convert string
strncpy(record[i].ID, tok, sizeof(record[i].ID) - 1);
tok = strtok(NULL, ",");
if(tok)
{//convert string
strncpy(record[i].postcode, tok, sizeof(record[i].postcode) - 1);
tok = strtok(NULL, ",");
if(tok)
{//convert int
record[i].population = atoi(tok);
//and so on ...

Read text from phone book list and add to a structure

I am attempting to read from file "pb_List.txt" that contains:
John:789-654-3210
Bill:852-123-4567
Amy:963-321-0000
I need to add the name and number contents to a phone book structure "pb"
struct phonebook{
char name[value_size];
char phone[value_size];
}
struct phonebook pb[book_size];
UPDATE:
void addFile(){
File *pb_List;
pb_List = fopen("pb_List.txt", "r");
char name[value_size];
char phone[value_size];
fscanf(pb_List, "%s %s", name, phone);
strcpy(pb[size].name, name);
strcpy(pb[size].phone, phone);
size++
}
I was able to add the first line, but my function obviously doesn't iterate to the next line. How would my while loop look for this?
So I ended up wanting to continue writing an almost full program to your problem. Thus, I updated this answer with a full example of how to use my original answer in a program. Below the code of my original answer is the actual code of a working program.
For your code, I don't think the "phonebook"'s name and phone should be an array but should be a pointer to arrays of chars. That is because if you have a large program even if you give a size that is always bigger than the necessary size or working with a version of "c" that allows dynamic array allocation at runtime, you can run into the problem of running out of stack memory.
Nonetheless, this code below is an example and is for parsing just one line. In a real usage case, you would have to modify the code properly to use it in a loop. For the routines, you just look for the ":" location. If you found that then look for the null char(end of string) location. I did place comments in the code, thus, I am not going to explain much here.
Also, you should give consideration to letting pb be dynamic with calloc() and realloc(). Also, calloc() and realloc() do not always work. You can also, use malloc() but you have to inject the null char by yourself.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct phonebook{
char * name;
char * phone;
};
int main(void) {
char * line = "John:789-654-3210";
char c;
// search for the :
struct phonebook pb[1];
int dotloc = -1;
int endloc = 0;
int recordIndex = 0;
// Find the dot first
// and then end line.
// This could be done in one loop
// But split into two
for ( int i = 0; ; i++) {
if( line[i] == ':' ) {
dotloc = i;
break;
}
if( line[i] == '\0' ) break;
}
// If found : then that is valid
if ( dotloc > -1 ){
for( int i = dotloc + 1; ; i++){
if( line[i] == '\0' ){
endloc = i;
break;
}
}
// Positioning
// If : is at pos, there is 5 char in the string
// add + for null char.
// If : is at 0 there isn't a char but when calloc still need one for the null char. Empty string.
pb[recordIndex].name = (char*) calloc(dotloc + 1, sizeof(char));
// If : is at 5 and end is at 10, there is only 4 char in between but add keep five because of end char.
pb[recordIndex].phone = (char*) calloc(endloc - dotloc, sizeof(char));
// Memory allocation fail.
// Do something else.
if( pb[recordIndex].name == NULL ) return 1;
if( pb[recordIndex].phone == NULL ) return 1;
// copy from line[0] to dotloc location as how many chars.
// if dotloc is 0, nothing will be copy.
// null char is already appended by calloc.
memcpy(pb[recordIndex].name, line, dotloc * sizeof(char));
// copy from the location of where dotloc is plus 1
// how many char is base on where endloc is - dotloc
// -1
// If endloc at 1 and dot loc is at 0(next to), nothing to be copy.
memcpy(pb[recordIndex].phone, &line[dotloc + 1], (endloc - dotloc - 1));
recordIndex++;
}
printf("%s\n", pb[0].name);
printf("%s", pb[0].phone);
// Sometimes you need to free memory properly.
// depend on which system you target or your use case.
free(pb[0].name);
free(pb[0].phone);
return 0;
}
I won't give much explanation into what each function in the program does, as the key point already mentioned above and that can take hours to write. This program below to demonstrate how to utilize my original answer.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct phoneRecord{
char * name;
char * phone;
};
struct phoneBook{
struct phoneRecord * record;
int length;
int size;
};
struct phoneBook newBook(){
// change init_size to 1 for debugging
int init_size = 30;
static size_t recordSize = sizeof(struct phoneRecord);
struct phoneBook output;
output.record = (struct phoneRecord*) malloc( init_size * recordSize);
output.length = 0;
output.size = init_size;
return output;
}
void freeBook( struct phoneBook * pb){
for ( int i = 0; i < pb->length; i++ ){
free(pb->record[i].name);
free(pb->record[i].phone);
}
free(pb->record);
pb->record = NULL;
pb->length = 0;
pb->size = 0;
}
// 0 for success
// 1 for error
int increaseBookSize(struct phoneBook * pb){
if ( pb == NULL ) return 1;
static size_t recordSize = sizeof(struct phoneRecord);
const int newSize = pb->size * 2;
if ( newSize == 0 ) return 1;
struct phoneRecord * tempPointer = (struct phoneRecord*) realloc(pb->record, newSize * recordSize);
if ( tempPointer != NULL ) {
pb->record = tempPointer;
pb->size = newSize;
return 0;
}
return 1;
}
// Return - 1 for error.
// Return 0 for no record found or no dot.
// Return 1 for record found.
int getRecord( const char * line, struct phoneRecord * pr){
// Null check
if ( pr == NULL ) return -1;
int dotloc = -1;
int endloc = 0;
for ( int i = 0; ; i++) {
if( line[i] == ':' ) {
dotloc = i;
break;
}
if( line[i] == '\0' ) break;
}
if ( dotloc > -1 ){
for( int i = dotloc + 1; ; i++){
if( line[i] == '\0' ){
endloc = i;
break;
}
}
pr->name = (char*) calloc(dotloc + 1, sizeof(char));
pr->phone = (char*) calloc(endloc - dotloc, sizeof(char));
if( pr->name == NULL ) return -1;
if( pr->phone == NULL ) return -1;
memcpy(pr->name, line, dotloc * sizeof(char));
memcpy(pr->phone, &line[dotloc + 1], (endloc - dotloc - 1));
return 1;
}
return 0;
}
int main(void) {
struct phoneBook pb = newBook();
const char * fileName = "test.txt";
char * line = NULL;
FILE *fp;
size_t len = 0;
int recordReturnCode;
fp = fopen(fileName, "r");
if (fp == NULL){
printf("Couldn't open file %s.\n", fileName);
return 1;
}
while (getline(&line, &len, fp) != -1) {
if ( pb.length >= pb.size ) {
if ( increaseBookSize(&pb) != 0 ) {
printf("Something is wrong with getting more memory for the book. However, still print out what already got.\n");
break;
}
}
recordReturnCode = getRecord(line, &pb.record[pb.length]);
if ( recordReturnCode == 1 ) pb.length++;
if ( recordReturnCode == -1 ){
printf("Something is wrong with getting the record. Clean up and exit.\n");
freeBook(&pb);
free(line);
return 1;
}
}
free(line);
printf("Print phonebook size for debuging. Size: %d\n", pb.size);
printf("Read file '%s' and found %d records. Printing each record.\n\n", fileName, pb.length);
for ( int i = 0; i < pb.length; i++ ){
printf("Record: %d | Name: %s | Phone: %s", i, pb.record[i].name, pb.record[i].phone);
}
freeBook(&pb);
printf("\n\nChecking book after free. Length: %d, Size: %d", pb.length, pb.size);
if ( pb.record == NULL ) printf("\nPhonebook free properly, record is NULL.");
return 0;
}
test.txt content:
John:789-654-3210
Bill:852-123-4567
This is not a valid record
Amy:963-321-0000
AfterEmpty:123-456-789
:###-###-####
noNumber:
Kevin:123-123-1234

C Reading user entered data

I'm trying to write a simple program that reads user entered strings into an array of pointers. The reading goes fine, however when I want to add an extra parameter to my method in order to save how many Strings I actually read, it stops working. The compiler isn't very helpfull so I decided to take my problem here.
Actual code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void read(char**, int *);
void write(char**);
int main() {
int amount = 0;
int * amount_p = &amount;
char *pt_p[1000];
read(pt_p,amount_p);
write(pt_p);
}
void read(char ** pt, int * amount) {
char stop[] = "STOP";
char* woord;
int i = 0;
printf("Enter a word: ");
scanf("%70s", woord);
pt[i] = malloc(sizeof(char)*(strlen(woord)+1));
pt[i] = strcpy(pt[i], woord);
i++;
while(strcmp(stop,pt[i-1]) != 0) {
printf("Enter a word: ");
scanf("%70s", woord);
pt[i] = malloc((strlen(woord)+1)*sizeof(char));
pt[i] = strcpy(pt[i], woord);
i++;
}
*amount = i;
}
void write(char ** pt) {
int i = 0;
char stop[] = "STOP";
while(strcmp(stop,pt[i]) != 0 ) {
printf("pt[%d]-> %s",i,pt[i]);
printf("X \n");
i++;
}
}
you need to allocate some space where you can enter the string
char* woord; is just declaring a pointer that points nowhere in particular.
instead declare it as
char woord[128];
to allocate 128 bytes on the stack for your input.
also use fgets() instead of scanf() to read strings, that way you can prevent user from entering a too large string.
if ( fgets( woord, sizeof(wooord), stdin ) != NULL )
{
char* p = strchr( woord, '\n' );
if (p != NULL )
{
*p = '\0';
}
}

reading a csv file into struct array

I'm beginning to code in C. My code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STR_LEN 256
#define MAX_BOOKS 256
struct book{
int ID;
char *name;
char *dateIn;
char *dateOut;
};
struct book books[MAX_BOOKS];
/* PROTOTYPE OF FUNCTIONS */
int readBookFile();
void printBookList();
int main(int argc, char **argv)
{
int isOK = 0;
isOK = readBookFile();
printBookList();
system("pause");
return 0;
}
int readBookFile()
{
/* FileStream for the Library File */
FILE *bookFile;
/* allocation of the buffer for every line in the File */
char *buf = malloc(MAX_STR_LEN);
char *tmp;
/* if the space could not be allocaed, return an error */
if (buf == NULL) {
printf ("No memory\n");
return 1;
}
if ( ( bookFile = fopen( "library.dat", "r" ) ) == NULL ) //Reading a file
{
printf( "File could not be opened.\n" );
}
int i = 0;
while (fgets(buf, 255, bookFile) != NULL)
{
if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
buf[strlen (buf) - 1] = '\0';
tmp = strtok(buf, ";");
books[i].ID = atoi(tmp);
tmp = strtok(NULL, ";");
books[i].name = tmp;
tmp = strtok(NULL, ";");
books[i].dateIn = tmp;
tmp = strtok(NULL, ";");
books[i].dateOut = tmp;
//tempBook.ID = atoi(buf);
printf("index i= %i ID: %i, %s, %s, %s \n",i, books[i].ID , books[i].name, books[i].dateIn , books[i].dateOut);
i++;
}
//free(buf);
fclose(bookFile);
return 0;
}
void printBookList()
{
int i;
//i = sizeof(books) / sizeof(books[0]);
//printf ("%i \n", i);
for (i = 0; i <= sizeof(books); i++)
{
if (books[i].ID != 0)
printf("index i= %i ID: %i, %s, %s, %s \n",i, books[i].ID , books[i].name, books[i].dateIn , books[i].dateOut);
else
break;
}
}
The problem is, that after readBookFile() ends, the Array of my struct is full of the last value of the input file..
My input file is:
1;das erste Buch; 12122013; 13122013
2;das Zweite Buch; 12122013; 13122013
3;das dritte Buch; 12122013; 13122013
4;das vierte Buch; 12122013; 13122013
5;das fünfte Buch; 12122013; 13122013
6;das sechste Buch; 12122013; 13122013
so in the readBookFile function the printf returns the correct values, but in the printBooksList() function all values seem to have changed to the last line of my inputfile.
Can anyone explain this to me and maybe point me in the right direction?
Thanks a lot
Hagbart
The problem is your struct:
struct book{
int ID;
char *name;
char *dateIn;
char *dateOut;
};
name, dateIn, dateOut are "pointer", they are just point to something, you're not allocating spaces for them.
What you do is just point them to tmp(buf).
So what you do in printBookList() is just print same string block, while ID is OK since it's not pointer.
To solve this, allocate space for them, you can use strdup(), but make sure to free them.
In the while loop:
while (fgets(buf, 255, bookFile) != NULL)
you are copying into the memory location of buffer new contents from file. As tmp points to a certain point in the buffer, its contents are being replaced too.
tmp = strtok(NULL, ";");
books[i].name = tmp;
You should allocate memory for each struct of the array and then use strcopy.
You can find an explanation of differences between strcpy and strdup here:
strcpy vs strdup
The reason is that in something like
books[i].name = tmp;
You're not actually copying a string from tmp into books[i].name: you just make both point to the same location - somewhere into the buf buffer.
Try using strdup instead, as in:
books[i].name = strdup(tmp);

Resources