qsorting a struct with arrays, error as output - c

The output of the program should be like this, but with each item sorted by "nummer"
nummer: 30871
namn: Eternal Darkness
pris: 39.799999
volym: 330.000000
typ: Porter och stout
stil: Imperial porter och stout
forpackning: Flaska
land: Sverige
producent: Beerbliotek AB
alkoholhalt: 11.000000(this is one item, with 10 elements)
The output im getting now is a bit messy and wrong
nummer: 1966644114
namn: £
pris: 0.000000
volym: 0.000000
typ:
stil:
forpackning:
land:
producent:
alkoholhalt: 0.000000
My guess is that NUM_ITEMS is wrong
An example from row from "varor.csv" each element is devided by a comma:
30854,Stigbergets West Coast India Pale Ale,32.90,330.00,Ale brittisk-amerikansk stil,India pale ale (IPA),Flaska, Sverige,Stigbergets Bryggeri,6.50
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((b)>(a)?(a):(b))
#define NUM_ITEMS (sizeof(items) / sizeof(items[0]))
char *oneline, *tok;
char envara[512];
char delim[] = ",";
FILE *fp;
int i;
struct vara
{
int nummer;
char namn[100];
float pris;
float volym;
char typ[100];
char stil[100];
char forpackning[20];
char land[20];
char producent[50];
float alkoholhalt;
} items[100];
if ((fp = fopen("varor.csv", "r")) == NULL)
{
fprintf(stderr, "Filen varor.csv gick inte att öppna\n");
exit(-1);
}
for (i = 0; i < 100 && fgets(envara, 512, fp); i++) {
envara[strcspn(envara, "\n")] = '\0';
oneline = strdup(envara);
...
tok = strtok(oneline, delim);
items[i].nummer = atoi(tok);
tok = strtok(NULL, delim);
strncpy(items[i].namn, tok, sizeof items[i].namn); items[i].namn[(sizeof items[i].namn) - 1] = 0;
tok = strtok(NULL, delim);
items[i].pris = atof(tok);
tok = strtok(NULL, delim);
items[i].volym = atof(tok);
tok = strtok(NULL, delim);
strncpy(items[i].typ, tok, sizeof items[i].typ); items[i].typ[(sizeof items[i].typ) - 1] = 0;
tok = strtok(NULL, delim);
strncpy(items[i].stil, tok, sizeof items[i].stil); items[i].stil[(sizeof items[i].stil) - 1] = 0;
tok = strtok(NULL, delim);
strncpy(items[i].forpackning, tok, sizeof items[i].forpackning); items[i].forpackning[(sizeof items[i].forpackning) - 1] = 0;
tok = strtok(NULL, delim);
strncpy(items[i].land, tok, sizeof items[i].land); items[i].land[(sizeof items[i].land) - 1] = 0;
tok = strtok(NULL, delim);
strncpy(items[i].producent, tok, sizeof items[i].producent); items[i].producent[(sizeof items[i].producent) - 1] = 0;
tok = strtok(NULL, delim);
items[i].alkoholhalt = atof(tok);
int nummer_sortering(const void* n1, const void* n2)
{
const struct vara *item1 = n1;
const struct vara *item2 = n2;
return item1->nummer - item2->nummer;
}
...
printf("\n\nVaror sorterade på varunummer:\n");
qsort(items, i, sizeof(struct vara), nummer_sortering);
for (i = 0; i < NUM_ITEMS; i++)
{
printf("nummer: %d\n"
"namn: %s\n"
"pris: %f\n"
"volym: %f\n"
"typ: %s\n"
"stil: %s\n"
"forpackning: %s\n"
"land: %s\n"
"producent: %s\n"
"alkoholhalt: %f\n\n",
items[i].nummer,
items[i].namn,
items[i].pris,
items[i].volym,
items[i].typ,
items[i].stil,
items[i].forpackning,
items[i].land,
items[i].producent,
items[i].alkoholhalt
);
free(oneline);
}
}
fclose(fp);
}

Related

Trying to use a double pointer struct to read a csv file and print off the entries but I'm getting "Invalid read of size 8" in C

