parse a string for the required value - c

I have a data as shown below, it contains the name of person and age respectively, her i have shown just 3 person names and the respective age, i can also have many names with respective age in that string.
I want to parse this string and store the name and corresponding age in a structure, can u please tell me how to parse this name and age. Ex: I want to get Allan 35 and store it in the below structure in name and age field respectively.So and forth for all the names present in that string.
How to parse the string, i tried strstr() but it was of no use for me. Pls can anyone tell how to parse this string.
struct data_base{
char *name;
int age;
};
char data[] = "Name Allan Age 35 Name John Age 50 Name Jim Age 20 ....."

You can use strtok to extract the tokens.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct data_base{
char *name;
int age;
};
#define NAME_TAG "Name "
#define AGE_TAG "Age "
char* getName(char **p){
int tag_size = strlen(NAME_TAG);
if(strncmp(*p, NAME_TAG, tag_size)==0){
char *agep = strstr(*p += tag_size, AGE_TAG);
int len = agep - *p -1;//-1: for space before "Age"
char *name = calloc(len + 1, sizeof(char));
strncpy(name, *p, len);
*p = agep;
return name;
}
return NULL;
}
int getAge(char **p){
int tag_size = strlen(AGE_TAG);
if(strncmp(*p, AGE_TAG, tag_size)==0){
int age = (int)strtol(*p += tag_size, p, 10);
if(**p == ' ')
*p += 1;
return age;
}
return -1;
}
struct data_base *parse(char *data, size_t *size){
char *p = data;
struct data_base *dbp=NULL;
*size = 0;
while(*p){
*size += 1;
dbp = realloc(dbp, sizeof(struct data_base)*(*size));
dbp[*size -1].name = getName(&p);//this is treated as format is correct
dbp[*size -1].age = getAge(&p);
}
return dbp;//realloc(dbp, sizeof(struct data_base)*(*size));
}
int main(void){
char data[] = "Name Allan Age 35 Name John Age 50 Name Jim Age 20";
struct data_base *dbp;
size_t i, size;
dbp = parse(data, &size);
//check print
for(i = 0; i < size ;++i){
printf("Name: %s, Age: %d\n", dbp[i].name, dbp[i].age);
}
//deallocate
return 0;
}

You might want to use sscanf. You'll need %s format for person's name and %d for age.

u can use two pointer to get the begin and end of a word,
use pointer p by strstr to get the place of "Name", then forth 5 to get the begin place of the name , then pointer q by strchr with space to get the end of the name, then you can use strncpy of snprintf to get the exactly name

Create function to handle each record, using sscanf().
// Scan string, return >0 if successful, 0 if done, else error code
int JZ_ScanDB(const char *data, int *Index, struct data_base *Dest) {
if (Dest) {
Dest->age = 0; // Form default answer
Dest->name = 0;
}
if (!data || !Index || !Dest) return -1; // Gremlins in your code
if ((*Index < 0) || (*Index > strlen(data))) return -2; // More gremlins
if (data[*Index] == '\0') return 0; // we are done
char Name[1000];
int retval = sscanf(&data[*Index], "Name %s Age %d %n", Name, &Dest->age, Index);
if (retval != 2) {
return -3;
}
Dest->name = strdup(Name);
return 0;
}
void DoIt() {
char data[] = "Name Allan Age 35 Name John Age 50 Name Jim Age 20";
int i = 0;
struct data_base Person;
int ret;
while ((ret = JZ_ScanDB(data, &i, &Person)) > 0) {
// do something with Person, watch for memory management
}
if (ret) {
// deal with unusual reason for stopping
}
}

Related

C - "error: Cannot access memory at address" occurs

