C CSV GLIB sort optmization - c

I was asked a question on a recent interview at Sevone but I never got a response on how I did. I only had 2 hours to complete the challenge and i could not get it to sort by gender. Here is what I started with, and even further below was my solution
// Sevone Programming Challenge!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
//! This is the sex of a human.
typedef enum { MALE, FEMALE } Gender;
//! This is a human person.
typedef struct Person {
//! The given name.
char* firstName;
//! The family name.
char* lastName;
//! The age, in calendar years.
int age;
//! The sex (see above).
Gender gender;
} Person;
/** This is the bonus function.
**/
void bonusFunction();
/** This is the core of the program.
**/
int main( int argc, char** argv ) {
// INSTRUCTIONS:
// Please refer to: http://developer.gnome.org/glib/2.30/glib-Double-ended-Queues.html
// 1. Open "people.csv".
// 2. Read in the list of people (format is "firstName lastName,age,{male|female}").
// 3. Create a "Person" for each one read.
// 4. Insert each "Person" into a GLib GQueue (this is a "double-ended queue", but think of it like a list).
// 5. Sort the list (by using "g_queue_sort") by:
// 1. Gender
// 2. Last name
// 6. Print out the list (by using "g_queue_foreach"). The format should be:
// (male/female) Last name, First name (age)
// 7. Free up all memory (we're gonna valgrind this afterward).
// Ready for the bonus?
bonusFunction();
// KTHXBYE
return( 0 );
}
/** This is the bonus function.
**/
void bonusFunction() {
//! This is the bonus array!
int arrayOfInts[] = { 1, 1, 2, 3, 5, 8, 13, 21, 34 };
// BONUS!
// 1. Loop through the bonus array and print it without using square brackets ("[" and "]").
// All done.
return;
}
Here is my solution, please let me know how to sort by gender or any other optimizations.
// Programming Challenge!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
//! This is the sex of a human.
typedef enum { MALE, FEMALE } Gender;
//! This is a human person.
typedef struct Person {
//! The given name.
char* firstName;
//! The family name.
char* lastName;
//! The age, in calendar years.
int age;
//! The sex (see above).
Gender gender;
} Person;
/** This is the bonus function.
**/
void bonusFunction();
gint sort_lastName(gconstpointer a, gconstpointer b, gpointer data) {
return strcmp( ((Person*)a)->lastName, ((Person*)b)->lastName );
}
void prt(gpointer per) {
printf("%s, %s %d \n ", ((Person*)per)->lastName, ((Person*)per)->firstName,((Person*)per)->age);
}
/** This is the core of the program.
**/
int main( int argc, char** argv ) {
puts("Starting SevOne Test\n");
// INSTRUCTIONS:
// Please refer to: http://developer.gnome.org/glib/2.30/glib-Double-ended-Queues.html
// 1. Open "people.csv".
// 2. Read in the list of people (format is "firstName lastName,age,{male|female}").
// 3. Create a "Person" for each one read.
// 4. Insert each "Person" into a GLib GQueue (this is a "double-ended queue", but think of it like a list).
// 5. Sort the list (by using "g_queue_sort") by:
// 1. Gender
// 2. Last name
// 6. Print out the list (by using "g_queue_foreach"). The format should be:
// (male/female) Last name, First name (age)
// 7. Free up all memory (we're gonna valgrind this afterward).
char buffer[100];
int counter=0;
char * token;
GQueue* q = g_queue_new();
FILE *fp;
fp=fopen("people.csv", "r");
if( fp == NULL )
{
puts("Failed to open file");
return 0;
}
while(fgets(buffer, sizeof(buffer), fp) != NULL)
{
Person *ptr_one;
ptr_one = (Person *) malloc (sizeof(Person));
// Get first name
token = strtok(buffer," ");
ptr_one->firstName=(char *)malloc(sizeof(char)*sizeof(token));
strcpy(ptr_one->firstName,token);
// Get last name
token = strtok(NULL,",");
ptr_one->lastName=(char *)malloc(sizeof(char)*sizeof(token));
strcpy(ptr_one->lastName,token);
// Get age
token = strtok(NULL, ",");
ptr_one->age=(int *)malloc(sizeof(int));
sscanf (token, "%d", &ptr_one->age);
// Get gender
token = strtok(NULL,",\n");
g_queue_push_tail(q, ptr_one);
}
// Sort list by last name
g_queue_sort(q, (GCompareDataFunc)sort_lastName, NULL);
// print the list
g_queue_foreach(q, (GFunc)prt, NULL);
if( fclose(fp) != 0 )
{
puts("Failed to close file."); /* prints !!!Hello World!!! */
return 0;
}
g_queue_free(q);
// Ready for the bonus?
bonusFunction();
// KTHXBYE
return( 0 );
}
/** This is the bonus function.
**/
void bonusFunction() {
//! This is the bonus array!
int arrayOfInts[] = { 1, 1, 2, 3, 5, 8, 13, 21, 34 };
// BONUS!
// 1. Loop through the bonus array and print it without using square brackets ("[" and "]").
// All done.
return;
}
And the CSV below
Brad Fawcett,24,male
Steve Settlemyre,29,male
Dave Hegenbarth,44,male
Cathy Colapiertro,41,female
Steve Mahoney,23,male
Dave Mulford,26,male
Doug Manley,24,male
Steve Carrington,24,male
Lauren Jordan,31,female
Tanya Bakalov,26,female