Creating a C program to read a csv file into a double pointer struct but I'm getting a segmentation fault.
This is the struct:
typedef struct vehicle{
char *make;
char *model;
char *color;
char *license_plate;
int year;
}vehicle_t;
I'm using a single pointer and double pointer in this program to get a better understanding of using pointers:
vehicle_t *v = NULL;
vehicle_t **car;
car = calloc(1, sizeof(vehicle_t *));
int num_cars = 0;
while(!fgets(buffer, BUF_SIZE, fp)){
//car = calloc(1, sizeof(vehicle_t *));
*(car+num_cars) = calloc(1, sizeof(vehicle_t));
*((car)+num_cars) = read_csv(fp, v);
num_cars++;
//free(v->year);
free(v->make);
free(v->model);
free(v->color);
free(v->license_plate);
free(v);
}
Here is the function I created to read csv file and return that in car:
vehicle_t *read_csv(FILE *fp, vehicle_t *v){
char buffer[BUF_SIZE] = {0};
v = calloc(1, sizeof(vehicle_t));
fgets(buffer, BUF_SIZE, fp);
char *tok = strtok(buffer, ",");
(v)->year = atoi(tok);
tok = strtok(NULL, ",");
(v)->make = calloc(strlen(tok)+1, sizeof(char));
tok = strtok(NULL, ",");
strcpy((v)->make, tok);
(v)->model = calloc(strlen(tok)+1, sizeof(char));
strcpy((v)->model, tok);
tok = strtok(NULL, ",");
(v)->color = calloc(strlen(tok)+1, sizeof(char));
strcpy((v)->color, tok);
tok = strtok(NULL, ",");
(v)->license_plate = calloc(strlen(tok)+1, sizeof(char));
strcpy((v)->license_plate, tok);
return v;
}
Valgrind is telling me that I'm getting an issue here when I try to print the vehicles from double pointer car:
for(int i= 0; i<=num_cars; i++){
printf("%d %s %s (%s) LIC:%s\n", ((*car)->year), ((*car)->make), ((*car)- >model), ((*car)->color), ((*car)->license_plate));
}
Valgrind error message:
I finish off the code by freeing the car double pointer:
for(int i= 0; i<=num_cars; i++){
//free((*car)->year);
free((*car)->make);
free((*car)->model);
free((*car)->color);
free((*car)->license_plate);
free(*car);
free(car);
}
Not sure as to why I'm getting the segmentation fault. Thought I had a decent handle of pointer usage and appeared like allocated and deallocated memory correctly.
At least these problems:
Wrong pointer
(v)->make = calloc(strlen(tok)+1, sizeof(char));
tok = strtok(NULL, ","); // ????
strcpy((v)->make, tok);
Code is strange. Why allocate to a string length and then change tok before copying?
I'd expect something more like
(v)->make = calloc(strlen(tok)+1, sizeof(char));
strcpy((v)->make, tok);
tok = strtok(NULL, ",");
Research strdup() for alternative code.
Only 1 car, wrong fgets() usage
Code only allocates for 1 car
//vehicle_t **car;
//car = calloc(1, sizeof(vehicle_t *));
//int num_cars = 0;
//while(!fgets(buffer, BUF_SIZE, fp)){
// *(car+num_cars) = calloc(1, sizeof(vehicle_t));
// *((car)+num_cars) = read_csv(fp, v);
int car_n = 1000;
// n cars
vehicle_t **car = calloc(car_n, sizeof *car);
int num_cars = 0;
// v --- no !
while(num_cars < car_n && fgets(buffer, BUF_SIZE, fp)){
car[num_cars] = calloc(1, sizeof car[num_cars][0]);
car[num_cars] = read_csv(fp, v);

Almost done with a sorting based on int(I hope). I am having some final troubles with declaring a variable

This is a sample from my code that im working on. However i have been trying to understand what to input where I wrote "XXXX" in my sorting alghoritm. Any ideas? Am i doing something or right or is it all wrong. The only error that occurs is "XXXX" is undeclared, but i can not figure out the right input.(the struct name and the "if" is somewhat "hard to read", due to my limited skills in creating code on this website)
code:
#define NUM_ITEMS sizeof(items) / sizeof(items[0]) struct vara
{
int nummer;
char namn[100];
float pris;
float volym;
char typ[100];
char stil[100];
char forpackning[20];
char land[20];
char producent[50];
float alkoholhalt;
} items[100]; for (i = 0; i < 100 && fgets(envara, 512, fp); i++)
{
envara[strlen(envara) - 1] = '\0';
oneline = strdup(envara);
tok = strtok(oneline, delim);
items[i].nummer = atoi(tok);
tok = strtok(NULL, delim);
strncpy(items[i].namn, tok, (max(strlen(tok), sizeof(items[0].namn))));
tok = strtok(NULL, delim);
items[i].pris = atof(tok);
tok = strtok(NULL, delim);
items[i].volym = atof(tok);
tok = strtok(NULL, delim);
strncpy(items[i].typ, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].stil, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].forpackning, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].land, tok, min(strlen(tok), sizeof(items->land)));
tok = strtok(NULL, delim);
strncpy(items[i].producent, tok, strlen(tok));
tok = strtok(NULL, delim);
items[i].alkoholhalt = atof(tok);
int nummer_sortering(const void* n1, const void* n2)
{
items *XXXX = n1;
items *XXXX = n2;
return items->nummer - items->nummer;
}
printf("\n\nItems sorted by number:");
qsort(items, NUM_ITEMS, sizeof(items), nummer_sortering);
for (i = 0; i < NUM_ITEMS; i++)
printf("nummer: %d\n"
"namn: %s\n"
"pris: %f\n"
"volym: %f\n"
"typ: %s\n"
"stil: %s\n"
"forpackning: %s\n"
"land: %s\n"
"producent: %s\n"
"alkoholhalt: %f\n\n",
items[i].nummer,
items[i].namn,
items[i].pris,
items[i].volym,
items[i].typ,
items[i].stil,
items[i].forpackning,
items[i].land,
items[i].producent,
items[i].alkoholhalt);
Your comparing function is wrong, the compiler should have given you an error.
What kind of type is items? You cannot declare a variable with the name of
another variable. It should be:
int nummer_sortering(const void* n1, const void* n2)
{
struct vara *item1 = n1;
struct vara *item2 = n2;
return n1->nummer - n2->nummer;
}
Also doing
envara[strlen(envara) - 1] = '\0';
is not correct. If the input is longer than 511 characters,
envara[strlen(envara) - 1] won't be a newline. The best way to remove the
newline (if any) is:
envara[strcspn(envara, "\n")] = '\0';
Also
strncpy(items[i].producent, tok, strlen(tok));
You are using strncpy wrong. The point of strncpy is that you limit the
numbers of bytes to copy based on the size of the destination. You are limiting the
number bytes based on the length of the source. If it's longer than what
producent can hold, then you will have a buffer overflow. Also remember that
strncpy does not necessarily write the '\0'-terminating byte. You have to
make sure that for yourself:
size_t max = sizeof items[i].producent;
strncpy(items[i].producent, tok, max);
items[i].producent[max - 1] = '\0';
Also you should check that strtok doesn't return NULL. If it does, that
means that the line does not have the format you're looking for. In this case
you can dismiss the line and read the next one.

