read txt, count each alphabet using linked list - c

#pragma warning (disable:4996)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <ctype.h>
#define NUM_OF_ALPHABET 26
#define MAX_HEAP_SIZE 100
typedef struct _CharFrequency
{
char character;
int frequency;
struct _CharFrequency * next;
}CharFrequency;
typedef CharFrequency* pCharFrequency;
pCharFrequency pHead = NULL;
void initList();
void addNode(char ch);
void printAllNode(pCharFrequency pHead);
int main()
{
int i = 0, cnt = 0;
FILE *pFile;
char readLine[1024], *ptr;
char *token = " \t\n.";
pFile = fopen("C:\\Users\\Home\\Desktop\\dataset.txt", "r");
if (pFile == NULL)
{
printf("File open failed.\n");
return 0;
}
while (fgets(readLine, 1024, pFile) != NULL)
{
ptr = strtok(readLine, token);
while (ptr != NULL)
{
for (i = 0; i < strlen(ptr); i++)
{
addNode(ptr[i]);
}
ptr = strtok(NULL, token);
}
}
printAllNode(pHead);
return 0;
}
void initList()
{
pHead = (CharFrequency*)malloc(sizeof(CharFrequency));
if (!pHead)
{
printf("Fault\n");
return;
}
pHead->character = '\0';
pHead->frequency = 0;
pHead->next = NULL;
}
void addNode(char ch)
{
int i = 0;
pCharFrequency pNode = NULL;
pCharFrequency pCurrent= NULL;
if (isalpha(ch) == 0)
return;
if (ch >= 'A' && ch <= 'Z')
ch = ch + 32;
printf("%c ", ch);
for (pCurrent = pHead; pCurrent != NULL ; pCurrent = pCurrent->next)
{
if (pCurrent->character == ch)
{
pCurrent->frequency++;
}
else
{
pNode = (CharFrequency*)malloc(sizeof(CharFrequency));
pNode->frequency = 0;
pNode->next = NULL;
pNode->character = ch;
pNode->frequency++;
pCurrent->next = pNode;
}
}
pNode = (CharFrequency*)malloc(sizeof(CharFrequency));
pNode->frequency = 0;
pNode->next = NULL;
pNode->character = ch;
pNode->frequency++;
pCurrent->next = pNode;
}
void printAllNode(pCharFrequency pHead)
{
pCharFrequency pCurrent;
pCurrent = pHead;
pCurrent = pHead;
while (pCurrent->next != NULL) {
printf("%c %d", pCurrent->character, pCurrent->frequency);
pCurrent = pCurrent->next;
}
}
I want to build a program that reads txt file, count only alphabet, and count them using linked list. I make struct called CharFrequency to count alphabet.
addNode function gets the character, checks if it's in the list or not, and count them.
It makes error when doing for() in the addNode function.

You need to rethink about the logic inside your addNode method. It is adding a new node every time a character is not found in the list, and even if a match is found,the loop will continue until the last node adding a new node every time.
You could do something like this to get you started and experiment on it to make it more efficient.
pCharFrequency pNode = NULL;
pCharFrequency pCurrent= NULL;
pCharFrequency pTail= NULL;//this will keep track of the last node in the list
//so that we use it to insert a new node
....//your other code
pCurrent = pHead;//start from the head
while (pCurrent!=NULL)
{
if (pCurrent->character == ch)
{
pCurrent->frequency++;
return;//if a match was found, count and return
}
if(pCurrent->next == NULL)
pTail=pCurrent;//save the pointer to the last node in the list if we reach to it
pCurrent=pCurrent->next;//get the next node
}
//if we reach here, then we need to create a new node
pNode = (CharFrequency*)malloc(sizeof(CharFrequency));
if(pNode==NULL)
{
//show error message
return;
}
pNode->frequency = 1;
pNode->next = NULL;
pNode->character = ch;
if(pHead==NULL)
pHead=pNode;//for the very first node,we just assign to head
else
pTail->next = pNode;//otherwise set the last node's next to the node we just created

Related

What is wrong with my replace string with another string or character using linked list

