I'm new to C programming, and I'm having some difficulties. I'm trying to re-code to use a linked list to store the structs you read from the file, but I can't understand how to save the text line for line and add it to the linked list.
This is my code:
#include<stdio.h>
#include<stdlib.h>
#include"Cars.h"
void write_to_file() {
FILE* file = NULL;
file = fopen("Cars.dat", "wb");
Car c = { "Toyota", "Civic", "black", 5, 50000 };
size_t ret = fwrite(&c, sizeof c, 1, file);
Car d = { "Toyota1", "Civic1", "Red", 2, 55000 };
ret = fwrite(&d, sizeof d, 1, file);
fclose(file);
}
void print_cars(Car *c, int count) {
FILE* f = fopen("output.txt", "w");
for (int i = 0; i < count; i++) {
fprintf(f, "%s %s %s %d %f\n", c[i].model, c[i].manufacturer, c[i].color, c[i].seatCapacity, c[i].price);
}
fclose(f);
}
void read_from_file() {
Car c;
size_t SIZE = sizeof c;
size_t ret = 1;
FILE* file = NULL;
file = fopen("Cars.dat", "rb");
int count = 0;
do{
ret = fread(&c, sizeof c, 1, file);
if (ret != 1)
break;
count++;
//printf("%s %s %s %d %f\n", c.model, c.manufacturer, c.color, c.seatCapacity, c.price);
}while(!feof(file));
fclose(file);
if (count == 0) {
printf("No cars in the file provided\n");
return;
}
file = fopen("Cars.dat", "rb");
Car* cars_array = NULL;
cars_array = (Car *)malloc(count * SIZE);
ret = fread(cars_array, SIZE*count, 1, file);
//printf("%d\n", ret);
print_cars(cars_array, count);
}
int main()
{
//write_to_file();
read_from_file();
return 0;
}
Struct code:
#ifndef Cars_h
#define Cars_h
typedef struct cars {
char manufacturer[35]; //toyota, honda
char model[35]; //camry, civic
char color[20]; //black, red
int seatCapacity; //4,5
float price; //20k, 25k
}
Car;
#endif
If anyone could give me some pointers on what I need to do next to make it work, I would highly appreciate it. Thank you
You could implement a linked list of 'Car' objects by adding a next pointer to the Car structure. For example:
typedef struct Car {
/* existing fields */
char model[LLL];
char manufacturer[MMM];
char color[CCC];
int seatCapacity;
float price;
struct Car *next; /* new member for linked list */
} Car;
(An assumption is made about Car as source code for Cars.h was not provided.)
You would have to account for this extra field in Cars.dat (where it could be stored with a NULL value assuming that the file has just an array and no notion of a linked list).
When reading, you could malloc space for each Car separately, fread it into the malloc-ed buffer, and update the previous' Car's next pointer to point to the newly loaded Car. In this manner you can build up a simple linked list of Cars.
p.s. I've never heard of a Toyota Civic :)
You can create a new struct as below which includes your Car struct as a field. And a pointer to the next node.
typedef struct {
Car payload;
CarNode* nextNode;
}CarNode
And you don't need to calculate the count of records in the file. Just read one and malloc a CarNode and use the CarNode.payload field as the buffer to hold the content read from the file. Something like below snippet:
CarNode head, tail; // Suppose you are using single linked list.
/*
Initialize the head and tail pointer. At first, they should both point to the first node.
*/
...
/*
Allocate a new node while reading a record from the file.
*/
CarNode *p = (CarNode *)malloc(sizeof(CarNode));
ret = fread(&(p->payload), sizeof(Car), 1, file);
/*
Insert the newly read node into the link list.
*/
tail->nextNode = p;
tail = p;
Related
Basicaly what i want is to read a file in these case im using the stdin as my file FILE *f=stdin;
the files contains something like these
2019 - Frog and Mouse
1982 - Water and gelly
3025 - Sugar
...
I want to be able to read and print the names and the total number of characters of the names
so far i have created a structure and a list
typedef struct struct_data_uc{
int *uc_number;
char *uc_name;
} S_data_uc;
typedef struct List_uc_data{
S_data_uc uc_data;
struct List_uc_data *next;
} L_uc_data;
L_uc_data* UC_add(L_uc_data *list, L_uc_data data_uc){
L_uc_data *new;
new=(L_uc_data*)malloc(sizeof(L_uc_data));
if(new!=NULL){
(*new)=data_uc;
new->next=list;
return new;
}
return list;
}
then i created the functions to read the list and show the results and also ree the list
void UC_free(L_uc_data *list){
L_uc_data *aux;
while(list!=NULL){
aux=list->next;
free(list);
list=aux;
}
}
void UC_read(L_uc_data *data_uc, FILE *fin, FILE *fout){
char str[MAXSTR];
if(fout!=NULL)
fscanf(fin,"%d - %c",&data_uc->uc_data.uc_number,&data_uc->uc_data.uc_name);
void UC_Show(L_uc_data *data_uc, FILE *fout, int prompt){
if(prompt==0){
fprintf(fout,"%d - %c\n",
data_uc->uc_data.uc_number,
data_uc->uc_data.uc_name);
}else{
fprintf(fout,"%d - %c\n",
data_uc->uc_data.uc_number,
data_uc->uc_data.uc_name);
}
}
than my main
int main(){
FILE *f=stdin;
L_uc_data *list=NULL, *i, data_uc;
UC_read(&data_uc, stdin, stdout);
list=UC_add(list,data_uc);
for(i=list;i!=NULL;i=i->next)
UC_Show(i,f,0);
return 0;
}
but the program wont seems to work, any help?
There are quite a few errors in your code. I've tried to fix without modifying the original code too much. See the comments for details.
typedef struct struct_data_uc{
int uc_number; // Changed from int *
char *uc_name;
} S_data_uc;
typedef struct List_uc_data{
S_data_uc uc_data;
struct List_uc_data *next;
} L_uc_data;
// Allocate a new L_uc_data and insert into list
L_uc_data* UC_add(L_uc_data *list, int number, const char *name)
{
L_uc_data *new = malloc(sizeof(L_uc_data));
if (new != NULL) {
new->uc_data.uc_number = number;
// Need strdup here to alloc mem and copy
new->uc_data.uc_name = strdup(name);
new->next = list;
return new;
}
return list;
}
// Free the entire list
void UC_free(L_uc_data *list)
{
while (list) {
L_uc_data *aux = list->next;
// Free the mem from strdup
free(list->uc_data.uc_name);
free(list);
list = aux;
}
}
// Reads the entire file and returns a new list
L_uc_data * UC_read(FILE *f)
{
char line[MAXSTR];
L_uc_data *the_list = NULL;
// Using fgets to get the entire line, then sscanf to parse
while (fgets(line, MAXSTR, f)) {
int number;
char name[MAXSTR];
// Remember to check the return from sscanf
if (2 == sscanf(line, "%d - %[^\n]", &number, name)) {
// Add to list
the_list = UC_add(the_list, number, name);
}
}
return the_list;
}
// Print the entire list
void UC_show(L_uc_data *list, FILE *fout)
{
while (list) {
fprintf(fout, "%d - %s\n", list->uc_data.uc_number, list->uc_data.uc_name);
list = list->next;
}
}
int main()
{
L_uc_data *list = UC_read(stdin);
UC_show(list, stdout);
UC_free(list);
return 0;
}
You're making (at least) 2 mistakes. First, you are passing stdin as the argument to UC_Show, where an attempt is made to write to it. 2nd, you are not checking the value returned by printf, which is almost certainly going to indicate an error and set errno to EBADF to tell you exactly what the error is. You cannot write to stdin.
I have a program coded in Windows 10, and it works perfectly.
In this case (I'm going to simplify this overly because this is where the program fails), it takes a simple linked list of 200 elements (struct) and copies the data of the first 100 elements into an array of 100 elements (array[100]).
The program works perfectly on Windows, but it doesn't copy a single struct on Ubuntu.
Is there any difference between the GCC compiler from both systems that can cause this?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 100
typedef struct{
int id;
char title[100];
char director[100];
char genre[100];
int likes;
int number_of_voters;
float average_rating;
int year;
int cost;
char color[100];
float ratingW;
}Movie;
struct Node{
Movie movie;
struct Node *next;
};
//Simply linked list
typedef struct{
struct Node *head;
}List;
//Array
typedef struct{
Movie movies[SIZE];
int num; //number of elements
}Array;
void Initialize(List *l);
void PrintList(List l);
void InsertNode(List *l, Movie reg);
void PrintMovie(Movie mov);
void PrintArray(Array arr);
void FromListToArray(List l, Array *arr);
int main(){
List sls;
Array a;
Initialize(&sls);
PrintList(sls); //Prints 200 movies, and shows the message "Movies loaded in the list ::: 200"
FromListToArray(sls, &a);
PrintArray(a); //Doesn't print any movie, and shows the message "Movies loaded in the array ::: 0"
return 0;
}
//Initializes the list
void Initialize(List *l){
l->head = NULL;
}
void PrintList(List l){
struct Node *p;
p = l.head;
while(p != NULL)
{
PrintMovie(p->movie);
p = p->next;
}
}
//Inserts a node at the beginning of the list
void InsertNode(List *l, Movie reg){
struct Node *r;
r = (struct Node *) malloc (sizeof(struct Node));;
if (r == NULL){
printf("No memory!\n");
}
else{
r->movie.id = reg.id;
strcpy(r->movie.title, reg.title);
strcpy(r->movie.director, reg.director);
strcpy(r->movie.genre, reg.genre);
r->movie.likes = reg.likes;
r->movie.number_of_voters = reg.number_of_voters;
r->movie.average_rating = reg.average_rating;
r->movie.year = reg.year;
r->movie.cost = reg.cost;
strcpy(r->movie.color, reg.color);
r->movie.ratingW = reg.ratingW;
r->next = l->head;
l->head = r;
}
}
/*Copies the data from a txt file into a simply linked list.
Line 1 is the id of the first movie, line 2 the title... line 10 is the color.
Line 11 is the id of the second movie, and so on... This repeated 200 times (200 movies = 2000 lines)*/
void FromTxtToList(List *l, FILE *f){
int j = 0;
char cad[100];
Movie reg;
f = fopen("movies.txt", "r");
if (f == NULL){
printf("Error, could not open the file\n");
}
else{
while(!feof(f)){
fgets(cad, 100, f);
reg.id = atoi(cad);
fgets(cad, 100, f);
strcpy(reg.title, cad);
fgets(cad, 100, f);
strcpy(reg.director, cad);
fgets(cad, 100, f);
strcpy(reg.genre, cad);
fgets(cad, 100, f);
reg.likes = atoi(cad);
fgets(cad, 100, f);
reg.number_of_voters = atoi(cad);
fgets(cad, 100, f);
reg.average_rating = atof(cad);
fgets(cad, 100, f);
reg.year = atoi(cad);
fgets(cad, 100, f);
reg.cost = atoi(cad);
fgets(cad, 100, f);
strcpy(reg.color, cad);
reg.ratingW = 0;
InsertNode(l, reg);
j++;
}
}
fclose(f);
printf("Movies loaded in the list ::: %d\n", j);
}
void PrintMovie(Movie mov){
printf("///////////////////////////////////\n");
printf("Id: %d\n", mov.id);
printf("Title: %s", mov.title);
printf("Director: %s", mov.director);
printf("Genre: %s", mov.genre);
printf("Likes: %d\n", mov.likes);
printf("Number of voters: %d\n", mov.number_of_voters);
printf("Average rating: %.2f\n", mov.average_rating);
printf("Year: %d\n", mov.year);
printf("Cost: $%d\n", mov.cost);
printf("Color or BW: %s", mov.color);
printf("Rating: %.2f\n", mov.ratingW);
printf("///////////////////////////////////\n");
}
void PrintArray(Array arr){
int i;
printf("\nArray: \n");
for(i=0; i < arr.num; i++){
PrintMovie(arr.movies[i])
}
}
void FromListToArray(List l, Array *arr){
int i = 0;
arr->num = 0;
struct Node *p;
p = l.head;
while ((p != NULL) && (arr->num < SIZE)){
if (strcmp(p->movie.color, "Color\n")==0){
//If I find a "colored" movie (color = "Color")
//Copy the data into the array
arr->movies[i].id = p->movie.id;
strcpy(arr->movies[i].title, p->movie.title);
strcpy(arr->movies[i].director, p->movie.director);
strcpy(arr->movies[i].genre, p->movie.genre);
arr->movies[i].likes = p->movie.likes;
arr->movies[i].number_of_voters = p->movie.number_of_voters;
arr->movies[i].average_rating = p->movie.average_rating;
arr->movies[i].year = p->movie.year;
arr->movies[i].cost = p->movie.cost;
strcpy(arr->movies[i].color, p->movie.color);
arr->movies[i].ratingW = p->movie.ratingW;
arr->num++;
i++;
}
p = p->next;
}
printf("Movies loaded in the array ::: %d\n", arr->num);
}
First, in function PrintArray
there is a miising ";" in the line PrintMovie(arr.movies[i]).
Second, there is an issue with while(!feof(f)) in the function FromTxtToList.
Apparently feof() is TRUE only after the end of file (EOF) is read, not when EOF is reached. See here and here.
A possible fix could be if(fgets(cad, 100, f)==NULL) break;
instead of fgets(cad, 100, f);
in function FromTxtToList.
Also the movies.txt file should end with only one empty line (otherwise you will get "Color" instead of "Color\n" for the last movie).
Here is my struct:
struct Car{
char plateNum[10];
char returnDate[7];
int milage;
float income;
struct Car * next;
};
typedef struct Car Car;
I need to use fwrite and fread to store the value and load back in after. Is there an easy way?
To write a LL to a file
// Be sure to have opened the file in binary mode
Car *x = head;
// Walk the list and write each node.
// No need to write the next field - which happens to be the last one.
// v-----------------v size of data before the `next` field
while (x && fwrite(x, offsetof(Car, next), 1, out_stream) == 1) {
x = x->next;
}
To read records from a file into a LL and return the head node:
#include <stddef.h>
// Be sure to have opened the file in binary mode
Car *ReadCars(FILE *in_stream) {
Car Top;
Top.next = NULL; // code only uses the `next` field of Top
Car *previous = &Top;
Car x;
// While another record was successfully read ...
while (fread(&x, offsetof(Car, next), 1, in_stream) == 1) {
// Fill the next field
x.next = NULL;
// Allocate space and copy
previous->next = malloc(sizeof *(previous->next));
assert(previous->next);
*(previous->next) = x;
// Advance to the next
previous = previous->next;
}
return Top.next;
}
The following was written off the cuff by me and has not been tested, so it might need tweaking. Please also note; for the sake of time, I have not tested the return value of fwrite and fread or checked for read errors. YOU SHOULD DO THIS.
Writing the file
int length = lengthOfList(bar); // Assuming you've already created bar as a linked list of Cars
Car foo[length];
putLinkedListIntoArray(&bar, foo);
FILE* fh = NULL;
if((fh = fopen("filename", "wb")) == NULL) {
// Error and die
}
fwrite(&length, sizeof(int), 1, fh);
fwrite(bar, sizeof(Car), length, fh);
fclose(fh);
Reading the file
FILE* fh = NULL;
if((fh = fopen("filename", "rb")) == NULL) {
// Error and die
}
int length;
fread(&length, sizeof(int), 1, fh);
Car foo[length];
fread(foo, sizeof(Car), length, fh);
fclose(fh);
relinkCarList(foo, length);
Functions
int lengthOfList(Car* start) {
int length;
for(length = 0; start->next != NULL; length++) {
start = start->next;
}
return length;
}
void putLinkedListIntoArray(Car* start, Car* array) {
for(int i = 0; start->next != NULL; i++) {
array[i] = *start;
start = start->next;
}
}
void relinkCarList(Car* array, int length) {
for(int i = 0; i < length; i++) {
if(i < length - 1) {
array[i].next = array[i + 1].next;
}
}
}
I have a struct like this;
struct abc
{
char bbb[10];
char ccc[4];
char ddd[6];
int i1;
int i2;
struct abc *prior, *next;
};
struct abb *start, *last, *this, *temp;
I have a lot of code using these pointers start,last, etc. so I would like to use them but I would like to add pointers to the struct and pointers within the struct to accomplish the following:
load the struct up with data then depending on the value of i1 for example, display and change data when the value of i1 = 0 or when the value of i1 = 1 or the contents of the struct regardless of the value of i1. And, at the end of the day save the whole struct to file with changes made in any of the three conditions.
I thought to have added pointers such as these:
struct abc *prior1, *next1;
struct abc *prior2, *next2;
};
struct abb *start1, *last1...etc.
struct abb *start2, *last2...etc.
I can have:
start = start1;
last = last1;
But how then do I reference
prior1
next1
Or tell me a better way of doing this.
It sounds like you're asking for advice on a serialization strategy. I'd suggest writing free operator<< and operator>> methods for handling I/O on your struct, where the pointers are referenced in a map that assigns an ascending numeric ID value to each. When writing, upon visiting each struct pointer, you check the map, and if the ID is found, you simply write out the ID and nothing else. Otherwise, you add the next available ID to the map, assign the ID to the pointer, and then write out the entire struct. When reading, you reverse the process, reading in each ID and looking in the map for that pointer. If it's not found, you have the first instance, so you proceed to read in the entire struct and store it in the map. Otherwise, you retrieve the previously read struct from the map and assign the pointer. This general strategy is used by CArchive serialization (Microsoft Foundation Classes). There may be some open source libraries to do the same.
Your question is not very clear.
You might just need to check out C pointers, and such syntax as start->prior which references the value of prior contained within start.
E.g.
// Set the i1 value of start to 42
start->i1 = 42;
printf("The answer is: %d\n", start->i2);
Or you might want to have the equivalent of methods to interact with one of your structures.
For example:
struct abc
{
char bbb[10];
char ccc[4];
char ddd[6];
int i1;
int i2;
struct abc *prior, *next;
};
struct abb *start, *last, *this, *temp;
/**
* Set the BBB of struct target to a given value.
* #param value the value to set
* #return pointer to the target
*/
struct abc *setBBB(struct abc *target, char *value)
{
strncpy(target->bbb, value, sizeof(target->bbb));
return target;
}
char *getBBB(struct abc *target)
{
return target->bbb;
}
struct abc *next(struct abc *target)
{
return target->next;
}
int getI1(struct abc *target)
{
return target->i1;
}
to accomplish the following: load the struct up with data then
depending on the value of i1 for example, display and change data when
the value of i1 = 0 or when the value of i1 = 1 or the contents of the
struct regardless of the value of i1.
This you would do with:
switch(getI1(start))
{
case 0:
setBBB(start, "Hello"); // NOTE: to treat this as a string,
// at most 9 characters are allowed,
// else the terminating zero will be lost.
break;
case 1:
setBBB(start, "World"); // ditto.
break;
...
}
And, at the end of the day save the whole struct to file with changes made in any of the three conditions.
You want to save the structure to a file, you need something like
int saveStruct(char *filename, int position, struct abc *target)
{
int ret;
FILE *fp;
if (NULL == (fp = fopen(filename, "r+")))
{
if (NULL == (fp = fopen(filename, "w")))
{
return -1; // Error.
}
}
fseek(fp, position * sizeof(struct abc), SEEK_SET);
ret = fwrite(target, sizeof(struct abc), 1, fp);
fclose(fp);
return ret;
}
The above has some problems, though. The values of pointers (*prior and *next) will become meaningless once saved to disk, and will remain so when loaded from disk. So the loadStruct() function will require as additional parameters the values of *prior and *next to be restored.
Also, you can't (but it depends on the platform) save a structure at position #1 if you haven't saved one at position #0 before. And each save performs an expensive open-and-close.
int loadStruct(char *filename, int position, struct abc *target)
{
int ret;
FILE *fp;
if (NULL == (fp = fopen(filename, "r")))
{
return -1; // Error.
}
fseek(fp, position * sizeof(struct abc), SEEK_SET);
ret = fread(target, sizeof(struct abc), 1, fp);
target->prior = NULL;
target->next = NULL;
fclose(fp);
return ret;
}
// The above, tested with the test case below, returns 'Hello!' as expected.
int main()
{
struct abc *start, *another;
start = malloc(sizeof(struct abc));
setBBB(start, "Hello!");
saveStruct("file.bin", 0, start);
another = malloc(sizeof(struct abc));
loadStruct("file.bin", 0, another);
printf("Re-read: %s.\n", getBBB(another));
}
I write a program to count the frequency word count using hash table, but I don't how to sort it.
I use struct to store value and count.
My hash code generate function is using module and my hash table is using by linked list.
1.My question is how do I sort them by frequency?
2.I am wondering that why my printed execute time is always zero, but I check it for many time. Where is the wrong way?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#define HASHSIZE 29989
#define FACTOR 31
#define VOCABULARYSIZE 30
typedef struct HashNode HashNode;
struct HashNode{
char* voc;//vocabulary
int freq;//frequency
struct HashNode *next;//pointed to the same hashcode
//but actually are different numbers
};
HashNode *HashTable[HASHSIZE] = {NULL,0,NULL};//an array of pointers
unsigned int HashCode(const char *pVoc){//generate hashcode
unsigned int index = 0;
int n = strlen(pVoc);
int i = 0;
for(; i < n; i ++)
index = FACTOR*index + pVoc[i];
return index % HASHSIZE;
}
void InsertVocabulary(const char *pVoc){//insert vocabulary to hash table
HashNode *ptr;
unsigned int index = HashCode(pVoc);
for(ptr = HashTable[index]; ptr != NULL; ptr = ptr -> next){//search if already exist
if(!strcmp (pVoc, ptr -> voc)){
(ptr->freq)++;
return;
}
}
ptr = (HashNode*)malloc(sizeof(HashNode));//if doesn't exist, create it
ptr -> freq = 1;
ptr -> voc = (char*)malloc(strlen(pVoc)+1);
strcpy(ptr -> voc, pVoc);
ptr -> next = HashTable[index];
HashTable[index] = ptr;
}
void ReadVocabularyTOHashTable(const char *path){
FILE *pFile;
char buffer[VOCABULARYSIZE];
pFile = fopen(path, "r");//open file for read
if(pFile == NULL)
perror("Fail to Read!\n");//error message
char ch;
int i =0;
do{
ch = fgetc(pFile);
if(isalpha(ch))
buffer[i++] = tolower(ch);//all convert to lowercase
else{
buffer[i] = '\0';//c-style string
i = 0;
if(!isalpha(buffer[0]))
continue;//blank line
else //printf("%s\n",buffer);
InsertVocabulary(buffer);
}
}while(ch != EOF);
fclose(pFile);
}
void WriteVocabularyTOHashTable(const char *path){
FILE *pFile;
pFile = fopen(path, "w");
if(pFile == NULL)
perror("Fail to Write\n");
int i = 0;
for(; i < HASHSIZE; i++){
HashNode *ptr = HashTable[i];
for(; ptr != NULL; ptr = ptr -> next){
fprintf(pFile, "Vocabulary:%s,Count:%d\n", ptr -> voc, ptr -> freq);
if(ptr -> next == NULL)
fprintf(pFile,"\n");
}
}
fclose(pFile);
}
int main(void){
time_t start, end;
time(&start);
ReadVocabularyTOHashTable("test.txt");
WriteVocabularyTOHashTable("result.txt");
time(&end);
double diff = difftime(end,start);
printf("%.21f seconds.\n", diff);
system("pause");
return 0;
}
This is an answer to your first question, sorting by frequency. Every hash node in your table is a distinct vocabulary entry. Some hash to the same code (thus your collision chains) but eventually you have one HashNode for every unique entry. To sort them by frequency with minimal disturbing of your existing code you can use qsort() with a pointer list (or any other sort of your choice) with relative ease.
Note: the most efficient way to do this would be to maintain a sorted linked-list during vocab-insert, and you may want to consider that. This code assumes you already have a hash table populated and need to get the frequencies out in sorted order of highest to lowest.
First, keep a running tally of all unique insertions. Simple enough, just add a counter to your allocation subsection:
gVocabCount++; // increment with each unique entry.
ptr = (HashNode*)malloc(sizeof(HashNode));//if doesn't exist, create it
ptr -> freq = 1;
ptr -> voc = (char*)malloc(strlen(pVoc)+1);
strcpy(ptr -> voc, pVoc);
ptr -> next = HashTable[index];
HashTable[index] = ptr;
Next allocate a list of pointers to HashNodes as large as your total unique vocab-count. then walk your entire hash table, including collision chains, and put each node into a slot in this list. The list better be the same size as your total node count or you did something wrong:
HashNode **nodeList = malloc(gVocabCount * sizeof(HashNode*));
int i;
int idx = 0;
for (i=0;i<HASHSIZE;++i)
{
HashNode* p = HashTable[i];
while (p)
{
nodeList[idx++] = p;
p = p->next;
}
}
So now we have a list of all unique node pointers. We need a comparison function to send to qsort(). We want the items with the largest numbers to be at the head of the list.
int compare_nodeptr(void* left, void* right)
{
return (*(HashNode**)right)->freq - (*(HashNode**)left)->freq;
}
And finally, fire qsort() to sort your pointer list.
qsort(nodeList, gVocabCount, sizeof(HashNode*), compare_nodeptr);
The nodeList array of HashNode pointers will have all of your nodes sorted in descending frequency:
for (i=0; i<gVocabCount; ++i)
printf("Vocabulary:%s,Count:%d\n", nodeList[i]->voc, nodeList[i]->freq);
Finally, don't forget to free the list:
free(nodeList);
As I said at the beginning, the most efficient way to do this would be to use a sorted linked list that pulls an incremented value (by definition all new entries can go to the end) and runs an insertion sort to slip it back into the right place. In the end that list will look virtually identical to what the above code would create (like-count-order not withstanding; i.e. a->freq = 5 and b->freq = 5, either a-b or b-a can happen).
Hope this helps.
EDIT: Updated to show OP an idea of what the Write function that outputs sorted data may look like:
static int compare_nodeptr(const void* left, const void* right)
{
return (*(const HashNode**)right)->freq - (*(const HashNode**)left)->freq;
}
void WriteVocabularyTOHashTable(const char *path)
{
HashNode **nodeList = NULL;
size_t i=0;
size_t idx = 0;
FILE* pFile = fopen(path, "w");
if(pFile == NULL)
{
perror("Fail to Write\n");
return;
}
nodeList = malloc(gVocabCount * sizeof(HashNode*));
for (i=0,idx=0;i<HASHSIZE;++i)
{
HashNode* p = HashTable[i];
while (p)
{
nodeList[idx++] = p;
p = p->next;
}
}
// send to qsort()
qsort(nodeList, idx, sizeof(HashNode*), compare_nodeptr);
for(i=0; i < idx; i++)
fprintf(pFile, "Vocabulary:%s,Count:%d\n", nodeList[i]->voc, nodeList[i]->freq);
fflush(pFile);
fclose(pFile);
free(nodeList);
}
Something like that, anyway. From the OP's test file, these are the top few lines of output:
Vocabulary:the, Count:912
Vocabulary:of, Count:414
Vocabulary:to, Count:396
Vocabulary:a, Count:388
Vocabulary:that, Count:260
Vocabulary:in, Count:258
Vocabulary:and, Count:221
Vocabulary:is, Count:220
Vocabulary:it, Count:215
Vocabulary:unix, Count:176
Vocabulary:for, Count:142
Vocabulary:as, Count:121
Vocabulary:on, Count:111
Vocabulary:you, Count:107
Vocabulary:user, Count:102
Vocabulary:s, Count:102