Sorting a struct array based on int

I'm kinda stuck when trying to sort int number. I'm trying to display the items based on its number (nummer). The int main and #includes are in the program, just couldn't copy them. Is there a preferred why to handle this issue and can someone explain it, I been trying for a while now haha. Thanks
char *oneline, *tok;
char envara[512];
char delim[] = ",";
FILE *fp;
int i;
struct vara
{
int nummer;
char namn[100];
float pris;
float volym;
char typ[100];
char stil[100];
char forpackning[20];
char land[20];
char producent[50];
float alkoholhalt;
} items[100];
if ((fp = fopen("varor.csv", "r")) == NULL)
{
fprintf(stderr, "Filen varor.csv gick inte att öppna\n");
exit(-1);
}
for (i = 0; i < 100 && fgets(envara, 512, fp); i++)
{
envara[strlen(envara) - 1] = '\0';
oneline = strdup(envara);
tok = strtok(oneline, delim);
items[i].nummer = atoi(tok);
tok = strtok(NULL, delim);
strncpy(items[i].namn, tok, (max(strlen(tok), sizeof(items[0].namn))));
tok = strtok(NULL, delim);
items[i].pris = atof(tok);
tok = strtok(NULL, delim);
items[i].volym = atof(tok);
tok = strtok(NULL, delim);
strncpy(items[i].typ, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].stil, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].forpackning, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].land, tok, min(strlen(tok), sizeof(items->land)));
tok = strtok(NULL, delim);
strncpy(items[i].producent, tok, strlen(tok));
tok = strtok(NULL, delim);
items[i].alkoholhalt = atof(tok);
printf("nummer: %d\n"
"namn: %s\n"
"pris: %f\n"
"volym: %f\n"
"typ: %s\n"
"stil: %s\n"
"forpackning: %s\n"
"land: %s\n"
"producent: %s\n"
"alkoholhalt: %f\n\n",
items[i].nummer,
items[i].namn,
items[i].pris,
items[i].volym,
items[i].typ,
items[i].stil,
items[i].forpackning,
items[i].land,
items[i].producent,
items[i].alkoholhalt
);
free(oneline);
}
fclose(fp);
}
int cmpVara(const struct vara *left, const struct vara *right)
{
// quick & dirty way to compare two ints.
return (long) left.nummer - (long) right.nummer;
}
// :
// :
qsort(items, i, sizeof(vara), cmpVara);
i is the number of elements in items.