#include <stdio.h>
#include <string.h>
typedef struct birth{
char *name;
char time[12];
}birth;
void swap(struct birth *a, struct birth *b){
struct birth tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int main(){
int n;
birth list[100], *p, *q;
scanf("%d", &n);
getchar();
for(p = list; p < list + n; p++){
scanf("%s %s", &p->name, &p->time);
}
for(p = list; p < list + n - 1; p++){
for(q = p + 1; q < list + n; q++){
if(strcmp(p->time, q->time) > 0){
swap(p ,q);
}
else if(strcmp(p->time, q->time) == 0){
if(strcmp(p->name, q->name) > 0){
swap(p ,q);
}
}
}
}
for(int i = 0; i < n; i++){
printf("%s %s\n", list[i].name, list[i].time);
}
return 0;
}
I am solving the problem of receiving n, which means the number of students, repeating the number of students, receiving the student's name and date of birth, and printing the names in advance if the date of birth is the same.
However, there was no answer, so I checked using the debugger in vcode, and when I received the input, the date of birth was well entered, but the name was not.
You are trying to read a string using a char pointer that was never initialized.
typedef struct birth{
char *name;
char time[12];
}birth;
...
scanf("%s %s", &p->name, &p->time); // error, &p->name points to nowhere
You should either allocate memory yourself or declare it as a fixed size char array. It would be best to check the string boundaries too:
#define S_NAME 12
#define S_TIME 12
typedef struct birth{
char name[S_NAME];
char time[S_TIME];
}birth;
...
// read string with safety guard
if (fgets(p->name, S_NAME, stdin) != NULL) {
// read name successfully
}
if (fgets(p->time, S_TIME, stdin) != NULL) {
// read time successfully
}

How to allocate memory in struct for char* fields

How to allocate memory for my char * fields in struct ?
My struct:
struct student{
int score;
char* name;
char* surname;
};
int main(){
struct student st[];
int i;
int n = 5;
for(i = 0; i < n; i++){
printf("Score: \n");
scanf("%d", &st[i].score);
printf("Name \n");
scanf("%s", &st[i].name);
printf("Surname \n");
scanf("%s",&st[i].surname)
}
}
How to malloc to char* name and char* surname ?
I must have an array of struct in form struct student st[].
I don't know, how do this rationally.
void initialise_student( struct student *st, char* name, char* surname)
{
st->name = ( strlen( name ) + 1);
st->surname = (strlen( surname ) +1 );
}
int main(){
int i;
int n = 5;
struct student *st[n] = initialise_student();
for(i = 0; i < n; i++){
printf("Score: \n");
scanf("%d", &st[i].score);
printf("Name \n");
scanf("%s", &st[i].name);
printf("Surname \n");
scanf("%s",&st[i].surname);
}
How to match this ?
For example
#include <stdlib.h>
#include <string.h>
//...
struct student st[1];
char *name = "Marek";
char *surname = "Piszczaniuk";
st[0].name = malloc( strlen( name ) + 1 );
strcpy( st[0].name, name );
st[0].surname = malloc( strlen( surname ) + 1 );
strcpy( st[0].surname, surname );
st[0].score = 100;
You can write separate functions to set the data members name and surname for an element of the array.
For example
_Bool set_name( struct student *st, const char *name )
{
st->name = malloc( strlen( name ) + 1 );
_Bool success = st->name != NULL;
if ( success )
{
strcpy( st->name, name );
}
return success;
}
You need to write an initialise_student(struct student* s) function which calls malloc on the char* members. Perhaps this function also takes the name and surname char* pointers of which you take deep copies?
You then call this function with every member of the st array.
Don't forget to build a corresponding free_student(struct student* s) function.
If you are already using scanf, you can tell the function to allocate the sting for you by using %ms instead of %s, like this:
char *name;
if (scanf("%ms", &name) != 1) {
fprintf(stderr, "error: could not read name\n");
exit(1);
}
EDIT This may not be a valid solution—I forgot that %ms is in POSIX only, not in ISO C.

Proper way to set array values in C with doubling scheme?

I am reading in a file of locations with coordinates, city names and country names. Currently I am just testing to see if i can store the first element of each line in my array. The fallowing is a sample of the file i am reading in:
Durban, South Africa
29 53 S
30 53 E
The trouble i am having is that when I try to store the first element of each line in my array the same value gets stored for every element in the array. Code i have so far is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kml.h"
#define LEN 128
struct quard_t {
char *city;
char *state;
char *country;
int longitude;
int latitude;
};
struct data_t {
int nval;
int max;
struct quard_t *data;
};
enum {INIT = 1, GROW = 2};
int main(int argc, char **argv)
{
char buf[LEN];
char *str;
int cnt = 0;
FILE *in = fopen(argv[1], "r") ;
struct data_t *data = malloc(sizeof(struct data_t));
data->nval = INIT;
data->max = INIT;
data->data = NULL;
while (fgets(buf, LEN, in)) {
if (data->nval > data->max){
data->data = realloc(data->data, GROW * data->max *sizeof(struct quard_t));
data->max = GROW * data->max;
}
else if (data->data == NULL)
data->data = malloc(INIT * sizeof(struct quard_t));
str = strtok(buf, " ");
data->data[cnt].city = str;
cnt++;
}
int i = 0;
for ( ; i < cnt; i++ ){
printf("%d: %s\n", i, data->data[i].city);
}
fclose(in);
return 0;
}
The fallowing is the output i am getting, the numbers being the index of the array and everything after being what is stored in the array:
190: 30
191: 30
192: 30
193: 30
194: 30
When you are assigning a value to city:
data->data[cnt].city = str;
All you are doing is assigning a pointer, not the actual data currently stored in str. So when you overwrite str later, city is pointing to the latest value of str. To fix this you need to allocate space for city when you allocate space for the quard_t structure. Then copy the string into this new buffer using strcpy. You'll have to do the same for the state and country fields.
Also, your data structure isn't really a linked list. You've really just created your own quasi-vector structure. A true linked list has the data members plus a pointer to the structure itself. I suggest you do a little research on linked list implementations.

C array of structs segmentation fault

I am trying to make a dynamic array of structs, and I can successfully add one struct to it. But any more structs I add cause a segmentation fault. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 4
struct Person {
char *first_name;
char *last_name;
unsigned int age;
};
int add_person(struct Person **people, size_t *people_size, size_t *population, struct Person p) {
if ((sizeof(struct Person) * *population) > *people_size) {
return -1;
}
if ((sizeof(struct Person) * (*population + 1)) >= *people_size) {
*people_size = *people_size + sizeof(struct Person) * PEOPLE_BLOCK;
*people = realloc(*people, *people_size);
if (!*people) {
return -1;
}
}
*people[*population] = p;
++*population;
return 0;
}
int main(int argc, char const *argv[]) {
size_t population;
size_t people_size;
struct Person *people, timn, batman;
population = 0;
people_size = sizeof(struct Person) * PEOPLE_BLOCK;
people = malloc(people_size);
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add_person(&people, &people_size, &population, timn);
printf("Person 0's first name: %s\n", people[0].first_name);
batman.first_name = "Bat";
batman.last_name = "Man";
batman.age = 42;
add_person(&people, &people_size, &population, batman);
printf("Person 1's first name: %s\n", people[1].first_name);
free(people);
return 0;
}
I'd appreciate any help on why this is happening, thanks!
The problem resides with this line :
*people[*population] = p;
Change it to:
(*people)[*population] = p;
Why are the parenthesis requried?
The compiler has rules of operator precedence. When applying them, it sees your code as this:
*(people[*population]) = p;
which is not what you intended. Given a pointer-to-pointer Type **pp,
*pp[n] = value;
means "take the n'th pointer starting at pp, and assign value at the location dereferenced from the address that pointer holds. In other words, it means essentially this:
Type *p = pp[n];
*p = value;
What you really want is something that does this:
Type *p = *pp;
p[n] = value;
and that is what (*pp)[n], distinguishing the dereference of the pointer to pointer, gives you. Without that, you're using an invalid pointer, leading to your fault.
Not sure whether this answer will help, but anyway.
I don't understand your code, what you are trying to do.
You directly use the number of elements, a pointer to the first person, and the maximum number of elements. You'll probably have a lot of problems passing that all around.
You're storing literal strings directly in your structs, which means that in a real case (using no literals) that would result in memory leaks.
Here is my take. I've made PEOPLE_BLOCK smaller for testing reasons.
Hope this helps.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 2
typedef struct _Person {
char *first_name;
char *last_name;
unsigned int age;
} Person;
typedef struct _VectorPeople {
Person * people;
size_t num;
size_t max;
} VectorPeople;
void init(VectorPeople *v)
{
v->max = PEOPLE_BLOCK;
v->num = 0;
v->people = (Person *) malloc( sizeof(Person) * v->max );
}
void clear(VectorPeople *v)
{
// Clear persons
Person * it = v->people;
while( ( it - v->people ) < v->num ) {
free( it->first_name );
free( it->last_name );
++it;
}
// Clear vector
v->max = v->num = 0;
free( v->people );
v->people = NULL;
}
void add(VectorPeople *v, Person *p)
{
// Reserve
if ( v->num >= v->max ) {
v->max += PEOPLE_BLOCK;
// Realloc
v->people = realloc( v->people, v->max * sizeof(Person) );
if ( v->people == NULL ) {
exit( -1 );
}
}
// Copy strings
p->first_name = strdup( p->first_name );
p->last_name = strdup( p->last_name );
// Insert
v->people[ ( v->num )++ ] = *p;
}
int main(int argc, char const *argv[]) {
VectorPeople vp;
Person timn;
Person batman;
Person bond;
Person superman;
init( &vp );
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add( &vp, &timn );
batman.first_name = "Batn";
batman.last_name = "Man";
batman.age = 42;
add( &vp, &batman );
bond.first_name = "James";
bond.last_name = "Bond";
bond.age = 45;
add( &vp, &bond );
superman.first_name = "Super";
superman.last_name = "Man";
superman.age = 45;
add( &vp, &superman );
int i = 0;
for(; i < vp.num; ++i ) {
printf( "Person: %s, %s.\n", vp.people[ i ].last_name, vp.people[ i ].first_name );
}
clear( &vp );
return 0;
}
There were a number of errors in your code. One thing to keep in mind, when you dynamically allocate memory, you are responsible for keeping track of it and freeing it when you no longer need it (otherwise, you will leak memory like a sieve).
In your code, you attempt to create an array of structs holding pointer to an array of characters. The char * pointers are NOT allocated and cannot simply be assigned in the manner you attempt. strdup can help, but you have just allocated memory, so free it when you are done with it.
Attempting to allocate an array of structs with varying (unknown) lengths of first_name and last_name requires that you keep track of every allocation. In some sense, you are better off declaring people as pointer to pointer to Person This allows iteration over your people without having to store the population somewhere allowing you to iterate until the first NULL pointer is encountered.
Likewise, creating a typedef to your struct can greatly cut down on the number of times you write sizeof (struct Person). It keeps the code clean and helps you think though the pointer haze.
Here is an example using a pointer-to-pointer-to-struct of what I think you intended to do. It is followed below by an implementation using only a pointer to struct. Evaluate both and decide which implementation you prefer:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max);
Person **realloc_person (Person **ppl, size_t *n);
void free_person (Person *p);
void free_person_names (Person *p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person **people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (&timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0]->first_name, people[0]->last_name, people[0]->age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (&batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1]->first_name, people[1]->last_name, people[1]->age);
for (i = 0; i < population; i++)
free_person (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
if (!((*ppl)[*pop] = malloc (sizeof ***ppl)))
return NULL;
size_t i = (*pop)++;
(*ppl)[i]-> first_name = strdup (p.first_name);
(*ppl)[i]-> last_name = strdup (p.last_name);
(*ppl)[i]-> age = p.age;
return (*ppl)[i];
}
/* realloc an array of pointers to Person setting memory to 0. */
Person **realloc_person (Person **ppl, size_t *n)
{
Person **tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free memory for a Person */
void free_person (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
free (p);
}
/* free only names of Person (for temp structs) */
void free_person_names (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
}
Note: updated to correct ppl start address on reallocation.
Using only Array of Person
While not inherently different than using a pointer to pointer to Person using a simple pointer to Person eliminates the ability to iterate over your array until a NULL or (empty) pointer is encountered. The following is an implementation of the same code using only an array of Person:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max);
Person *realloc_person (Person *ppl, size_t *n);
void free_person_names (Person p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person *people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0].first_name, people[0].last_name, people[0].age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1].first_name, people[1].last_name, people[1].age);
for (i = 0; i < population; i++)
free_person_names (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
size_t i = (*pop)++;
(*ppl)[i].first_name = strdup (p.first_name);
(*ppl)[i].last_name = strdup (p.last_name);
(*ppl)[i].age = p.age;
return ppl[i];
}
/* realloc an array Person setting memory to 0. */
Person *realloc_person (Person *ppl, size_t *n)
{
Person *tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free only names of Person (for temp structs) */
void free_person_names (Person p)
{
if (p.first_name) free (p.first_name);
if (p.last_name) free (p.last_name);
}
Output
$ ./bin/struct_add_person
Person 0
first name: Timn
last name : Timothy
age : 38
Person 1
first name: Bat
last name : Man
age : 42
One problem is the last argument of add_person() to be specific, the argument '(struct Person) p'. When 'timn' and 'batman' are passed into the add_person() function, they are passed as a copy of the original structure. In the add_person() structure, that data is actually on the stack and is volatile outside the scope of the function. Try changing the last argument to a pointer.

Making an array of tokens and comparing it with another string

I have an *input string from a console. That string might look like: show name year xxx.. and I need an output to look like this:
name: Adi
year: 1994 (for example)..
I have been trying to achieve this by using strtok() function, but I also need to compare every tokon with allowed keyywords(name, year...) if that word is not allowed, than the token needs to be skiped(deleted).. for example in this case it would skip show, and xxx.
Another problem is that I need those tokens in a form of an array in order to work with them and with a structs..
There should be no limit to number of words that could be entered in an input..
I hope you understood what I asked.. so, how to make tokens from a string using strtok or something else and make them be arrays or pointers, and how to compare those tokens with another string ( for example constant: #define NAME "name") and of there are some other inputs to skip(delete) them..
I would really appreciate it if you could help me with this.. Thanks..
I would avoid the array. It provides unnecessary overhead. What you're asking for could be accomplished with something like this:
void parseString(char * string) {
char * name = NULL;
char * year = NULL:
char * ptr = strtok(string, " ");
while (ptr != NULL) {
if (stricmp(ptr, "name") == 0) {
ptr = strtok(ptr, " ");
name = ptr;
/* do whatever with name */
} else if (stricmp(ptr, "year") == 0) {
ptr = strtok(ptr, " ");
/* do whatever with year */
year = ptr;
} /* else if ... */
ptr = strtok(ptr, " ");
}
This gives you a fair amount of flexibility. You check all the terms you need, you don't need to worry about how to allocate the array, and you can access values for settings if necessary.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
char *tolowerstr(char *str){
char *p = str;
while((*p++ = tolower(*p)));
return str;
}
int cmp(const void *a, const void *b){
return strcmp(*(const char **)a, *(const char **)b);
}
bool isBanWord(const char *word){
static const char *table[] =
{ "fuck", "show", "xxx" };//sorted
char **ret, *key;
key = tolowerstr(strdup(word));
ret=bsearch(&key, table, sizeof(table)/sizeof(*table), sizeof(*table), cmp);
free(key);
return !!ret;//ret != NULL ? true : false;
}
//Create and return as a dynamic array of pointer to the copy of the word from a string.
//String passed is destroyed.
char **strToWords(char *str, size_t *size){
const char *delimiters = " .";
size_t count=0;
char **array = malloc(strlen(str)*sizeof(char*));//number of words < string length
if(array){
char *token=strtok(str, delimiters);
for(; token ;token=strtok(NULL, delimiters)){
if(!isBanWord(token))//skip ban word
array[count++] = strdup(token);
}
array[count] = NULL;//End mark
array=realloc(array, (count + 1)*sizeof(*array));//include NULL
}
*size = count;
return array;
}
typedef struct words {
char **words;
size_t n; //number of words
} Words;
void clearWords(Words *w){
size_t i;
for(i=0;i < w->n;++i)
free(w->words[i]);
free(w->words);
w->words = NULL;
w->n = 0;
}
void printWords(Words *w){
size_t i=0;
while(i < w->n){
printf("%s", w->words[i++]);
if(w->words[i])
putchar(' ');
}
putchar('\n');
}
int main(){//DEMO
char sentence[] = "show name year xxx.";//input string. Will be destroyed.
Words w;
w.words = strToWords(sentence, &w.n);
printWords(&w);//name year
clearWords(&w);
return 0;
}

Resources