I've been looking for answer here, but didn't find solution for this particular problem.
I have this csv file
For imagination, I should have creating hockey players from this .csv file as
enum POZICE { UTOCNIK = 'U', OBRANCE = 'O', BRANKAR = 'B' };
typedef struct hrac {
int cislo;
char jmeno[50];
enum POZICE pozice;
int utocnaSila;
int obrannaSila;
struct hrac *dalsi;
} tHrac;
Number, name, position of player(keeper, defender, etc.), his attack power, defensive power and link to next player.
Creating method:
tHrac* vytvorHrace(int cislo, char* jmeno, enum Pozice pozice, int utocnaSila, int obrannaSila) {
tHrac* hrac = (tHrac*)malloc(sizeof(tHrac));
hrac->cislo = cislo;
strcpy(hrac->jmeno, jmeno);
hrac->pozice = pozice;
hrac->utocnaSila = utocnaSila;
hrac->obrannaSila = obrannaSila;
hrac->dalsi = NULL;
}
And now the core of my problem, loading method
void nactiSeznamHracu() {
char *tmp1[30];
char *tmp2[30];
char *tmp3[30];
char *tmp4[30];
char *ch[255];
int a = 0;
FILE* soubor = fopen(SEZNAM, "r");
fgets(ch, sizeof(ch), soubor);
fputs(ch, stdout);
sscanf(ch, "% [^;]%[^;]", tmp1, tmp2); // parsing whole char *ch into smaller pieces, wont work
puts(tmp1,tmp2);
fscanf(soubor, "%c [^;]%s [^;]", tmp3,tmp4); // without saving first line into ch, and cutting it right away
puts(tmp3);
//token = strtok(ch, "\n");
//puts(token);
}
I can succesfully load first line, but when I try somehow cut the lines into smaller pieces, like Number,Name,Attack,Deffence,Position, it won't work(or cutting only header into smaller pieces), I tried many ways, more than I've here, but result was always the same... (first two lines of img is unrelated, was just testing tHrac, creating and writing)
My intention is to create for cycle where it will load first line, load it seperately into variable which i will use to create tHrac and then it skip into next line and same thing ...
But my experience with C is low, so that's why I'm turning on you. Anyone can help?
sscanf(ch, "% [^;]%[^;]", tmp1, tmp2); // parsing whole char *ch into
puts(tmp1,tmp2);
fscanf(soubor, "%c [^;]%s [^;]", tmp3,tmp4);
tmp1 ,tmp2 and all are array 30 of pointers to char , and passing wrong arguments would cause undefined behavior.
In case either you need to use individual pointer of these arrays after allocating memory to them ,
Or use them as array to char or char * , and you need to allocate memory in latter case and then pass them to sscanf or fscanf.
Note- Also you should check return value of sscanf and fscanf functions .
Related
I am trying to place some text into a structure part of my array is a array which takes part of the text.
For example my structure is:
struct animal
{
char animal_Type[11];
int age;
int numberOfLegs;
int walksPerDay;
char favoriteFood[];
};
I will then have input such as:
dog,2,4,2,biscuits,wet
cat,5,4,0,biscuits,wet,dry,whiskers
bird,1,2,0,birdseed,biscuits,bread,oats,worms,insects,crackers
I have a working solution that places all the values up to walks per day into the structure, however I want to be able to place the food items into Favorite food. I have a dynamic array for this, but i'm not sure how to read remaining text into the favoriteFood array.
The code used is:
fp = open("animals.txt","r");
struct animal *animal = malloc(sizeof(sturct animal)*3);
int i = 0;
if(fp != NULL) {
while(i < 3) {
fscanf(fp,"%s %d %d %d %s",
animal[i].animal_Type,
animal[i].age,
animal[i].numberOfLegs,
animal[i].walksPerDay,
animal[i].favoriteFood); // need to be able to enter the string of food into here
i++
}
How would I go about doing this?
First of, your struct doesn't match what you've said in the comments.
char favoriteFood[];
The above is an array of char, so couldn't possibly hold a list of favourite foods except if it were one string. And since the size of the array is unspecified, you'd not be able to fill it like you have been either. Instead what you actually want is
char **favoriteFood;
unsigned int favoriteFoodSize;
That will let you create an expanding list of strings to fit whatever data you need to accommodate.
As for reading it in, the best way would be to read the entire line in using fgets and then use something like strtok to break the line up by your separator character. First define a very large string to hold the entire line and a char * to hold each field.
char buffer[1024];
char *token;
And then to the main loop would be something like this:
while(fgets(buffer,1024,fp)) {
token=strtok(buffer,",");
strcpy(beasts[i].animal_Type,token);
token=strtok(NULL,",");
beasts[i].age = atoi(token);
/* etc... */
}
You'd need to check whether token is ever NULL to cope with the possibility of short lines and handle it accordingly. And also make sure that the string copied into animal_Type isn't longer than 10 characters...or alternative make it a char * so you can have any size of string.
For the favoriteFood, you'll need to use realloc to increase the size of it to accommodate each new food added and keep going through the string until you run out of tokens.
token=strtok(NULL,",");
if(token) {
beasts[i].favoriteFood=malloc(sizeof(char *));
beasts[i].favoriteFood[0]=strdup(token); // Need to index using 0 as favoriteFoodSize won't have a value yet
beasts[i].favoriteFoodSize=1;
token=strtok(NULL,",");
while(token) {
beasts[i].favoriteFood=realloc(beasts[i].favoriteFood,(beasts[i].favoriteFoodSize+1)*sizeof(char *));
beasts[i].favoriteFood[beasts[i].favoriteFoodSize]=strdup(token);
beasts[i].favoriteFoodSize++;
token=strtok(NULL,",");
}
}
The last food will have a \n in it as fgets keeps it in the buffer it reads, so you could use that to tell if you've finished processing all the foods (you will also need to remove it from the last food). Or if you don't have it, you know the line was longer and you'll need to read more in. But that seems unlikely based on your sample data.
And since you're doing lots of memory allocation, you should ensure that you check the values returned to make sure you've not run out of memory.
I made a simple dictionary code, and surfed for an hour, and I found a file I/O code. But my compiler(I use Microsoft visual C++) says my code(unfortunately, the core part of the code) is wrong. but I can't get it. What is actually wrong and why???
/*
DosDic ver 1.0.0 2015-07-03
*/
#include<stdio.h>
#include<string.h>
char key = 0;
FILE *fp; //set a file pointer var
fp = fopen("dicdata.dat","r"); //open a file
int b = 0;
int trial = 0;
char result[];
char searchfor[] = fp; //save a whole list of dictionary in a var
int i;
char sb[]; //var for search in a list
int getsearchtrial(char sb[]){
for(i=0;i=strlen(sb);i++){ //how much I tried to reach to the word
switch((int)searchfor[b]-(int)sb[i]){ //are two alphabets same?
case 0 :
default : i=0;
}
b++; //keep finding in a list
trial++; //try again
}
return trial;
}
int adress;
int mainpage(){
printf("Type what you want to search : ");
scanf("%c",sb[ ]);
getsearchtrial(sb[ ]) - strlen(sb[ ]) = adress; //where the word is located in the list
for(i = adress;i = adress + 30; i++){ //print
printf("%c",searchfor[i]);
}
printf("Thank you for using DosDic ver.1.0!"); //thank you!
}
void main(){ //call all those functions
mainpage();
fclose(fp); //close list
}
//and whats wrong with this? i cant even get it, but it's not working
Multiple issues.
First of all, you can't assign the result of fopen to fp outside the body of a function; you must move fp = fopen("dicdata.dat", "r"); to within the body of one of your functions (getsearchtrial most likely).
Secondly, you don't read from a file by simply assigning a file pointer to an object; you must use a library function like fscanf or fread or fgets. Assuming your file contains a single string of length 80, you'd need to write something like
char searchfor[81] = {0}; // make sure string is initially empty,
// extra space for string terminator
if ( !fgets( searchfor, sizeof searchfor, fp) )
{
// error reading from file
}
Of course, this depends on how your input file is structured. If it contains a list of strings, then you'll need to use a multidimensional array (or some other structure).
Third, when you declare an array, must specify its size before you can use it. result and searchfor are incomplete array definitions.
Finally, this line
getsearchtrial(sb[ ]) - strlen(sb[ ]) = adress;
needs to be reversed; the target of an assignment must be on the left of the assignment operator.
You need to step back and learn how to write C code from the ground up.
There is so much wrong I'm not even going to itemise it all here - most of it seems to stem from your lack of understanding of arrays in C.
Most notably...
You can't declare an array and not initialise it or specify a size.
You can't assign a FILE * to a char array (and expect decent
results).
You can't execute a statement like fp = fopen at the
global scope like you are.
Try this tutorial and you may fix 95% of your problems, then go from there.
I have my struct, and I have a vector of that struct. Also, I have a .csv file with data separated by comma.
What I need to do is take each value of every line of the .csv file and throw it into my struct vectors. Each line corresponds to a different vector index.
File.csv model
1,carlos,1232
321,patricia,212
5,james riko,23432
The quantity of fields in the same lane may vary, but it will always be separted by comma only.
I did something using strtok() but I couldn't find a way to fill the struct with that.
My Code:
typedef struct{
int num_acc;
char name[25];
double value;
}struct_acc;
int main(){
FILE *arq_acc;
struct_acc acc[3];
char buffer[256];
char *pointer;
arq_acc = fopen("accs.csv", "r");
if(arq_acc == NULL){
printf("Error"); exit(0);}
while(pointer = fgets(buffer, sizeof(buffer), arq_acc) ){
char *token;
while( (token = strtok(pointer, ",") != NULL){
//Then I don't know how to fill the struct vector.
}
return 0;
}
I just pasted my old code to show you that I'm actually trying to do something.
I couldn't find a way to do what I need with this code, so I can get total differente code ideas to reach my goal.
In the old code, I need to restart the loop to change to the next value of the currently lane, then I can't add something like this:
while( (token = strtok(pointer, ",") ) != NULL ){
acc[0].num_acc = token;
// Now I need to set pointer as NULL and read the token again, to get the next value of the first row. But I can't do it like this. ;\
}
Since the data in the file are uniform, you can use sscanf. Keep track of an index that indicates which account from the array to fill.
sscanf(buffer, "%d,%24[^,],%lf",
&(acc[index].num_acc),
acc[index].name,
&(acc[index].value));
So what my program should do is: Read a .txt file with this piece of code.
FILE *fp;
char filename[40],part1[4],part2[4];
int c=0,pt1,pt2;
printf("\nEnter a file name to open: ");
gets(filename);
if ((fp = fopen(filename, "r"))!= NULL)
{
printf("\nThe file %s was opened successfully!",filename);
}
else
{
printf("\nThe file didnt open succesfully!");
}
And then store each line in the row string like this.
fgets(part1,4,fp);
pt1 = atoi(part1);
struct input
{
char name[20],row[30],code[3],nPieces[3],needed[3],usage[3],nUses[3];
};
struct input list[pt1];
while (c++ < pt1 )
{
fgets(list[c].row,30,fp);
printf ("\n%s", list[c].row);
}
But the problem is that after that i must take the row string and cut it into pieces (for exp the 1st line of txt was <1 Glass 2 0 9 3 1> where each number represents something) So what i want is to put the "1" into the code[3] string the "Glass" into the name[30] string etc. I tried to make it work using the isspace() scaning the row string and whenever it found a space it would copy the row array from 0-(the space - 1) using strncpy(). For some reason when ever i run tha program is stops working. Anyone that could suggest anything?
It seems you want to allocate an array of size pt1, but that won't work because this is compile time and the value of pt1 is not known.
With:
struct input
{
char name[20],row[30],code[3],nPieces[3],needed[3],usage[3],nUses[3];
};
you declare a variable, but it seems you want to define a type, so:
typedef struct input
{
char name[20],row[30],code[3],nPieces[3],needed[3],usage[3],nUses[3];
};
and then later you must malloc the memory:
struct input list= calloc(pt1, sizeof(struct input));
The statement
struct input list[pt1];
should give a compiler error (does with my compiler).
So what i want is to put the "1" into the code[3] string the "Glass"
into the name[30] string etc.
It's much easier with sscanf() rather than isspace() and strncpy():
sscanf(list[c].row, "%2s%19s%2s%2s%2s%2s",
list[c].code,
list[c].name, list[c].nPieces, list[c].needed, list[c].usage,
list[c].nUses)
I have a structure with the following definition:
typedef struct myStruct{
int a;
char* c;
int f;
} OBJECT;
I am able to populate this object and write it to a file. However I am not able to read the char* c value in it...while trying to read it, it gives me a segmentation fault error. Is there anything wrong with my code:
//writensave.c
#include "mystruct.h"
#include <stdio.h>
#include <string.h>
#define p(x) printf(x)
int main()
{
p("Creating file to write...\n");
FILE* file = fopen("struct.dat", "w");
if(file == NULL)
{
printf("Error opening file\n");
return -1;
}
p("creating structure\n");
OBJECT* myObj = (OBJECT*)malloc(sizeof(OBJECT));
myObj->a = 20;
myObj->f = 45;
myObj->c = (char*)calloc(30, sizeof(char));
strcpy(myObj->c,
"This is a test");
p("Writing object to file...\n");
fwrite(myObj, sizeof(OBJECT), 1, file);
p("Close file\n");
fclose(file);
p("End of program\n");
return 0;
}
Here is how I am trying to read it:
//readnprint.c
#include "mystruct.h"
#include <stdio.h>
#define p(x) printf(x)
int main()
{
FILE* file = fopen("struct.dat", "r");
char* buffer;
buffer = (char*) malloc(sizeof(OBJECT));
if(file == NULL)
{
p("Error opening file");
return -1;
}
fread((void *)buffer, sizeof(OBJECT), 1, file);
OBJECT* obj = (OBJECT*)buffer;
printf("obj->a = %d\nobj->f = %d \nobj->c = %s",
obj->a,
obj->f,
obj->c);
fclose(file);
return 0;
}
When you write your object, you're writing the pointer value to the file instead of the pointed-to information.
What you need to do is not just fwrite/fread your whole structure, but rather do it a field at a time. fwrite the a and the f as you're doing with the object, but then you need to do something special with the string. Try fwrite/fread of the length (not represented in your data structure, that's fine) and then fwrite/fread the character buffer. On read you'll need to allocate that, of course.
Your first code sample seems to assume that the strings are going to be no larger than 30 characters. If this is the case, then the easiest fix is probably to re-define your structure like this:
typedef struct myStruct{
int a;
char c[30];
int f;
} OBJECT;
Otherwise, you're just storing a pointer to dynamically-allocated memory that will be destroyed when your program exits (so when you retrieve this pointer later, the address is worthless and most likely illegal to access).
You're saving a pointer to a char, not the string itself. When you try to reload the file you're running in a new process with a different address space and that pointer is no longer valid. You need to save the string by value instead.
I would like to add a note about a potential portability issue, which may or may not exist depending upon the planned use of the data file.
If the data file is to be shared between computers of different endian-ness, you will need to configure file-to-host and host-to-file converters for non-char types (int, short, long, long long, ...). Furthermore, it could be prudent to use the types from stdint.h (int16_t, int32_t, ...) instead to guarantee the size you want.
However, if the data file will not be moving around anywhere, then ignore these two points.
The char * field of your structure is known as a variable length field. When you write this field, you will need a method for determining the length of the text. Two popular methods are:
1. Writing Size First
2. Writing terminal character
Writing Size First
In this method, the size of the text data is written first, followed immediately by the data.
Advantages: Text can load quicker by block reads.
Disadvantages: Two reads required, extra space required for the length data.
Example code fragment:
struct My_Struct
{
char * text_field;
};
void Write_Text_Field(struct My_Struct * p_struct, FILE * output)
{
size_t text_length = strlen(p_struct->text_field);
fprintf(output, "%d\n", text_length);
fprintf(output, "%s", p_struct->text_field);
return;
}
void Read_Text_Field(struct My_STruct * p_struct, FILE * input)
{
size_t text_length = 0;
char * p_text = NULL;
fscanf(input, "%d", &text_length);
p_text = (char *) malloc(text_length + sizeof('\0'));
if (p_text)
{
fread(p_text, 1, text_length, input);
p_text[text_length] = '\0';
}
}
Writing terminal character
In this method the text data is written followed by a "terminal" character. Very similar to a C language string.
Advantages: Requires less space than Size First.
Disadvantages: Text must be read one byte at a time so terminal character is not missed.
Fixed size field
Instead of using a char* as a member, use a char [N], where N is the maximum size of the field.
Advantages: Fixed sized records can be read as blocks.
Makes random access in files easier.
Disadvantages: Waste of space if all the field space is not used.
Problems when the field size is too small.
When writing data structures to a file, you should consider using a database. There are small ones such as SQLite and bigger ones such as MySQL. Don't waste time writing and debugging permanent storage routines for your data when they have already been written and tested.