Breaking down a string and putting it into array using strtok()

I'm writing a basic program that takes a CSV file, prints the first field, and does some numerical evaluation of the other fields.
I'm looking to put all the numerical fields into an array but every time I do this and try to access a random element of the array, it prints the entire thing
My CSV file is:
Exp1,10,12,13
Exp2,15,16,19
and i'm trying to access the second field so it prints
Exp1 12
Exp2 16
but instead I'm getting
Exp1 101213
Exp2 151619
If someone could provide some suggestions. This is my code:
#define DELIM ","
int main(int argc, char *argv[])
{
if(argc == 2) {
FILE *txt_file;
txt_file = fopen(argv[1], "rt");
if(!txt_file) {
printf("File does not exist.\n");
return 1;
}
char tmp[4096];
char data[4096];
char expName[100];
char *tok;
int i;
while(1){
if(!fgets(tmp, sizeof(tmp), txt_file)) break;
//prints the experiment name
tok = strtok(tmp, DELIM);
strncpy(expName, tok, sizeof(expName));
printf("\n%s ", expName);
while(tok != NULL) {
tok = strtok(NULL, DELIM);
//puts data fields into an array
for(i=0; i < sizeof(data); i++) {
if(tok != NULL) {
data[i] = atoi(tok);
}
}
printf("%d", data[1]);
}
}
fclose(txt_file);
return 0;
}
sample to fix
char tmp[4096];
int data[2048];
char expName[100];
char *tok;
int i=0;
while(fgets(tmp, sizeof(tmp), txt_file)){
tok = strtok(tmp, DELIM);
strncpy(expName, tok, sizeof(expName));
printf("\n%s ", expName);
while((tok = strtok(NULL, DELIM))!=NULL){
data[i++] = atoi(tok);
}
printf("%d", data[1]);
i = 0;
}
A modified code snippet:
int data[20]; // change 20 to a reasonable value
...
while (1)
{ if (!fgets(tmp, sizeof(tmp), txt_file))
break;
//prints the experiment name
tok = strtok(tmp, DELIM);
strncpy(expName, tok, sizeof(expName));
printf("\n%s ", expName);
i = 0;
tok = strtok(NULL, DELIM);
while (tok != NULL)
{ //puts data fields into an array
data[i++] = atoi(tok);
if (i == 20)
break;
tok = strtok(NULL, DELIM);
}
if (i > 1)
printf("%d", data[1]);
}

Storing Token Char Array in C

I'm having some issues trying to print out an array of characters (storage) that hold tokens (tok). Everytime I print the array I get a strange symbol.
while(1)
{
printf("repl>");
char storage [30];
char* tok;
char g;
char buffer[20];
int pos = 0, i;
size_t bufferlength = 20;
fgets(buffer,sizeof(buffer),stdin);
tok = strtok(buffer," ");
while (tok != NULL)
{
storage[pos] = tok;
tok = strtok(NULL," ");
pos++;
}
printf(" %c\n", storage[0]);
}

Resources