First you need to parse the gender properly :
// Get gendor
token = strtok(NULL,",\n");
if(strcasecmp(token, "female") == 0)
ptr_one->gender = FEMALE;
else
ptr_one->gender = MALE;
Then use an appropriate comparison function :
gint sort_gender(gconstpointer a, gconstpointer b, gpointer data) {
return ((Person*)a)->gender - ((Person*)b)->gender;
}
And the bonus function :
void bonusFunction() {
//! This is the bonus array!
int arrayOfInts[] = { 1, 1, 2, 3, 5, 8, 13, 21, 34 };
// BONUS!
// 1. Loop through the bonus array and print it without using square brackets ("[" and "]").
int i;
int *n = arrayOfInts;
for(i = 0; i < sizeof(arrayOfInts)/sizeof(int); i++)
printf("%d\n", *n++);
return;
}

Related

String in structure gets deleted

I'm working on the last exercise of the "Think like a computer scientist, C version" book and I have some trouble with one particular point.
The exercise consists of making a small game, where the computer picks a random value between 0 and 20 and then asks me to guess the number.
After that, the computer counts the number of tries I made and, if I get a better score than the previous party, I need to store my name and the number of tries in a structure.
My problem is the following: When I restart the game, the string value, player_name, in the structure gets somehow deleted but player_score is still there.
First, I made a "call by value" function to create the structure and then a tried with a "call by reference" but getting the same results.
I think I tried everything I could with my actual knowledge for now; so, if someone could check my code and give me some tips about what's wrong I would much appreciate it!
//HEADERS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define FALSE 0
#define TRUE 1
//TYPEDEF STRUCTS
typedef struct
{
int player_score;
char *player_name;
} HS_Player;
//FUNCTION PROTOTYPES
int Random_Value(void);
int Get_User_Choice(void);
int Check_Result(int computer, int my_choice);
int Try_Again(int game_result, int computer);
void Player_Infos(HS_Player *player_p, int score);
int Game_Restart(void);
//MAIN
int main(void)
{
int end_game;
int high_score_value = 100;
HS_Player player;
while (end_game != TRUE)
{
int computer_number = Random_Value();
printf("Guess the number between 0 et 20 chosen by the computer.\n");
int your_number = Get_User_Choice();
int result_game = Check_Result(computer_number, your_number);
int tries_to_win = Try_Again(result_game, computer_number);
printf("Number of tries: %i\n", tries_to_win);
if (tries_to_win < high_score_value)
{
Player_Infos(&player, tries_to_win );
high_score_value = player.player_score;
}
printf("Highest score: %i By: %s\n", player.player_score, player.player_name);
printf("\n");
end_game = Game_Restart();
}
return EXIT_SUCCESS;
}
//Random_Value FUNCTION
int Random_Value(void)
{
srand(time(NULL));
int x = rand();
int y = x % 20;
return y;
}
//Get_User_Choice FUNCTION
int Get_User_Choice(void)
{
int success, x;
char ch;
printf("Your Guess:\t");
success = scanf("%i", &x);
while (success != 1)
{
printf("Your input is not a number. Please try again:\t");
while ((ch = getchar()) != '\n' && ch != EOF);
success = scanf("%i", &x);
}
if (x < 0 || x > 20)
{
printf("Your input must be between 0 and 20. Please try again.\n");
Get_User_Choice();
}
return x;
}
//Check_Result FUNCTION
int Check_Result(int computer, int my_choice)
{
int check_result;
if (my_choice < computer)
{
printf("Computer number is larger!\n");
check_result = FALSE;
}
else if (my_choice > computer)
{
printf("Computer number is smaller!\n");
check_result = FALSE;
}
else if (my_choice == computer)
{
printf("It's a Match! You chose the same number than the computer.\n");
printf("\n");
check_result = TRUE;
}
return check_result;
}
//Try_Again FUNCTION
int Try_Again(int game_result, int computer)
{
int tries_befor_success = 1;
while (game_result != TRUE)
{
int your_number = Get_User_Choice();
game_result = Check_Result(computer, your_number);
tries_befor_success++;
}
return tries_befor_success;
}
//Player_Infos FUNCTION
void Player_Infos(HS_Player *player_p, int score)
{
char new_name[80];
printf("Congrats! Your made a new high score.\n");
printf("What's your name ?\t");
scanf("%s", new_name);
printf("\n");
player_p->player_score = score;
player_p->player_name = new_name;
}
//Game_Restart FUNCTION
int Game_Restart(void)
{
int quit_value;
printf("Quit Game ?\n");
printf("Press 'y' to quit or any other keys to continue.\n");
fflush(stdin);
char quit_game = getchar();
printf("\n");
if (quit_game == 'y')
{
quit_value = TRUE;
}
else
{
quit_value = FALSE;
}
return quit_value;
}
The problem is that, in your Player_Infos function, you are assigning the address of a local array to the char* player_name pointer member of the passed structure. When that function ends, the local array it used will be deleted and the pointer in the structure will be invalid. (In the case of the player_score, you don't have that problem, because the given value is copied to the structure member.)
There are several ways around this; one would be to use the strdup() function to make a copy of the local char new_name[80]; array – but that is really overkill, and you would need to manage (i.e. free()) that allocated string whenever you make a modification.
A simpler way is to make the player_name member an actual array of char and then use strcpy() to copy the local array into that member.
Better, still, with the player_name member defined as char [80], you can read directly into that (in the function), and avoid the local array completely:
typedef struct
{
int player_score;
char player_name[80];
} HS_Player;
//...
void Player_Infos(HS_Player *player_p, int score)
{
printf("Congrats! Your made a new high score.\n");
printf("What's your name ?\t");
// Read directly. Limit input to 79 chars (allowing room for null terminator).
scanf("%79s", player_p->player_name);
printf("\n");
player_p->player_score = score;
}
Also, just as a "style" tip, you may want to change the member names to just score and name, as the "player" part is implied by the structure type-name itself.
This issue you are having is that you are associating the player name pointer to a variable that goes out of scope when you leave the "player_Infos" function. What you probably would want to do is define the name as a character array in your structure and then use the "strcpy" call in your function instead. Following is a couple of code snippets illustrating that point.
//TYPEDEF STRUCTS
typedef struct
{
int player_score;
char player_name[80];
} HS_Player;
Then, in your function, use the "strcpy" call.
//Player_Infos FUNCTION
void Player_Infos(HS_Player *player_p, int score)
{
char new_name[80];
printf("Congrats! Your made a new high score.\n");
printf("What's your name ?\t");
scanf("%s", new_name);
printf("\n");
player_p->player_score = score;
strcpy(player_p->player_name, new_name);
//player_p->player_name = new_name;
}
When I tested that out, I got a name to appear in the terminal output.
Computer number is smaller!
Your Guess: 4
It's a Match! You chose the same number than the computer.
Number of tries: 8
Highest score: 4 By: Craig
FYI, you will need to include the "string.h" file.
Give that a try.
Name Update
The reason your player.player_name is not getting updated is because you can't assign a string this way in C. When doing player_p->player_name = new_name; you're actually saving in player_p->player_name the memory address of new_name.
Instead, what you want to achieve, is to copy each character of new_name to player_p->player_name and in order to achieve this, you have to change the type of prlayer_name field from char* player_name to char player_name[80], then assign it using, for example, strcpy():
#include <string.h>
// [...]
//TYPEDEF STRUCTS
typedef struct
{
unsigned int player_score;
char player_name[80];
} HS_Player;
// [...]
//Player_Infos FUNCTION
void Player_Infos(HS_Player *player_p, int score)
{
char new_name[80];
printf("Congrats! Your made a new high score.\n");
printf("What's your name ?\t");
scanf("%s", new_name);
printf("\n");
player_p->player_score = score;
strcpy(player_p->player_name, new_name);
}
Data Persistence
To make data (players info) persistent over multiple runs, you have to save the content of the struct to a file.
Example
int Save_Score(char* filename, HS_Player* player)
{
FILE* file = fopen(filename, "w");
if (file == NULL)
{
fprintf(stderr, "\nAn error occurred while opening the file\n");
return -1;
}
if (fprintf(file, "%d %s", player->player_score, player->player_name) < 0)
return -1;
fclose(file);
return 0;
}
int Load_Score(char* filename, HS_Player* player)
{
FILE* file = fopen(filename, "r");
if (file == NULL)
{
fprintf(stderr, "\nAn error occurred while opening the file\n");
return -1;
}
if (fscanf(file, "%d %79s", &player->player_score, player->player_name) < 0)
return -1;
fclose(file);
return 0;
}

Copying strings from a string array to a string struct which usually works for the first 2 strings but get this error when copying the 3rd string. C

I have this array of strings(contains 3 strings) and in this part of the program, it will copy to the struct the string the user wants. It works with the 1st two strings when the user inputs "1" or "2" but it creates this error when I input "3".
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
Array of strings (names of places):
string Laguna[3] = {"Mamplasan Toll Exit",
"Phase 5, San Jose Village",
"Milagros Del Rosario (MRR) Building – East Canopy"};
The struct:
typedef struct passengerInfo {
struct date{ // stores the date by mm/dd/yy
int month;
int day;
int year;
} date;
int tripNo; // Trip Number; "AE" will be added in print
struct name{ // Passenger Name
string firstName;
string lastName;
char middleInitial;
} name;
int idNo; // ID Number
int priotityNumber; // priority number
string dropOffPoint; // drop off point in strings
int seatNumber;
} passengerInfo;
and the error happens at this block of code:
do
{
printLagunaDropOffPoints();
scanf("%d", &dropOffPoint);
if (dropOffPoint < 1 || dropOffPoint > 3)
printf("That is not a drop-off point, please choose again.\n");
} while (dropOffPoint < 1 || dropOffPoint > 3);
strcpy(trip[tripCodeArray][passengerSeat].dropOffPoint, Laguna[dropOffPoint-1]);
specifically at strcpy(trip[tripCodeArray][passengerSeat].dropOffPoint, Laguna[dropOffPoint-1]);
I do not know what is causing the error. I am just a beginner C student and we are only allowed to use what we were taught so I can't use memory manipulation. Can someone please explain to me what's happening?
[EDIT]
As I was told, I have made a reprex of the problem. The problem seems to lie on the specific string:
"Milagros Del Rosario (MRR) Building – East Canopy"
as I have tried to change it to a different string with the same char count as I thought that maybe the string was too long but it wasn't the case.
Here is the reprex:
#include <stdio.h>
#include <string.h>
typedef char string[51];
typedef struct passengerInfo {
string dropOffPoint; // drop off point in strings
} passengerInfo;
void printDropOffPoints(void);
int main ()
{
struct passengerInfo tripStruct[20][16];
int dropOffPoint;
string DropOffPoints[3] = {"Point 1",
"Point 2",
"Milagros Del Rosario (MRR) Building – East Canopy"};
do
{
printDropOffPoints();
scanf("%d", &dropOffPoint);
if (dropOffPoint < 1 || dropOffPoint > 3)
printf("That is not a drop-off point, please choose again.\n");
} while (dropOffPoint < 1 || dropOffPoint > 3);
strcpy(tripStruct[0][0].dropOffPoint, DropOffPoints[dropOffPoint-1]);
printf("\n%s\n", tripStruct[0][0].dropOffPoint);
return 0;
}
void
printDropOffPoints(void) {
printf("[1] Drop-off point 1\n"
"[2] Drop-off point 2\n"
"[3] Drop-off point 3\n"
"Input: ");
}

Saving from a linked list to a file and loading it back

I'm having trouble loading from a file into a linked list, been trying the whole day
first of all this is my struct
typedef struct Sensor {
int id;
int intervalo;
char local[30];
char tipo[30];
//bool active;
int active;
struct Sensor* anterior;
struct Sensor* proximo;
} Sensor;
this is my save function which i think its working fine, since the file gets created and the content is there.
void gravaLista(Sensor* l) {
FILE *ficheiro;
Sensor* temp = l;
ficheiro = fopen("sensores.txt", "r+t");
if (ficheiro == NULL) {
ficheiro = fopen("sensores.txt", "w+t");
}
while (temp != NULL) {
fprintf(ficheiro, "%d%d%d%30s%30s", temp->id, temp->intervalo, temp->active,
temp->local, temp->tipo);
temp = temp->proximo;
}
fclose(ficheiro);
}
now where i cant seem to make this work regardless of what i read about it is the load function.
heres what i have atm
int CarregaTodos(Sensor** l) {
Sensor sens;
FILE *ficheiro;
int i = 0;
ficheiro = fopen("sensores.txt", "r+t");
if (ficheiro == NULL) {
printf("no file\n", "sensores.txt");
return i;
}
rewind(ficheiro);
while (fscanf(ficheiro, "%d%d%d%30s%30s", &sens.id, &sens.intervalo, &sens.active,
&sens.local, &sens.tipo) == 5) {
//novo() function returns a pointer to a new element and insereSensor adds the new element to the last position of the list
insereSensorFim(&l, novo(sens.id, sens.intervalo, sens.local, sens.tipo)); //this function inserts the new element at the end of the list
}
fclose(ficheiro);
return i;
}
the helper functions work fine outside of the load function, but when i try to print the list after loading nothing gets printed. what am i missing?
edit: ill just post the helper functions too
Sensor* novo(int id, int tempo, char* l, char* t) {
Sensor* novoSensor = (Sensor*)malloc(sizeof(struct Sensor));
//novoSensor->id = ++(*totalSens);
novoSensor->id = id;
novoSensor->intervalo = tempo;
strcpy(novoSensor->local, l);
strcpy(novoSensor->tipo, t);
novoSensor->active = 1;
novoSensor->anterior = NULL;
novoSensor->proximo = NULL;
//gravaSensor(novoSensor, (*totalSens), 1);
return novoSensor;
}
void insereSensorFim(Sensor** Lista, Sensor* novo) {
Sensor* atual = *Lista;
if ((*Lista == NULL))
(*Lista = novo);
else {
while (atual->proximo != NULL) {
atual = atual->proximo;
}
atual->proximo = novo;
novo->anterior = atual;
}
}
edit2: its fixed now, thanks to everyone who commented, you can read all the comments or just https://stackoverflow.com/a/44078897/8038340
Using printf() and scanf() properly is surprisingly hard. It's possible to do all sorts of magic with them, but you need to know how they work to be able to perform that magic.
In the example code, you make life more difficult for yourself by not including a record delimiter in the output. A newline is the conventional and simplest delimiter, but you can choose others if you wish, or no delimiter. However, if you choose no delimiter, you have to know information about the data that is not given in the question. If the strings never contain spaces, you can be less stringent in your formatting. But you must have some way of knowing where one number ends and the next one starts — you can't simply smush all the numbers together as the sample printf() format does unless they're all negative, or you add a plus sign to the positive number (%+d). There has to be some way to tell scanf() when to stop reading one and start on the next number.
This code is an elaboration of what I wrote in numerous comments. The output format uses fixed width fields; this makes it easier to read them. It does not assume there are no spaces in the strings, so it uses %29c to read 29 characters, and adds a null-terminator and removes trailing blanks via strip_blanks(). It includes code to print lists; it uses that code.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Sensor
{
int id;
int intervalo;
char local[30];
char tipo[30];
int active;
struct Sensor *anterior;
struct Sensor *proximo;
} Sensor;
static void insereSensorFim(Sensor **Lista, Sensor *novo);
static Sensor *novoSensor(int id, int tempo, char *l, char *t);
static const char *outfile = "sensores.txt";
static
void gravaLista(Sensor *l)
{
FILE *ficheiro = fopen(outfile, "w");
if (ficheiro == NULL)
{
fprintf(stderr, "Failed to open file '%s' for writing\n", outfile);
exit(1);
}
Sensor *temp = l;
while (temp != NULL)
{
fprintf(ficheiro, "%11d%11d%11d%-29.29s%-29.29s", temp->id, temp->intervalo, temp->active,
temp->local, temp->tipo);
temp = temp->proximo;
}
fclose(ficheiro);
}
/* Strip trailing blanks and null terminate string */
static inline void strip_blanks(char *data, size_t size)
{
assert(size > 0);
size_t offset = size - 1;
data[offset--] = '\0';
while (offset > 0 && data[offset] == ' ')
data[offset--] = '\0';
}
static
int CarregaTodos(Sensor **l)
{
Sensor sens;
FILE *ficheiro;
int i = 0;
ficheiro = fopen(outfile, "rt");
if (ficheiro == NULL)
{
fprintf(stderr, "Failed to open file '%s'\n", outfile);
exit(1);
}
while (fscanf(ficheiro, "%11d%11d%11d%29c%29c", &sens.id, &sens.intervalo, &sens.active,
sens.local, sens.tipo) == 5)
{
strip_blanks(sens.local, sizeof(sens.local));
strip_blanks(sens.tipo, sizeof(sens.tipo));
insereSensorFim(l, novoSensor(sens.id, sens.intervalo, sens.local, sens.tipo));
}
fclose(ficheiro);
return i;
}
static inline void str_copy(char *dst, const char *src, size_t size)
{
assert(size > 0);
strncpy(dst, src, size - 1);
dst[size - 1] = '\0';
}
static
Sensor *novoSensor(int id, int tempo, char *l, char *t)
{
Sensor *novoSensor = (Sensor *)malloc(sizeof(struct Sensor));
if (novoSensor == NULL)
{
fprintf(stderr, "Failed to allocate %zu bytes memory\n", sizeof(struct Sensor));
exit(1);
}
novoSensor->id = id;
novoSensor->intervalo = tempo;
str_copy(novoSensor->local, l, sizeof(novoSensor->local));
str_copy(novoSensor->tipo, t, sizeof(novoSensor->tipo));
novoSensor->active = 1;
novoSensor->anterior = NULL;
novoSensor->proximo = NULL;
return novoSensor;
}
static
void insereSensorFim(Sensor **Lista, Sensor *novo)
{
Sensor *atual = *Lista;
if ((*Lista == NULL))
*Lista = novo;
else
{
while (atual->proximo != NULL)
atual = atual->proximo;
atual->proximo = novo;
novo->anterior = atual;
}
}
static void print_sensor(Sensor *sensor)
{
printf("%5d %5d %1d [%-29s] [%-29s]\n", sensor->id, sensor->intervalo,
sensor->active, sensor->local, sensor->tipo);
}
static void print_sensor_list(const char *tag, Sensor *list)
{
printf("%s:\n", tag);
while (list != 0)
{
print_sensor(list);
list = list->proximo;
}
}
static void free_sensor_list(Sensor *list)
{
while (list != 0)
{
Sensor *next = list->proximo;
free(list);
list = next;
}
}
int main(void)
{
Sensor *list = 0;
print_sensor_list("Empty", list);
insereSensorFim(&list, novoSensor(10231, 23, "abc123-bothersome", "d92-x41-ccj-92436x"));
insereSensorFim(&list, novoSensor(20920, 25, "def456-troublesome", "e81-p42-ggk-81366x"));
insereSensorFim(&list, novoSensor(30476, 83, "ghi789-wearisome", "f70-q43-omm-70296x"));
print_sensor_list("After insertion", list);
gravaLista(list);
free_sensor_list(list);
list = 0;
print_sensor_list("Emptied", list);
CarregaTodos(&list);
print_sensor_list("After rereading", list);
insereSensorFim(&list, novoSensor(231, 325, "jkl012 blank laden stream", "minimum mess or cleaning"));
insereSensorFim(&list, novoSensor(6812, -11, "mno345 longer than was expected", "maximum type of untidiness at work"));
print_sensor_list("After extending", list);
free_sensor_list(list);
return 0;
}
When run, it produces the output:
Empty:
After insertion:
10231 23 1 [abc123-bothersome ] [d92-x41-ccj-92436x ]
20920 25 1 [def456-troublesome ] [e81-p42-ggk-81366x ]
30476 83 1 [ghi789-wearisome ] [f70-q43-omm-70296x ]
Emptied:
After rereading:
10231 23 1 [abc123-bothersome ] [d92-x41-ccj-92436x ]
20920 25 1 [def456-troublesome ] [e81-p42-ggk-81366x ]
30476 83 1 [ghi789-wearisome ] [f70-q43-omm-70296x ]
After extending:
10231 23 1 [abc123-bothersome ] [d92-x41-ccj-92436x ]
20920 25 1 [def456-troublesome ] [e81-p42-ggk-81366x ]
30476 83 1 [ghi789-wearisome ] [f70-q43-omm-70296x ]
231 325 1 [jkl012 blank laden stream ] [minimum mess or cleaning ]
6812 -11 1 [mno345 longer than was expect] [maximum type of untidiness at]
The output file, sensores.txt, looks like this:
10231 23 1abc123-bothersome d92-x41-ccj-92436x 20920 25 1def456-troublesome e81-p42-ggk-81366x 30476 83 1ghi789-wearisome f70-q43-omm-70296x
When split into records, that is:
10231 23 1abc123-bothersome d92-x41-ccj-92436x
20920 25 1def456-troublesome e81-p42-ggk-81366x
30476 83 1ghi789-wearisome f70-q43-omm-70296x
The integer width of 11 allows for a negative 32-bit number in each of the first two columns. If you know that the numbers are smaller, you can reduce the space used. In the scanf(), you could omit the lengths on the integer fields; it would work the same because numeric formats automatically skip white space. The printf() could add newlines; the scanning code needn't change at all because scanf() doesn't care about newlines when it is expecting a number (or a string — only %c, %[…] scan sets, and %n do not skip leading white space).
You could also arrange for some character that won't appear in the character strings (perhaps Control-A, '\1') to separate the strings. Then the scanning code could look for that and you could have variable length output.
Left to my own devices, I'd probably use a variable-length record with newline for the record delimiter, and a suitable field separator for the two strings, and a less rigid scanf() format. I'd read the lines with fgets() or POSIX
getline() and then scan the lines using
sscanf(). This would work nicely unless you can have newlines in your strings.
As I put it recently in another answer — lightly paraphrased:
Read the POSIX specification of printf() and scanf() for the full details. They do have some (clearly marked) extensions over standard C printf() and scanf(), but they serve for both POSIX and standard C. Then re-read them. And re-re-read them. And do that daily for a week, and then weekly for a month, and then monthly for a year, and then yearly ever after. It will repay the effort.
fprintf(ficheiro, "%d%d%d%30s%30s"... I suggest you put a delimiter, say coma or #. Imagine, your id is 11, intervalo is 10, when saved, it's 1110. How do you know, when reading from the file, the id is 11 instead of 1 or 111?
change insereSensorFim(&l, to insereSensorFim(l,
In insereSensorFim, you use a while loop to find the tail, it's not efficient. Let *Lista always points to the tail and skip the loop. For example,
void insereSensorFim(Sensor** tail, Sensor* novo) {
if (*tail != NULL)
{
(*tail)->proximo = novo;
novo->anterior = (*tail);
}
*tail = nova;
}
You have to separate your integers when writing to the file or else they will just look like one big number to the reading function.
You could try replacing fprintf(ficheiro, "%d%d%d%30s%30s", ...); with fprintf(ficheiro, "%d;%d;%d;%29s;%29s", ...); (29 instead of 30 because you don't write the string terminating '\0') and then should be able to read back with fscanf(ficheiro, "%d;%d;%d;%29s;%29s", ...);.
EDIT:
After writing a smaller test code and some debugging, I figured out that if you want to use %s in the formatting of fscanf() so that the white space is stripped of the end of the strings and they're \0 terminated for you, then this would work:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void test_save(void)
{
FILE *ficheiro;
ficheiro = fopen("sensores.txt", "r+t");
if (ficheiro == NULL) {
ficheiro = fopen("sensores.txt", "w+t");
}
fprintf(ficheiro, "%d;%d;%d;%-29s ;%-29s\n", 12, 138, 131,
"Local_test", "Tipo_test");
fprintf(ficheiro, "%d;%d;%d;%-29s ;%-29s\n", 21, 218, 213,
"Local_test_2", "Second_tipo_test");
fclose(ficheiro);
}
void test_read(void)
{
FILE *ficheiro;
ficheiro = fopen("sensores.txt", "r+t");
if (ficheiro == NULL) {
printf("no file %s\n", "sensores.txt");
return;
}
int id, intervalo, active;
char local[30], tipo[30];
while (fscanf(ficheiro, "%d;%d;%d;%29s ;%29s\n", &id, &intervalo, &active,
local, tipo) == 5) {
printf("id: %d intervalo: %d active: %d\tlocal: [%s]\ttipo: [%s]\n",
id, intervalo, active, local, tipo);
}
fclose(ficheiro);
}
int main(void)
{
test_save();
test_read();
}
Output of this test program:
id: 12 intervalo: 138 active: 131 local: [Local_test] tipo: [Tipo_test]
id: 21 intervalo: 218 active: 213 local: [Local_test_2] tipo: [Second_tipo_test]
As seen by the file writen by this test-program, each record is writen in one line:
12;138;131;Local_test ;Tipo_test
21;218;213;Local_test_2 ;Second_tipo_test

Structure pointer declared in function

Here is the part of the code that isnt working for me.I am declaring a pointer to a structure and I try to use it on a function,although c says that it cant convert main person to person.
void display (char *s2,FILE *f1,int max);
void insert (FILE *f1, struct person *p1);
void deletestring (FILE *f1,FILE *f2,char *s2,char *s1,char *file1,char *file2,int max);
void edit (FILE *f1,FILE *f2,char *s2,char *s1,char *file1,char *file2,struct person *p1,int max);
int main ()
{
char s1[MAX],s2[MAX];
FILE *f2,*f1;
struct person
{
char id[MIN];
char emer[MIN];
char mbiemer[MIN];
};
struct person p1;
struct person *pp1;
pp1 = &p1;
char *file1 = "f1.txt";
char *file2 = "f2.txt";
int zgjedhja=1;
printf("Programi funksionon sipas shpjegimit \n :");
printf("Shtypni 1 per te shtuar nje person \n Shtypni 2 per te ndryshuar informacionin e nje personi \n Shtypni 3 per te shfaqur te dhenat \n Shtypni 4 per te fshire nje person \n Shtypni -1 per te dale nga programi \n ");
while (zgjedhja != -1 )
{
printf("Jepni zgjedhjen tuaj \n ");
scanf(" %d " , & zgjedhja );
switch (zgjedhja)
{
case 1:
f1=fopen(file1,"a");
insert (f1,pp1);
The type struct person has main scope, so it won't have the same meaning in insert. In fact, a structure type whose members have not been specified is known as an incomplete type. To remove this error, declare your structure outside of your function.
struct person
{
/* ... */
};
/* Function declarations. */
int main (void)
{
/* ... */
}

how to best achieve string to number mapping in a c program

I have a definite set of strings and its corresponding numbers:
kill -> 1
live -> 2
half_kill -> 3
dont_live -> 4
List is of 30 such strings and their number mapping.
If user enters "kill", I need to return 1 and if he enters "dont_live" I need to return 4.
How should I achieve this in c program? I am looking for an efficient solution because this operation needs to be done 100s of times.
should I put them in #define in my .h file?
Thanks in advance.
Sort your table, and use the standard library function bsearch to perform a binary search.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct entry {
char *str;
int n;
};
/* sorted according to str */
struct entry dict[] = {
"dont_live", 4,
"half_kill", 3,
"kill", 1,
"live", 2,
};
int compare(const void *s1, const void *s2)
{
const struct entry *e1 = s1;
const struct entry *e2 = s2;
return strcmp(e1->str, e2->str);
}
int
main (int argc, char *argv[])
{
struct entry *result, key = {argv[1]};
result = bsearch(&key, dict, sizeof(dict)/sizeof(dict[0]),
sizeof dict[0], compare);
if (result)
printf("%d\n", result->n);
return 0;
}
Here's what you get when you run the program.
$ ./a.out kill
1
$ ./a.out half_kill
3
$ ./a.out foo
<no output>
PS: I reused portions of sidyll's program. My answer should now be CC BY-SA compliant :p
A possible solution:
#include <stdio.h>
#include <string.h>
struct entry {
char *str;
int n;
};
struct entry dict[] = {
"kill", 1,
"live", 2,
"half_kill", 3,
"dont_live", 4,
0,0
};
int
number_for_key(char *key)
{
int i = 0;
char *name = dict[i].str;
while (name) {
if (strcmp(name, key) == 0)
return dict[i].n;
name = dict[++i].str;
}
return 0;
}
int
main (int argc, char *argv[])
{
printf("enter your keyword: ");
char s[100]; scanf("%s", s);
printf("the number is: %d\n", number_for_key(s));
return 0;
}
Here's one approach:
int get_index(char *s)
{
static const char mapping[] = "\1.kill\2.live\3.half_kill\4.dont_live";
char buf[sizeof mapping];
const char *p;
snprintf(buf, sizeof buf, ".%s", s);
p = strstr(mapping, buf);
return p ? p[-1] : 0;
}
The . mess is to work around kill being a substring of half_kill. Without that issue you could simply search for the string directly.
If it is a very short list of strings then a simple block of ifs will be more than sufficient
if (0 == strcmp(value, "kill")) {
return 1;
}
if (0 == strcmp(value, "live")) {
return 2;
}
...
If the number approach 10 I would begin to profile my application though and consider a map style structure.
if you have a fixed set of strimgs, you have two options: generate a perfect hashing function (check gperf or cmph) or create a trie so that you never have to check charcters more than once.
Compilers usually use perfect hashes to recognize a language keyword, in your case I would probably go with the trie, it should be the fastest way (but nothing beats direct measurement!)
Is it really a bottleneck? You should worry about efficiency only if the simple solution proves to be too slow.
Having said that, possible speed improvements are checking the lengths first:
If it's 4 characters then it could be "kill" or "live"
If it's 9 characters then it could be "half_kill" or "dont_live"
or checking the first character in a switch statement:
switch (string[0]) {
case 'k':
if (strcmp(string, "kill") == 0)
return 1;
return 0;
case 'l':
...
default:
return 0;
}
Use hashmap/ hashtable i think this would be the best solution.
Can you use an Enumunerator?
int main(void) {
enum outcome { kill=1, live, half_kill, dont_live };
printf("%i\n", kill); //1
printf("%i\n", dont_live); //4
printf("%i\n", half_kill); //3
printf("%i\n", live); //2
return 0;
}
Create a list of const values:
const int kill = 1;
const int live = 2;
const int half_kill = 3;
etc

Resources