I have a linked list with many chars which I input from my input (what is the weather today?), to be replaced with another string (for example what replaced with how, so I get how is the weather today?).
But if the given words are right next to each other for example whatwhat, it will change to howwhat, disregarding the second part.
I think the problem is in the compare function, but I have no clue how to fix it, but the logic of replace should go like this:
If the words from my list and the needed word are the same, then proceed to iterate to the position where the next node of the word that should be changed (unwanted word) should be (pretty much the end of the word), then I create a new linked list with character with the wanted word, and connect temp to the start of the list and the next of the list to the position where the next character of the word that needs to be changed (unwanted word), which I found in the first loop.
Also don't roast my input() function, I know it is unsafe I just want to see what unsafe means with my own eyes, while I still have nothing to lose.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node {
int value_c;
struct node *next_c;
struct node *prev_c;
};
typedef struct node string;
int compare(string *head, char *word) {
int counter = 0;
string *temp = head;
for (int i = 0; i < strlen(word); i++) {
if (temp->value_c == word[i]) {
temp = temp->next_c;
counter++;
}
}
if (counter == strlen(word))
return 1;
else
return 0;
}
void print_c(string *head) {
while (head != NULL) {
printf("%c", head->value_c);
head = head->next_c;
}
}
void append_c(string **head, char thing) {
string *newNode = (string *)malloc(sizeof(string));
newNode->value_c = thing;
newNode->next_c = NULL;
if (*head == NULL) {
*head = newNode;
newNode->prev_c = NULL;
return;
}
string *temp = *head;
while (temp->next_c != NULL)
temp = temp->next_c;
temp->next_c = newNode;
newNode->prev_c = temp;
}
string *replace_all1(string *head, char *what, char *with_what) {
string *temp = head;
while (temp != NULL) {
printf("%c ", temp->value_c);
if (compare(temp, what) == 1) {
printf("%i ", 1);
printf("%c ", temp->value_c);
string *new = temp;
for (int i = 0; i < strlen(what) - 1; i++) {
new = new->next_c;
}
string *word = NULL;
for (int i = 0; i < strlen(with_what); i++) {
append_c(&word, with_what[i]);
}
string *word_temp = word;
while (word_temp->next_c != NULL) {
word_temp = word_temp->next_c;
}
word_temp->next_c = new->next_c;
if (temp->prev_c != NULL) {
temp->prev_c->next_c = word;
} else {
head = word;
print_c(head);
temp = word;
print_c(temp);
word->prev_c = NULL;
}
}
temp = temp->next_c;
}
printf("\n");
return head;
}
string *String(char *str) {
string *st = NULL;
int i = 0;
while (str[i] != '\0') {
append_c(&st, str[i]);
i++;
}
return st;
}
string *input() {
char *a = (char *)malloc(sizeof(char));
scanf("%[^\n]", a); //maximum of 1408
string *stri = String(a);
return stri;
free(a);
}
int main() {
string *list = NULL;
string *big_boy_string = input();
//printf("%c", big_boy_string->value_c);
//print_c(big_boy_string);
//printf("\n");
//printf("%i", compare(big_boy_string, "what"));
//printf("%i ", len(big_boy_string));
//printf("\n");
//print_c(slice(big_boy_string, 1, 10));
//print_c(replace(big_boy_string, 'h', 'a'));
//printf("\n");
//print_c(reverse(big_boy_string));
print_c(replace_all1(big_boy_string, "a", "b"));
//getline();
}
char *a = (char*) malloc(sizeof(char));
scanf("%[^\n]",a); //maximum of 1408
The first statement allocates memory for just 1 byte. So the maximum is not 1408, but 1. It can store a single char, or the null-terminator if it's a string, but no more.
Next, scanf() will write to out of bounds memory, and invoke undefined behaviour. The subsequent functions all depend on this undefined behaviour, so I'm not going to look at them.
But then, you've a memory leak in the same function.
return stri;
free(a);
You return before freeing the allocated memory. The call to free() is never executed.
The return value of malloc() is also ignored. Code risks undefined behaviour if the subsequent dereferences are on a NULL pointer.
Aside: The cast is meaningless and may hide a bug. malloc() and family returns a void * that is implicitly converted to the right type.
Re: Also don't roast my input() function, I know its unsafe I just
want to see what unsafe means with my own eyes.
If you are already aware of this, then you shouldn't be asking why your code doesn't work. You are relying on undefined behaviour (playing with fire).
There is no need to look further than the input function: it has undefined behavior or the worst kind because you attempt to read the input string into a very small array, allocated for a single byte. You must fix this first. Since you know the maximum length of your input string, you can use this:
string *input(void) {
char a[1409];
if (scanf("%1408[^\n]", a) != 1) { //maximum of 1408
// invalid or missing input
return NULL;
}
scanf(%*[^\n]"); // consume any remaining characters on the input line
scanf(%*1[\n]"); // consume the newline if present
return String(a);
}
Here is an alternative using getchar() instead of scanf() which is quite tricky and error prone:
string *input(void) {
char a[1409];
int c;
size_t i = 0;
while ((c = getchar()) != EOF && c != '\n') {
if (i + 1 < sizeof(a))
a[i++] = (char)c;
}
if (c == EOF && i == 0) {
/* end of file without any input */
return NULL;
}
a[i] = '\0';
return String(a);
}
The compare function is incorrect: it should return false as soon as the comparison fails and it must test for the end of string (temp == NULL):
int compare(const string *head, const char *word) {
string *temp = head;
for (size_t i = 0; word[i] != '\0'; i++) {
if (temp == NULL || temp->value_c != word[i])
return 0;
temp = temp->next_c;
}
return 1;
}
The replace_all1() function has problems too:
for (int i = 0; i < strlen(what) - 1; i++) will cause undefined behavior if what is an empty string because strlen(what) - 1 is unsigned with the value SIZE_MAX in this case, causing the loop to proceed for a very long time, well beyond the end of the list pointed to by new.
while (word_temp->next_c != NULL) will cause a undefined behavior if the replaced word is empty as word_temp will be NULL.
once you replace the sublist, you do not update temp correctly to point to the node after the replaced one, which you could achieve by setting temp to word_temp.
the function does not free the replaced sublist.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node {
int value_c;
struct node *next_c;
struct node *prev_c;
};
typedef struct node string;
void string_append_char(string **head, int c) {
string *node = malloc(sizeof(*node));
if (node == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
node->value_c = c;
node->next_c = NULL;
if (*head == NULL) {
node->prev_c = NULL;
*head = node;
} else {
string *temp = *head;
while (temp->next_c != NULL)
temp = temp->next_c;
node->prev_c = temp;
temp->next_c = node;
}
}
string *string_new(const char *str) {
string *st = NULL;
for (int i = 0; str[i] != '\0'; i++) {
string_append_char(&st, str[i]);
}
return st;
}
string *string_input(const char *prompt) {
string *st = NULL;
int c;
if (prompt) {
printf("%s", prompt);
}
while ((c = getchar()) != EOF && c != '\n') {
string_append_char(&st, c);
}
return st;
}
void string_print(const char *before, const string *head, const char *after) {
printf("%s", before);
while (head != NULL) {
putchar(head->value_c);
head = head->next_c;
}
printf("%s", after);
}
void string_free(string *head) {
while (head != NULL) {
string *next = head->next_c;
free(head);
head = next;
}
}
int string_compare(const string *head, const char *word) {
const string *temp = head;
for (size_t i = 0; word[i] != '\0'; i++) {
if (temp == NULL || temp->value_c != word[i])
return 0;
temp = temp->next_c;
}
return 1;
}
int string_replace(string **head, const char *what, const char *with_what) {
int count = 0;
if (*what == '\0')
return 0;
string *temp = *head;
while (temp != NULL) {
if (string_compare(temp, what)) {
count++;
// locate the last node of the substring
string *temp_end = temp;
for (size_t i = 0; what[i + 1] != '\0'; i++) {
temp_end = temp_end->next_c;
}
string *next = temp_end->next_c;
if (*with_what == '\0') {
// just delete the substring
if (temp->prev_c != NULL) {
temp->prev_c->next_c = next;
} else {
*head = next;
}
if (next) {
next->prev_c = temp->prev_c;
}
} else {
// create a string from the replacement
string *word = string_new(with_what);
// locate the last node of the new substring
string *word_end = word;
while (word_end->next_c != NULL) {
word_end = word_end->next_c;
}
word->prev_c = temp->prev_c;
if (temp->prev_c != NULL) {
temp->prev_c->next_c = word;
} else {
*head = word;
}
word_end->next_c = next;
if (next) {
next->prev_c = word_end;
}
}
temp_end->next_c = NULL;
string_free(temp);
temp = next;
} else {
temp = temp->next_c;
}
}
return count;
}
int main() {
string *list = string_input("enter string: ");
string_print("input: ", list, "\n");
printf("replacing 'what' to 'how': %d matches\n", string_replace(&list, "what", "how"));
string_print("rep1: ", list, "\n");
printf("replacing 'a' to 'b': %d matches\n", string_replace(&list, "a", "b"));
string_print("rep2: ", list, "\n");
printf("deleting 'h': %d matches\n", string_replace(&list, "h", ""));
string_print("rep3: ", list, "\n");
string_free(list);
return 0;
}
Sample session:
enter string: what is the weather today?
input: what is the weather today?
replacing 'what' to 'how': 1 matches
rep1: how is the weather today?
replacing 'a' to 'b': 2 matches
rep2: how is the webther todby?
deleting 'h': 3 matches
rep3: ow is te webter todby?

Linked list being overwritten instead of attached to another linked list

This is written in C.
I'm trying to take user input and use it to create/add to a linked list, which I point to with struct Node *dict; Everything is accomplished using global memory.
Creating a new linked list works fine, but when the user tries to add to the linked list, it overwrites the extant linked list.
Here's my code for adding to the list (words is an array of nodes to be appended to the list):
if (dict == NULL) { // If previous list does not exist, global dict pointer should point to node array
dict = words;
} else { // Else find end of current linked list and point it to the new list
struct Node *head = dict;
while (head->next != NULL) {
head = head->next;
}
head->next = words;
}
When I create a list with the words "apple orange peach," for example, when I print the list, I get the output "apple orange peach." But then when I add "pear" to the list, "apple orange peach" is overwritten and I only see the output "pear," instead of "apple orange peach pear."
EDIT:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//// GUI Stuff ////
void drawDashedLine() {
for (int i = 0; i < 30; i++) {
printf("-");
}
printf("\n");
}
void drawDottedLine() {
for (int i = 0; i < 30; i++) {
printf(".");
}
printf("\n");
}
void drawArrowLine() {
for (int i = 0; i < 30; i++) {
printf(">");
}
printf("\n");
}
void drawStarLine() {
for (int i = 0; i < 30; i++) {
printf("*");
}
printf("\n");
}
struct Node {
int length;
char word[5];
struct Node * next;
};
// Pointer to global linked list dictionary
struct Node *dict;
struct Node *newDict;
void printDict() {
drawDottedLine();
struct Node * head = dict;
while (head != NULL) {
printf("%s\n", head -> word);
head = head -> next;
}
drawDottedLine();
return;
}
void alphabetizeDict() { // Bubble sort
//printf("%p --- %p\n", dict, dict->next);
struct Node * head = dict;
if (head == NULL) {
return;
}
struct Node * ptr2 = NULL;
int swapped = 1;
while (swapped) {
swapped = 0;
head = dict;
while (head -> next != ptr2) {
char * temp1 = strdup(head -> word);
char * temp2 = strdup(head -> next -> word);
strupr(temp1);
strupr(temp2);
if (strcmp(temp1, temp2) > 0) {
char temp[5];
strcpy(temp, head -> word);
strcpy(head -> word, head -> next -> word);
strcpy(head -> next -> word, temp);
swapped = 1;
}
head = head -> next;
}
ptr2 = head;
}
return;
}
void createDict() {
// To hold the string entered by the user
char str[5000];
// Holds 1000 words, each up to 5 characters long (4 plus a NULL char)
char newString[1000][5];
printf("\n");
drawArrowLine();
printf("Enter word(s): \n");
fgets(str, sizeof str, stdin);
int i, j, ctr;
j = 0;
ctr = 0; // ctr to iterate through words, j to iterate through letters
for (i = 0; i <= (strlen(str)); i++) {
if (str[i] == ' ' || str[i] == '\0') { // This is whitespace. add null character to terminate string. Start next word
newString[ctr][j] = '\0';
ctr++;
j = 0;
} else { // Else add letter to string
newString[ctr][j] = str[i];
j++;
}
}
for (int i = 0; i < ctr; i++) {
struct Node n;
n.length = strlen(newString[i]);
int c = 0;
char sub[5];
// Only use word's first four letters
while (c < strlen(newString[i]) && c < 4) {
sub[c] = newString[i][c];
c++;
}
sub[c] = '\0';
strcpy(n.word, sub);
n.next = NULL;
if (dict == NULL) {
dict = &n;
} else {
n.next = dict;
dict = &n;
}
}
// alphabetizeDict();
printf("Word(s) added succesfully\n");
drawArrowLine();
printf("\n");
return;
}
void destroyDict() {
printf("Starting new dictionary......\n");
while (dict != NULL) {
struct Node * temp = dict;
dict = dict -> next;
temp -> next = NULL;
}
}
void caseInsensSearch(char * searchTerm) {
for (int i = 0; searchTerm[i]; i++) {
searchTerm[i] = tolower(searchTerm[i]);
}
struct Node * head = dict;
int index = 0;
while (head != NULL) {
char lowercaseWord[5];
for (int i = 0; head -> word[i]; i++) {
lowercaseWord[i] = tolower(head -> word[i]);
}
if (strcmp(lowercaseWord, searchTerm) == 0) {
printf("Found %s at index %i\n", head -> word, index);
drawDashedLine();
return;
}
head = head -> next;
index++;
}
printf("Sorry, I couldn't find %s in your dictionary.\n", searchTerm);
drawDashedLine();
return;
}
void caseSensSearch(char * searchTerm) {
struct Node * head = dict;
int index = 0;
while (head != NULL) {
if (strcmp(head -> word, searchTerm) == 0) {
printf("Found %s at index %i\n", head -> word, index);
drawDashedLine();
return;
}
head = head -> next;
index++;
}
printf("Sorry, I couldn't find %s in your dictionary.\n", searchTerm);
drawDashedLine();
return;
}
void search() {
int isSens;
drawDashedLine();
printf("Enter 1 for Case sensitive\n2 for case insensitive\n");
drawDashedLine();
scanf("%d", & isSens);
while (isSens < 1 || isSens > 2) {
printf("Please enter a number between 1 and 2:\n");
scanf("%d", & isSens);
}
drawDashedLine();
printf("Enter a word to search for:\n");
char searchTerm[5];
scanf("%s", searchTerm);
searchTerm[4] = '\0';
if (isSens == 1) {
caseSensSearch(searchTerm);
} else {
caseInsensSearch(searchTerm);
}
}
int promptUser() {
drawStarLine();
printf("1) Search for a word\n2) Add word(s)\n3) Print dictionary\n4) Start new dictionary\n5) Exit\n");
drawStarLine();
printf("\nEnter a number between 1 and 5:\n");
int choice;
scanf("%1d", & choice);
while (choice < 1 || choice > 5) {
printf("Please enter a number between 1 and 5:\n");
scanf("%d", & choice);
}
return choice;
}
int main() {
for (;;) {
int choice = promptUser();
fflush(stdin);
if (choice == 1) {
search();
} else if (choice == 2) {
createDict();
} else if (choice == 3) {
printDict();
} else if (choice == 4) {
destroyDict();
} else if (choice == 5) {
return 0;
}
}
return 1;
}
I've spent some time diagnosing this problem for you. The problem statement is bizarre... An array of words could be sorted (even with library qsort()) and grow to the fill the array to the brim, but you claim this must use both a linked list and a global "object pool" that is not dynamically allocated...
Here's some code I've compiled BUT NOT TESTED...
It should be simple to follow and expand to accommodate your requirements.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node { // use 'typdef'. Less typing
char word[5]; // VERY SHORT WORDS guaranteed
struct Node *next;
} Node_t;
Node_t dict[ 1000 ]; // global data space
int nextNode = 0;
void printDict() { // traverse LL outputting value(s)
for( Node_t *pn = dict; pn; pn = pn->next )
puts( pn->word );
}
void addNode( char *data ) {
if( nextNode + 1 >= sizeof dict/sizeof dict[0] )
return; // Fixed size cannot grow.
Node_t *pn = &dict[ nextNode ];
strcpy( pn->word, data ); // "apple" WON'T fit
nextNode++;
//EDIT:
// This is not correct.
// See code block below for correction
if( nextNode +1 >= sizeof dict/sizeof dict[0] )
pn->next = NULL;
else
pn->next = &dict[ nextNode ];
}
void createDict() {
char str[5000]; // one lo-o-o-ong input string of short words
printf( "Enter word(s): \n" );
fgets( str, sizeof str, stdin );
// chop words on spaces (or tabs) and store to LL
for( char *cp = str; ( cp = strtok( cp, " \t" ) ) != NULL; cp = NULL )
addNode( cp );
}
void destroyDict() { // super simple!
memset( dict, 0, sizeof dict );
nextNode = 0;
// return; // Do not need those return(s) before final closing brace.
}
80% of any problem is the clear understanding of what the problem is to begin with.
EDIT: Realising the code must 'straddle' both array and LL, the above was not exactly correct. Below is the necessary fix to conform with a LL having a NULL next pointer at its 'tail' node.
void addNode( char *data ) {
if( nextNode + 1 >= sizeof dict/sizeof dict[0] )
return; // Fixed size cannot grow.
strcpy( dict[ nextNode ].word, data ); // "apple" WON'T fit
if( nextNode ) // at node 1 or greater
dict[ nextNode - 1 ].next = &dict[ nextNode ];
nextNode++;
}
try this
if (dict == NULL) { // If previous list does not exist, global dict pointer should point to node array
dict = words;
} else { // Else find end of current linked list and point it to the new list
struct Node *head = dict;
while (head->next != NULL) {
head = head->next;
}
struct Node *ptr = NULL;
ptr->length = strlen(word);
strcpy(ptr->word, sub);
ptr->next = NULL;
head->next = ptr;
}

Creating a singly linked list from a .txt file and reversing odd numbers of each line in C

I have a project about linked lists but I'm having a hard time doing it. The teacher wants me to read a .txt file and create singly linked list from it. After that, I need to reverse odd numbers of every line. Then print it. Here is the code which I used for printing the linked list. But I need help to reverse the odd numbers of each line.
This is the code which I used to print the list:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list {
char *string;
struct list *next;
};
typedef struct list LIST;
int main(void) {
FILE *fp;
char line[10];
LIST *current, *head;
head = current = NULL;
fp = fopen("data.txt", "r");
while(fgets(line, sizeof(line), fp)){
LIST *node = malloc(sizeof(LIST));
node->string = strdup(line);
node->next =NULL;
if(head == NULL){
current = head = node;
} else {
current = current->next = node;
}
}
fclose(fp);
for(current = head; current ; current=current->next){
printf("%s", current->string);
}
return 0;
}
Here is the content of the .txt file:
10
9,6,11,7,12,18,19,14,15,13
13,14,9,12,15,3,18,20,1,2
4,11,8,17,12,15,20,10,3,16
19,4,11,1,13,17,12,16,20,18
1,6,20,11,13,9,7,16,10,2
12,4,11,16,3,20,9,19,17,15
20,3,10,12,18,2,5,14,15,16
18,19,15,2,6,9,1,3,17,4
7,6,20,1,11,4,3,5,8,16
1,2,16,13,17,10,12,9,4,15
"But I need help to reverse the odd numbers of each line."
There are several other parts that need to be considered before this step can be developed.
Following are suggestions for a functions approach implementation using your problem description. A few items are simply suggestions to simplify the existing code. And a few other steps, are not mentioned as necessary, but should be considered:
Since you are not mandated to use char *string; in your problem description, choose to use a reasonable string length variable that does not require an additional layer of dynamic allocation, such as char string[260]; (or even smaller to fit your input file.) This will greatly simplify the code.
Because the input file is sized with lines ~30 char long, declare the variable line to be at least large enough to contain one line, eg 80 would allow larger values, and still allow enough space, but since memory is cheap, go with the same size as is used in the string member of your linked list.
Move the work of populating each new node to a function. It also will greatly simplify the program, and provide greater readability. Eg: void insert(LIST **head_ref, char *str);
Always test the return of fopen() before attempting to use the file descriptor.
To manipulate the contents of each odd row (eg 1, 3, 5, 7, 9), as numbers, the contents of each line read in from a file as a string, needs to first be converted to a collection of numbers. This suggests an additional member be added to the struct. For example int num[10].
The previous observation implicitly suggests the need of an additional function to parse and convert each comma delimited string into discrete integer values. Perhaps with the prototype: void parseIntArray(LIST **list);
The next and final task also suggests an additional function to reverse the contents of selected array member integer arrays. This one might use a prototype such as: void reverse_odd(LIST **list, size_t size);
Finally, because each node of LIST created required dynamically allocated memory, once finished using LIST, the memory must be given back to the OS to prevent memory leaks. An additional function to do this could be prototyped: void freeList(LIST **head);
Following are the main() function and preceding support declarations etc. It is intended here to illustrate the above suggested steps, and the benefits of breaking down a bigger problem into smaller problems, then implementing each smaller solution to support the whole. Benefits include for example readability and maintainability and potential re-use of code-base, (Note the similarity of argument lists in each supporting function.):
#define MAX_STRLEN 260 //use mnemonic values to avoid magic numbers in code
struct list {
char string[MAX_STRLEN];
int arr[10];
struct list *next;
};
typedef struct list LIST;
//Prototypes of 'smaller' solutions
void insert(LIST **head_ref, char *str);
void parseIntArray(LIST **list);
void reverse_odd(LIST **list, size_t size);
void freeList(LIST **head);
int main(void)
{
FILE *fp;
char line[MAX_STRLEN];
LIST *current, *head;
char *convPtr = NULL;
head = current = NULL;
fp = fopen("data.txt", "r");
if(fp)
{
//consume 1st line
if(fgets(line, sizeof(line), fp));//10
{
sizeArray = strtol(line, &convPtr, 10);
if(errno != ERANGE)
{
while(fgets(line, sizeof(line), fp))
{
//(see implementations of each below)
//create new node, insert num string
insert(&current, line);
//convert new->string to integers, place in new->array
parseIntArray(&current);
//reverse 'odd' contents of each array
reverse_odd(&current, sizeArray);
}
}else{//handle error and leave}
}
fclose(fp);
}else{//handle error and leave}
//At this point in code, entire file is captured into nodes of list.
//use list as needed
//When finished using list, memory must be freed to prevent memory leaks
head = current;
freeList(&head);
return 0;
}
The remaining code segments are the function implementations used above:
void freeList(LIST **head)
{
LIST *tmp;
while (*head != NULL)
{
tmp = (*head);
(*head) = (*head)->next;
free(tmp);
}
}
//create new node, insert num string
void insert(LIST **head_ref, char *str)
{
int *arr = malloc(numNodes * sizeof(*arr));
//allocate node
LIST* new = calloc(1, sizeof(*new));
//put in the data
strcpy(new->string, str);
//Make next of new node as head
new->next = (*head_ref);
//Move the head to point to the new node
(*head_ref) = new;
}
//convert new->string to integers, place in list->array
void parseIntArray(LIST **list)
{
char *tok = NULL;
int i = 0;
int tmp = 0;
char *sArray = strdup((*list)->string);
tok = strtok(sArray, ",\n ");
while(tok)
{
errno = 0;
tmp = atoi(tok);
if(errno == ERANGE)
{
printf("Error converting string to number\nExiting.");
return;
}
(*list)->arr[i] = tmp;
i++;
tok = strtok(NULL, ",\n ");
}
}
//reverse 'odd' contents of list->array
void reverse_odd(LIST **list, size_t size)
{
int *ptr = &((*list)->arr[0]);
int *tmp = malloc(size * sizeof(*tmp));
memset(tmp, -1, size*sizeof(*tmp));
for(int i=0;i<size;i++)
{
if(ptr[i]%2 != 0)
tmp[size-1-i] = ptr[i];
}
for(int i=0;i<size;i++)
{
if(tmp[i] < 0)
{
while((*ptr)%2 != 0 ) ptr++;
tmp[i] = *ptr;
ptr++;
}
}
memcpy((*list)->arr, tmp, size*sizeof(int));
}
This hope this code will do the job.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct line {
struct num *first;
struct line *next;
} LineNode;
typedef struct num {
int num;
int order;
struct num *next;
} NumNode;
int main() {
FILE *fp;
char ch;
int counter = 0;
NumNode *curr_num, *even_ptr, *odd_ptr, *odd_head, *even_head;
LineNode *curr_line, *line_head;
curr_num = even_head = odd_head = even_ptr = odd_ptr = NULL;
line_head = curr_line = NULL;
fp = fopen("data.txt", "r");
if (fp == NULL)
{
return 1;
}
ch = fgetc(fp);
while(ch != EOF){
if (ch >= 48 && ch <= 57)
{
int n = 0;
while (ch != EOF && ch != '\n' && ch >= 48 && ch <= 57)
{
int x = ch - 48;
n = n * 10 + x;
ch = fgetc(fp);
}
NumNode *node = malloc(sizeof(NumNode));
node->num = n;
node->order = counter;
node->next =NULL;
if (n % 2 == 0){
if(even_head == NULL){
even_head = even_ptr = node;
} else {
even_ptr = even_ptr->next = node;
}
}else{
if(odd_head == NULL){
odd_head = node;
} else {
node->next = odd_head;
odd_head = node;
}
}
counter++;
}
if (ch == '\n' || ch == EOF)
{
NumNode *num_node, *head;
num_node = head = NULL;
even_ptr = even_head;
odd_ptr = odd_head;
counter = 0;
if (even_head != NULL && even_head->order == counter){
head = num_node = even_ptr;
even_ptr = even_ptr->next;
} else {
head = num_node = odd_ptr;
odd_ptr = odd_ptr->next;
}
counter++;
while (even_ptr != NULL)
{
if (even_ptr->order == counter) {
num_node = num_node->next = even_ptr;
even_ptr = even_ptr->next;
}
else if (odd_ptr != NULL) {
num_node = num_node->next = odd_ptr;
odd_ptr = odd_ptr->next;
}
counter++;
}
while (odd_ptr != NULL)
{
num_node = num_node->next = odd_ptr;
odd_ptr = odd_ptr->next;
}
LineNode *node = malloc(sizeof(LineNode));
node->next =NULL;
node->first = head;
if (line_head == NULL)
line_head = curr_line = node;
else
curr_line = curr_line->next = node;
odd_head = even_head = NULL;
counter = 0;
}
ch = fgetc(fp);
}
fclose(fp);
for(curr_line = line_head; curr_line != NULL ; curr_line=curr_line->next) {
for(curr_num = curr_line->first; curr_num != NULL ; curr_num=curr_num->next) {
printf("%d", curr_num->num);
if (curr_num->next != NULL)
printf(",");
}
printf("\n");
}
return 0;
}

Sorting and Displaying a Linked List

I am writing a library management program where the user can add books to a database which just means that the program will take the user's input and store it into a text document. Then, when the program starts up it will read through the text document where all the books are stored and build a linked list where each book would be a node. So, I have been able to get to a point where I can read the text file and store the values into nodes. However, when I try testing the bookList function to view the entire book list by title my program crashes. Here is the code:
void loadingMenu(){
FILE *fp = fopen(filename, "r");
int lines = 0;
char line[254];
char *ti = malloc(MAX_STR_LEN);
char *au = malloc(MAX_STR_LEN);
char *ca = malloc(MAX_STR_LEN);
char *id = malloc(MAX_STR_LEN);
char *ti_chopped;
char *au_chopped;
char *ca_chopped;
char *id_chopped;
int id_num;
struct node *tempNode;
while(fgets(line, sizeof(line), fp)){
if(line == 'EOF'){
break;
}
if(lines == 7){
lines = 0;
}
if(lines == 0){
line[strcspn(line, "\n")] = 0; // remove '\n' from string
strcpy(ti, line);
}
else if(lines == 1){
line[strcspn(line, "\n")] = 0;
strcpy(au, line);
}
else if(lines == 3){
line[strcspn(line, "\n")] = 0;
strcpy(ca, line);
}
else if(lines == 6){
line[strcspn(line, "\n")] = 0;
strcpy(id, line);
}
lines++;
if(lines == 6){
// removing the identifiers from each string
ti_chopped = ti + 6;
au_chopped = au + 7;
ca_chopped = ca + 9;
id_chopped = id + 3;
id_num = atoi(id_chopped);
// ------create book node------------
tempNode = malloc(sizeof *tempNode);
// ----------------------------------
tempNode->next = NULL;
tempNode->titleptr = malloc(strlen(ti_chopped) + 1);
strcpy(tempNode->titleptr, ti_chopped);
tempNode->authorptr = malloc(strlen(au_chopped) + 1);
strcpy(tempNode->authorptr, au_chopped);
tempNode->categoryptr = malloc(strlen(ca_chopped) + 1);
strcpy(tempNode->categoryptr, ca_chopped);
tempNode->id = id_num;
//printf("%d", tempNode->id);
head = addNode(head, tempNode);
}
}
fclose(fp);
}
int compareNode(struct node *n1, struct node *n2){
int compareValue = strcmp(n1->titleptr, n2->titleptr);
if(compareValue == 0){
return 0;
}
else if(compareValue < 0){
return -1;
}
else {
return 1;
}
}
struct node *addNode(struct node *list, struct node *node1){
struct node* tmp = list;
if(list == NULL){
return node1;
}
if(compareNode(node1,list) == -1){
node1->next = list;
list = node1;
return list;
}
else
{
struct node* prev = list;
while(tmp != NULL && compareNode(node1, tmp) >= 0){
prev = tmp;
tmp = tmp->next;
}
prev->next = node1;
node1->next = tmp;
return list;
}
}
void bookList(){
system("cls");
struct node *tmp;
tmp = head;
printf("List of all Books: ");
while(tmp != NULL)
{
printf("%s\n", tmp->titleptr);
tmp = tmp->next;
}
printf("\n\nEnd of list.");
}
First off I would like to apologize for the bad code, also I trimmed away some of the fat of the program and just left the functions involved behind.
So, please if you could help me out on this or at least point me in the right direction I would be very grateful. Also, if you have any coding tips or comments go ahead and tell me, I am always hungry to learn!
EDIT: The code now runs and the list will print, however, the strings will not be in alphabetical order. Currently trying to figure that out.
In the first addNode() function, you need to set the previous pointer. That should fix the problem.
if(compareNode(node1,list) == -1)
{
node1->next = list;
list->prev = node1;
list = node1;
return list;
}

Writing list to file

I have a problem with writing list to file. Please check my code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct record
{
char name[30];
int score;
} record;
int score = 23;
char winner[30] = "gracz";
typedef struct el_list
{
record record;
struct el_list* next;
} el_list;
el_list *first = NULL;
int addrecord()
{
el_list *record;
record = (el_list*) malloc (sizeof(el_list));
record->next = NULL;
record->record.score = score;
strcpy(record->record.name, winner);
return record;
}
void addtolist(el_list** first)
{
el_list *pom, *tmp = addrecord();
if (*first == NULL)
*first = tmp;
else if ((*first)->record.score > tmp->record.score)
{
tmp->next = *first;
*first = tmp;
}
else
{
pom = (*first);
while((pom->next != NULL) && (pom->record.score < tmp->record.score))
pom = pom->next;
tmp->next = pom->next;
pom->next = tmp;
}
}
void save2file(el_list* first)
{
el_list *tmp;
FILE *hs = fopen("highscores.txt", "w");
if( hs == NULL)
perror("Blad z plikiem.");
else
{
tmp = first;
while(tmp != NULL)
{
fprintf( hs, "%d %s\n", score, winner);
tmp = tmp->next;
}
}
fclose(hs);
}
int main()
{
addtolist(&first);
save2file(&first);
return 0;
}
Probably I have problem with while in save2file.
Sorry for my English. ;)
You're saving score and winner, which are global variables unrelated to the current list item in tmp.
Also, for clarity's sake, you should open the output file in text mode, i.e. with fopen("highscores.txt", "wt").

Resources