Add String nodes to an Expression Tree - c

I've spent many days trying to add strings recursively from a prefix expression like: + 1.5 * 13 2.5 inside a binary tree. I'm using the strtok function to separate elements of the string, but then how can I add the elements to the tree?
My code is very similar to the GeeksForGeeks example: https://www.geeksforgeeks.org/building-expression-tree-from-prefix-expression/, but here they only add characters as data on a node.
typedef struct node {
char * data;
struct node *left, *right;
} node;
// Function to recursively build the expression tree
char* add(node** p, char* a)
{
// If its the end of the expression
if (*a == '\0')
return '\0';
while (1) {
char* q = "null";
if (*p == NULL) {
// Create a node with *a as the data and
// both the children set to null
node* nn = (node*)malloc(sizeof(node));
nn->data = *a;
nn->left = NULL;
nn->right = NULL;
*p = nn;
}
else {
// If the character is an operand
if (*a >= '0' && *a <= '9') {
return a;
}
// Build the left sub-tree
q = add(&(*p)->left, a + 1);
// Build the right sub-tree
q = add(&(*p)->right, q + 1);
return q;
}
}
}
int main()
{
node* s = NULL;
char a[] = "3.5 + 4.7";
// (...) tokens
add(&s, str);
return 0;
}
Thank you very much for your help.

In the example of geeksforgeeks they use char data in the struct. But at your side, you use char * data, so you should use strcpy to copy data from strtok to data of struct. In this case, you have to allocate the memory for thedata of each node. If you do not want to allocate, you can change the char * data in struct node to char data[20].
For example:
node* nn = malloc(sizeof(node));
if(!nn) {
//handle the error
}
nn->data = malloc((sizeof(char)*20) // For example max size of data is equal to 20
char * token = strtok(str, delim); // you choose delim as you want to split the string str
While(token!=NULL) {
strcpy(nn->data, token);
token = strtok(NULL, delim);
}

Related

Convert a linked list to an array of characters

I wrote down this code in C to convert a linked list where each node contains a character and convert that list into a string. This is my code
struct node {
unsigned char bit : 1;
struct node *next;
};
//Converts the linked list into a String
char *list_to_bitstring( struct node *head ) {
struct node *countNode = head;
int count = 0;//Counts number of nodes
while ( countNode != NULL ) {
count++;
countNode = countNode->next;
}
char *result = (char *)malloc( sizeof( count + 1 ) );
struct node *temp = head;
int i = 0;
while ( temp != NULL ) {
result[i] = temp->bit;
i++;
temp = temp->next;
}
result[i] = '\0';
return result;
}
//main method
int main() {
struct node *head1 = bitstring_to_list( "111" ); //Converts a String into a linked list
char *result = list_to_bitstring( head1 );
printf( "%s", &result );
return 0;
}
But the output is this-
│
I'm not sure why I'm getting this output. Any advice would be appreciated
From the comments under the question, there are two problems in the code:
The printf is printing the address of the pointer, not the string itself. The printf should be printf("%s\n", result);
The elements of the string need to be converted to the characters '0' and '1'. This can be done by adding '0' to each element of the string, e.g. result[i] = temp->bit + '0';

Linked lists Node being overwritten incorrectly

I am creating a music library program in C using linked lists. Users should be able to create a node containing: song name, artists, and genre. These nodes should be sorted alphabetically by song name.
I am having trouble, however, creating new nodes without affecting my current head node. The picture below will illustrate this better. I call a line of code that is meant to receive a new value from the user and it resets the song name value in the head node and I have no clue why. Any help is appreciated.
(Problem occurs between Test Point 1 and Test Point 2 printf statements, I intend for Test Point 2 to display "zz" instead of "aa").
Code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
typedef struct node
{
char* artist;
char* songName;
char* genre;
struct node* nextNode;
} Node;
const int MAX_LENGTH = 1024;
void inputStringFromUser(char prompt[], char s[], int arraySize);
int main(void)
{
// Declare the head of the linked list.
// ADD YOUR STATEMENT(S) HERE
Node* head = NULL;
// Announce the start of the program
printf("Personal Music Library.\n\n");
printf("%s",
"Commands are I (insert), D (delete), S (search by song name),\n"
"P (print), Q (quit).\n");
char response;
char input[MAX_LENGTH + 1];
char input4[MAX_LENGTH + 1];
char input2[MAX_LENGTH + 1];
char input3[MAX_LENGTH + 1];
inputStringFromUser("\nCommand", input, MAX_LENGTH);
response = toupper(input[0]);
if (response == 'I') {
//insert a node code
char* promptName = "Song name";
char* promptArtist = "Artist";
char* promptGenre = "Genre";
char* newName;
char* newArtist;
char* newGenre;
//test points for the songname in the head node
if (head != NULL) {
printf("Test Point 1: %s\n", head->songName);
}
inputStringFromUser(promptName, input4, MAX_LENGTH);
newName = input4;
if (head != NULL) {
printf("Test Point 2: %s\n", head->songName);
}
inputStringFromUser(promptArtist, input2, MAX_LENGTH);
newArtist = input2;
inputStringFromUser(promptGenre, input3, MAX_LENGTH);
newGenre = input3;
//if it is the first node then just create a node then assign the values to the user input
if (head == NULL) {
head = malloc(sizeof(Node));
head->artist = newArtist;
head->genre = newGenre;
head->songName = newName;
} else {
//sorts through list until it finds the first node where the song name is not alphabetically ahead
//of the current entered name
Node* current = head;
while (strcmp(current->songName, newName) == 1) {
//if the loop goes to the end of the list place the new node at the end
if (current->nextNode != NULL) {
current = current->nextNode;
} else {
current->nextNode = malloc(sizeof(Node));
current = current->nextNode;
current->artist = newArtist;
current->genre = newGenre;
current->songName = newName;
break;
}
}
//if the loop finds the correct place for a node it shifts all the other ones down
//then create a new end node
char* tempName = " ";
char* tempName2 = " ";
char* tempArtist = " ";
char* tempArtist2 = " ";
char* tempGenre = " ";
char* tempGenre2 = " ";
tempName = current->songName;
tempArtist = current->artist;
tempGenre = current->genre;
current->artist = newArtist;
current->genre = newGenre;
current->songName = newName;
while (current->nextNode != NULL) {
current = current->nextNode;
tempName2 = current->songName;
tempArtist2 = current->artist;
tempGenre2 = current->genre;
current->songName = tempName;
current->artist = tempArtist;
current->genre = tempGenre;
tempName = tempName2;
tempGenre = tempGenre2;
tempArtist = tempArtist2;
}
current->nextNode = malloc(sizeof(Node));
current = current->nextNode;
current->songName = tempName;
current->artist = tempArtist;
current->genre = tempGenre;
}
}
}
// Support Function Definitions
// Prompt the user for a string safely, without buffer overflow
void inputStringFromUser(char prompt[], char s[], int maxStrLength)
{
int i = 0;
char c;
printf("%s --> ", prompt);
while (i < maxStrLength && (c = getchar()) != '\n')
s[i++] = c;
s[i] = '\0';
}
This is one example of what's wrong:
current->genre = newGenre;
You saving the value of the pointer newGenre (which is pointing to input3). So all nodes will end pointing to same object, i.e. when changing input3 all nodes will point the new value.
Try:
typedef struct node
{
char artist[MAX_LENGTH + 1];
char songName[MAX_LENGTH + 1];
char genre[MAX_LENGTH + 1];
struct node* nextNode;
} Node;
and then do:
strcpy(current->genre, newGenre);
to copy the value into the node.
Alternatively, you can keep the pointers and use dynamic memory allocation.

C strtok not parsing as I want

The *id value is initially 0000-0000:c29302. Once it gets send through the split function its value changes to 0000-0000. I think this has to do with it modifying the original value instead of modifying a temporary value. I want the *id value to stay the same. Thanks.
typedef struct node {
char *id;
char *clientid;
char *token;
struct node * next;
} credentials;
void split(credentials * head, char delim[]);
int main()
{
credentials * head = NULL;
head = malloc(sizeof(credentials));
head->id = strdup("0000-0000:c29302");
head->clientid = NULL;
head->token = NULL;
head->next = NULL;
split(head, ":");
print_list(head);
}
void split(credentials * head, char *delim)
{
char *token;
char *temp;
credentials * current = head;
while (current != NULL) {
temp = current->id;
int tempNum = 0;
token = strtok(temp, delim);
current->clientid = token;
while(token != NULL)
{
if(tempNum == 0)
{
current->clientid = token;
} else {
current->token = token;
}
token = strtok(NULL, delim);
tempNum++;
}
current = current->next;
}
}
From man strtok:
If a delimiter byte is found, it is overwritten with a null byte to terminate the current token, [...]
If you do not want your string to be modified you need to either write your own string-tokenizing function or to work with a copy of your string.
Note that if you do continue to use strtok (with a copy of your string), remember that the tokens returned by strtok are not new strings. They are pointers to points within the string you're working with. That means that when you free that string, all the tokens get freed at the same time.
You can use this instead of strtok.
void strsplit(const char* str, const char d, char** into)
{
if(str != NULL && into != NULL)
{
int n = 0;
int c = 0;
for(int i = 0; str[c] != '\0'; i++,c++)
{
into[n][i] = str[c];
if(str[c] == d)
{
into[n][i] = '\0';
i = -1;
++n;
}
}
}
}

How to store values with same memory location in c?

If I have a file stream with content
123 1234
1223 124235
21432 325
In my program I read line by line of the file and store the first target of each line into my list. These line with same location and when I run the program it will keep pointing to the most recent data and place it in to list. Which means If I have a function called printL() in while loop. It will print
123/
1223/1223/
21432/21432/21432/
instead of
123/
123/1223/
123/1223/21432
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct n{
char *value;
struct n *next;
} Node;
void printList(Node *head){
Node *cur = head;
while(cur!=NULL){
printf("%s/", cur->value);
cur = cur->next;
}
printf("\n");
}
void insertIntoList(Node **head, char *data){
Node *newNode = malloc(sizeof(Node));
if (newNode == NULL){
perror("Failed to allocate a new node for the linked list");
exit(1);
}
newNode->value = data;
newNode->next = NULL;
Node *currentList = *head;
if(*head == NULL){ //if the linked list head is null, then add the target into linked list
*head = newNode;
}
else{
while(currentList->next!=NULL){
currentList = currentList->next;
}
currentList->next = newNode;
}
}
int main(int argc, char**argv){
FILE *fileStream;
size_t len = 0;
char *line = NULL;
Node *head = NULL;
int j;
for(j=1; j<argc-2;j++){
fileStream = fopen(argv[j], "r");
if(fileStream == NULL){
fprintf(stderr, "could not open");
continue;
}
insertIntoList(&head,"a"); /////////////Line 95
insertIntoList(&head,"b");
insertIntoList(&head,"c");
insertIntoList(&head,"d");
printf("here is a try\n");
printList(head);
while(getline(&line, &len, fileStream)!=EOF){ /////////////Line 101
char *targetNum = strtok(line, " \t\r\n");
printf("*****%s\n", targetNum);
insertIntoList(&head, targetNum);
printf("######print head here is##########\n");
printList(head);
printf("######print head here is##########->\n");
}
//printList(head);
}
return 0;
}
In order to keep the content of each loaded field returned from strtok(), just add a strdup() before calling insertIntoList() after checking if not a null-pointer.
In your code, if you compare the value of both line and targetNum
are the same. If fact, the strtok() function returns a pointer to
the input string and keep the pointer for the next argument.
Replace the following code:
char *targetNum = strtok(line, " \t\r\n");
printf("*****%s\n", targetNum);
insertIntoList(&head, targetNum);
By that one:
char *targetNum = strtok(line, " \t\r\n");
if (targetNum != NULL) {
printf("*****%s\n", targetNum);
insertIntoList(&head, strdup(targetNum));
}
You don't store the contents of the string in your list nodes; you store a pointer to the buffer used for the contents of the string.
Consider changing your list node structure to
typedef struct node Node;
struct node {
Node *next;
char data[];
};
where the contents of the string are stored in the C99 flexible array member.
Your node constructor is then something like
Node *new_node(const char *data)
{
const size_t datalen = (data) ? strlen(data) : 0;
Node *result;
result = malloc(sizeof (Node) + datalen + 1);
if (!result) {
fprintf(stderr, "Out of memory!\n");
exit(EXIT_FAILURE);
}
if (datalen > 0)
memcpy(result->data, data, datalen);
result->next = NULL;
result->data[datalen] = '\0';
return result;
}
See how the function allocates memory for the copy of the data?
Personally, I prefer something like
typedef struct node Node;
struct node {
Node *next;
size_t hash;
size_t size;
char data[];
};
where the size member is basically strlen(data) (except that you can also use the nodes to hold binary data that includes nul bytes \0), and hash is a simple hash computed from data. hash is useful if you intend to compare the entire contents of nodes; if two nodes' lengths or hashes differ, then it is certain their contents differ; if they are the same, then you compare them character by character (memcmp(node1->data, node2->data, node1->length) == 0 if they are the same).
The constructor for the above is something like (using DJB2 hash):
Node *new_node(Node *next, const void *data, const size_t size)
{
Node *result;
result = malloc(sizeof (Node) + size + 1);
if (!result) {
fprintf(stderr, "new_node(): Out of memory (%zu bytes)\n", size);
exit(EXIT_FAILURE);
}
/* Copy and hash data using DJB2 hash (not that good, but fast) */
{
unsigned char *src = (unsigned char *)data;
unsigned char *const end = (unsigned char *)data + size;
unsigned char *dst = result->data;
size_t hash = 5381;
while (src < end) {
hash = hash * 33 + (size_t)(*src);
*(dst++) = *(src++);
}
/* Add terminator */
*dst = '\0';
}
result->next = next;
result->hash = hash;
result->size = size;
return result;
}
These Nodes can also be used in e.g. hash tables, which makes the type quite versatile.

character strings being mutated somehow

I am working on a a small program and it has a tiny logic error which I can't seem to trace. It tracks records input of this form:
time userID weight
It searches a linked list of all previous records to find the most recent one where the userID matches the current userID. It then compares the time and weight and calculates the rate of change in the weight. If the weight has changed abruptly it print "suspicious weight change". If there is not match and the input is valid it simply adds the new record to the list.
I have it working except when the userID is added to the list it seems to overwrite all the previous userID's. So even if a new record is input which has a unique userID, it finds a match, because all the ID's a made the same.
I just need a second pair of eyes to help spot where this is happening, I am new to C so it is probably some newbie mistake. But after 8 hrs of trying to find it, I am desperate for some help!
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define DELIM " " /* the delimiter */
#define MAX_CHANGE (10.0/86400.0) /* 10kg/day */
/* seconds in a day is 24 hours * 60 minutes * 60 seconds */
/* return 0 if the passed strings don't math, 1 otherwise */
/* defines the structure of Node */
struct Node
{
char * id;
float weight;
int time;
struct Node * next;
} *head, *p, *t, *last;
/* Constructor which returns a pointer to a new node*/
struct Node *newNode(int *time, char * id, float *w)
{
/*note malloc returns a pointer */
struct Node *r = (struct Node *)malloc( sizeof(struct Node) );
r->time = *time;
r->id = id;
r->weight = *w;
r->next = NULL;
return r;
}
/* prints the list starting with head */
printList(struct Node * head)
{
while(head != NULL)
{
printf("%d %s %f\n",head->time,head->id,head->weight);
head = head->next;
}
}
main()
{
char line[1024];
int lasttime = 0;
int success;
int timestamp;
int duration;
char userID[1000] = "";
char *token;
char temp[1000];
float weight;
float lastweight;
float change;
float changePerTime;
head = (struct Node*)malloc(sizeof(struct Node));
head->id = "";
head->weight = 0.0;
head->time = 0;
head->next = NULL;
last = head;
/*FILE * f = fopen("C:\\Users\\Chris\\Documents\\School\\York\\Computer Science\\2031 Software Tools\\Labs\\lab3\\testcases\\06.in","r"); */
/* last points to the last node in the list
head is always the same node
p is used to travers the list
t is a pointer the most recent occurrense of a user record
*/
while (fgets(line,1024,stdin) != NULL)
{
userID[0] ='\0'; // resets userID
token = strtok(line, DELIM);
success = sscanf(token,"%d",&timestamp);
if (success < 1 || timestamp == 0)
{
printf("Invalid time\n");
continue;
}
while((token = strtok(NULL,DELIM) ) != NULL && token[0] != '.' && ! isdigit(token[0]) )
{
strcpy(temp,token); //
strcat(temp,DELIM ); // adds space between each token
strcat(userID, temp); // src temp must be a const string, not a pointer
temp[0] = '\0';
}
userID[strlen(userID)-1] = '\0'; //erases the tailing space.
if(strlen(userID) > 179 || !strlen(userID) )
{
printf("Illegal userID\n");
continue;
}
else if(token == NULL || sscanf(token,"%f", &weight) < 1 || weight < 30.0 || weight > 300.0)
{
printf("Illegal weight\n");
continue;
}
else if (lasttime >= timestamp)
{
printf("Nonmonotonic timestamps\n");
continue;
}
else
{
/* sets t to last found user record and sets "last" to the last record*/
for(p = head; p != NULL; p = p->next)
{
if(strcmp(userID,p->id) == 0)
{
t=p;
}
last = p; // set last to last p.
}
if(t == NULL)
printf("OK newuser\n");
else if(t != NULL)
{
duration = timestamp - t->time;
change = weight - t->weight;
changePerTime = change / duration;
if(changePerTime < -MAX_CHANGE || changePerTime > MAX_CHANGE)
printf("Suspiciously large weight change\n");
else
printf("OK\n");
}
/* adds node to end of list */
last->next = newNode(&timestamp,userID,&weight);
printList(head);
}
}
//fclose(f);
}
I can fix the over-writing by changing newNode to:
struct Node *newNode(int *time, char * id, float *w)
{ /*note malloc returns a pointer */
struct Node *r = (struct Node *)malloc( sizeof(struct Node) );
r->time = *time;
r->id = strdup(id);
r->weight = *w;
r->next = NULL;
return r;
}
Note the addition of the call to strdup.

Resources