Merging and Sorting struct array in c - c

I am trying to make a c99 program that reports the number of bytes downloaded by devices using WiFi.
It takes in a packet file as input, and each packet is sorted into an array of structs that contain the mac id and size of packet.
Now I am trying to sort out the array of structs in ascending order, and add the bytes of the same mac address and delete the added record.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PACKETS "sample-packets"
#define MAXMACADD 500
struct packetStruct {
char mac[17];
int size;
} packetStruct[MAXMACADD];
struct output {
char mac[17];
int size;
} output[MAXMACADD];
void sortSize(struct packetStruct* macadd, int n) {
int j, i;
for (i = 1; i < n; i++) {
for (j = 0; j < n - i; j++) {
if (macadd[j].size < macadd[j + 1].size) {
struct packetStruct temp = macadd[j];
macadd[j] = macadd[j + 1];
macadd[j + 1] = temp;
}
}
}
}
void mergeMac2(struct packetStruct* macadd, struct output* output, int n) {
int i, j, k=0;
for (i = 0; i < n; i++) {
if (strcmp(macadd[i].mac, "\0") != 0) {
for (j = 0; j < n; j++) {
if (strcmp(macadd[j].mac, "\0") != 0) {
if (strcmp(macadd[i].mac, macadd[j].mac) == 0){
strcpy(output[k].mac, macadd[i].mac);
output[k].size += macadd[i].size;
macadd[i].size = 0;
}
} else j++;
}
} else i++;
k++;
}
}
int readpacket() {
char *token;
char buf[60];
int size;
FILE *packet = fopen(PACKETS, "r"); //open packet file in read mode
int i = 0;
int j = 0; //loop control variables
int k = 0;
while (fgets(buf, sizeof (buf), packet) != '\0') {
token = strtok(buf, "\t"); //tokenize buf and point to time
token = strtok(NULL, "\t"); //point to sender mac add
token = strtok(NULL, "\t"); //point to dest mac add
strcpy(packetStruct[i].mac, token);
token = strtok(NULL, "\t"); //point to byte size
packetStruct[i].size += atoi(token);
//printf("%i. %s\t%d\n", i, packetStruct[i].mac, packetStruct[i].size);
i++;
}
fclose(packet); //close packet file
sortSize(packetStruct, i);
mergeMac2(packetStruct, output, i);
for (i = 0; i < 20; i++) {
printf("%i. %s\t%d\n", i, packetStruct[i].mac, packetStruct[i].size);
}
for (i=0; i < 20; i++){
printf("%i. %s\t%d\n", i+1, output[i].mac, output[i].size);
}
return 0;
}
void main(int argc, char *argv[]) {
if (argc != 2) {
printf("%s: program needs 1 argument, but there was %d\n", argv[0], argc - 1);
exit(EXIT_FAILURE);
} else {
if (strcmp(argv[1], "packets") != 0) {
printf("%s: program expected command 'packets', but you wrote %s\n", argv[0], argv[1]);
} else {
if (readpacket() != 0) {
exit(EXIT_FAILURE);
}
}
}
}
You can compile in command line using:
$: gcc main.c
run using:
$: ./a.out packets
It is sorting it fine but the merging is the issue. Should i use another struct called output and store the values in there or should i just merge up the current array? time-efficiency is not necessary.
I can provide the sample input file if it would be useful.

If I have understood you correctly you want to add all sizes from the same mac address. Then mergeMac2() should be like that (edit: now it's a version that completely preserves the original macadd array):
// return number of elements in output
int mergeMac2(struct packetStruct* macadd, struct output* output, int n) {
int i, j, k=0;
for (i = 0; i < n; i++) {
// '"\0" makes no difference to "" here
//if (strcmp(macadd[i].mac, "\0") != 0) {
if (strcmp(macadd[i].mac, "") != 0) {
// search in putput;
for( j=0; j<k && strcmp( macadd[i].mac, output[j].mac ) != 0; j++ ) ;
if( j == k ) {
// not yet created in output
strcpy( output[k].mac, macadd[i].mac );
output[k].size = macadd[i].size;
k++;
} else {
output[j].size += macadd[i].size;
}
}
}
return k;
}
Now you have added all sizes for each mac address to the first struct element that originally contained that address. Your second printf() loop should now be:
int j, n;
...
n = mergeMac2( packetStruct, output, i );
for( j=0; j<n; j++ ) {
...
}
At least I think you should first merge and then sort but that depends on what you want to achieve, of course.

void mergeMac2(struct packetStruct* macadd, struct output* output, int n)
{
int i, j, k = 0;
for (i = 0; i < n; i++) {
/*
* If size is 0, the packet has already been processed.
*/
if (macadd[i].size == 0)
continue;
memcpy(&output[k], &macadd[i], sizeof(struct packetStruct));
for (j = i+1; j < n; j++) {
/*
* If size is 0, the packet has already been processed.
*/
if (macadd[j].size == 0)
continue;
if (strcmp(macadd[i].mac, macadd[j].mac) == 0) {
output[k].size += macadd[j].size;
/*
* Set size to 0 so that these packets won't be
* processed in the next pass.
*/
macadd[j].size = 0;
}
}
k++;
}
}

Related

C program to compare each word of a 2D array to all words of the order 2D array

I created 2 2D arrays containing words. For example:
loadKey[25][30] =
{
{'J','a','v','a','\0'},
{'P','y','t','h','o','n','\0'},
{'C','+','+','\0'},
{'H','T','M','L','\0'},
{'S','Q','L','\0'}
// ... 20 other words here
};
resume[189][30] =
{
{'L','a','l','a','\0'},
{'H','i','h','i','h','i','\0'},
{'C','+','+','\0'},
{'Y','o','Y','o','\0'},
{'S','Q','L','\0'}
// ... 184 other words here
};
I would like to compare each word of a loadKey[] to all words of resume[] to count how many times 25 words of loadKey[] matched of words of resume[]. I tried the strcmp(loadKey[i], resume[j]) but it's pointer of array.
Anyone can help me to solve this problem? Thanks so much!
My program code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PAUSE myPause()
#define KEYWORD 25
#define WORDS 250
#define MAX_LETTER 30
//*********************************************
// FUNCTION PROTOTYPES
void compAndCount(char loadKey[][MAX_LETTER], char resume[][MAX_LETTER]);
void myPause();
void readAndLoadKeyword(char loadKey[][MAX_LETTER]);
// MAIN FUNCTION
int main() {
char loadKey[KEYWORD][MAX_LETTER];
char resume[WORDS][MAX_LETTER];
// load keywords from keywords file into array loadKey[]
readAndLoadKeyword(loadKey);
for (int j = 0; j < KEYWORD; j++)
{
puts(loadKey[j]);
}
puts("\n");
// compare and count the occurrences of keyword in resumes file
compAndCount(loadKey, resume);
}
// FUNTIONS
void compAndCount(char loadKey[][MAX_LETTER], char resume[][MAX_LETTER]) {
FILE* fpr;
fpr = fopen("resumes.txt", "r");
int r = 0, count = 0, num = 0, res = 0;
char temp;
while ((temp = fgetc(fpr)) != EOF) {
if (temp != ' ' && temp != '\n') {
resume[res][r] = temp;
r++;
}
else
{
resume[res][r] = '\0';
r = 0;
res++;
}
}
printf("words in resume file %i\n", res);
for (int j = 0; j < res; j++)
{
puts(resume[j]);
}
puts("\n");
/*
// way 1 to compare and count (WRONG?)
for (int i = 0; i < res; i++) {
if (i < KEYWORD) {
scanf(" %[^\n]", loadKey[i]);
}
scanf(" %[^\n]", resume[i]);
}
for (int k = 0; k < KEYWORD; k++) {
for (int l = 0; l < res; l++) {
if (strcmp(loadKey[k], resume[l]) == 0)
count++;
}
}*/
/*
// way 2 to compare and count (WRONG?)
char key[MAX_LETTER] = {'\0'}, r[MAX_LETTER] = {'\0'};
for (int i = 0; i < KEYWORD; i++) {
strcpy(key, loadKey[i]);
for (int l = 0; l < res; l++) {
strcpy(r, resume[l]);
if (strcmp(key, r) == 0)
count++;
}
}
*/
printf("Resume Rating: %i\n", count);
fclose(fpr);
} // end compAndCount
void myPause() {
puts("\nPress ENTER to continue\n");
exit(0);
}
void readAndLoadKeyword(char loadKey[][MAX_LETTER]) {
FILE* fp;
fp = fopen("keywords.txt", "r");
char ch;
int row = 0, col = 0;
if (fp == NULL) {
puts("Not able to open keyword file!");
PAUSE;
}
// load 25 keywords and ',' into an array line[]
char line[181]; // 180 characters + '\0'
fgets(line, 181, fp);
puts(line);
puts("\n");
// load 25 words in array line[] into array loadKey[]
for (int i = 0; i < 180; i++) {
ch = line[i];
if (ch != ',') {
loadKey[row][col] = ch;
col++;
}
else {
loadKey[row][col] = '\0';
col = 0;
row++;
}
}
fclose(fp);
} // end readAndLoadKeyword
You can use string literals instead of {} of chars
Arrays can have a different number of columns and rows.
char loadKey[25][30] =
{
"Java",
"Python",
// ... more words here
};
char resume[189][30] =
{
"Lala",
"Hihihi",
"C++",
// more
};
//lkr - number of loadKer rows
//lkr - number of loadKer columns
//rr - number of resume rows
//rc - number of resume columns
//rep - count duplicates
size_t count(size_t lkr, size_t lkc, size_t rr, size_t rc, char (*loadKey)[lkc], char (*resume)[rc], int rep)
{
size_t result = 0;
for(size_t lkrow = 0; lkrow < lkr; lkrow++)
{
for(size_t rrow = 0; rrow < rr; lrow++)
{
if(!strcmp(loadKey[lkrow], resume[rrow]))
{
result++;
if(!rep) break;
}
}
}
return result;
}
If the same string can be present in the resume array more than once and oyu want to count duplicates as well rep parameter should be non-zero.
Example usage:
int main(void)
{
size_t cnt = count(25, 30, 189, 30, loadKey, resume, 0);
printf("%zu\n", count);
}

I wrote a code to print the union and intersection of two strings sets. I allocated memory in some parts.The last part is not working properly

The last part of the code where I am allocating memory to common_set is not working whereas other mallocs worked fine above. Similarly when I freed the unique_set for the third time it is not done properly. I am not able to get the printf sentences which I wrote after them. I am not able to free the pointer of the union_set. Also I am not able to allocate memory to a new pointer common_pointer.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
void dict(char **str, int man, int MAY) {
char temp[MAY];
for (int i = 0; i < man; ++i) {
for (int j = i + 1; j < man; ++j) {
if (strcmp(str[i], str[j]) > 0) {
strcpy(temp, str[i]);
strcpy(str[i], str[j]);
strcpy(str[j], temp);
}
}
}
}
//int k = 0;
char **unique_set;
char **a_new_sumset;
int unique(char **s, int l, int d, int k) {
if (d == l) {
unique_set[k] = (char*)malloc((strlen(s[l]) + 1) * sizeof(char));
strcpy(unique_set[k], s[l]);
return k;
} else {
for (int i = l; i < d; i++) {
if (strcmp(s[i], s[i + 1]) == 0 && i + 1 == d) {
unique_set[k] = (char*)malloc((strlen(s[i + 1]) + 1) * sizeof(char));
strcpy(unique_set[k], s[i + 1]);
return k;
//printf("demo: %s\n", unique_set[k]);
} else
if (strcmp(s[i], s[i + 1]) != 0) {
unique_set[k] = (char*)malloc((strlen(s[i]) + 1) * sizeof(char));
strcpy(unique_set[k], s[i]);
//printf("demo1: %s\n", unique_set[k]);
k++;
unique(s, i + 1, d, k);
break;
}
}
}
}
char **common_set;
char **intersection(char **sum_set, int d) {
int k = 0;
for (int i = 0; i < d; i++) {
if (strcmp(sum_set[i], sum_set[i + 1]) == 0 && strcmp(sum_set[i], sum_set[i + 2]) != 0 && i + 2 < d) {
common_set[k] = (char*)malloc((strlen(sum_set[i]) + 1) * sizeof(char));
strcpy(common_set[k], sum_set[i]);
k++;
} else
if (strcmp(sum_set[i], sum_set[i + 1]) == 0 && i + 2 > d) {
common_set[k] = (char*)malloc((strlen(sum_set[i]) + 1) * sizeof(char));
strcpy(common_set[k], sum_set[i]);
k++;
}
}
return common_set;
}
#define maxx1 3 // number of words in grp 1
#define maxy1 10 // word limit for the first group of words
#define maxx2 3 // number of words in grp2
#define maxy2 10 // word limit for the next group of words
int main() {
char **sum_set;
char **str_g1;
char **str_g2;
char words1[maxy1];
str_g1 = (char**)malloc(maxx1 * sizeof(char*));
char words2[maxy2];
str_g2 = (char**)malloc(maxx2 * sizeof(char*));
printf("Enter the first group:\n");
for (int i = 0; i < maxx1; i++) {
gets(words1);
str_g1[i] = (char*)malloc((strlen(words1) + 1) * sizeof(char));
strcpy(str_g1[i], words1);
//puts(ptr[i]);
//printf("%d", i);
}
printf("Enter the second group:\n");
for (int i = 0; i < maxx2; i++) {
gets(words2);
str_g2[i] = (char*)malloc((strlen(words2) + 1) * sizeof(char));
strcpy(str_g2[i], words2);
//puts(ptr[i]);
//printf("%d", i);
}
dict(str_g1, maxx1, maxy1); //to lexicographically arrange the string 1 group
dict(str_g2, maxx2, maxy2);
sum_set = (char**)malloc((maxx1 + maxx2) * sizeof(char*));
a_new_sumset = (char**)malloc((maxx1 + maxx2) * sizeof(char*));
for (int i = 0; i < maxx1; i++) {
sum_set[i] = (char*)malloc((strlen(str_g1[i]) + 1) * sizeof(char));
strcpy(sum_set[i], str_g1[i]);
//puts(sum_set[i]);
}
for (int i = 0; i < maxx2; i++) {
sum_set[i + maxx1] = (char*)malloc((strlen(str_g2[i]) + 1) * sizeof(char));
strcpy(sum_set[i + maxx2], str_g2[i]);
//puts(sum_set[i + maxx1]);
}
dict(sum_set, maxx1 + maxx2, maxy1 + maxy2);
unique_set = (char**)malloc((maxx1) * sizeof(char*));//allocating memory to next string group to compute its set
int k = unique(str_g1, 0, maxx1 - 1, 0);
printf("%d \n", k);
printf("The set of the string A in arranged order is:\n");
for (int i = 0; i <= k; i++) {
a_new_sumset[i] = (char*)malloc((strlen(unique_set[i]) + 1) * sizeof(char));
strcpy(a_new_sumset[i], unique_set[i]);
puts(unique_set[i]);
//
}
//printf("freed the pointers\n");
for (int i = 0; i <= k; ++i) {//freeing the arrays
free(unique_set[i]);
}
free(unique_set);
//printf("freed the pointers\n");//freeing the top pointer
int a = k;
unique_set = (char**)malloc((maxx2) * sizeof(char*));//allocating memory to next string group to compute its set
k = unique(str_g2, 0, maxx2 - 1, 0);
int b = k;
printf("The set of the string B in arranged order is:\n");
for (int i = 0; i <= k; i++) {
a_new_sumset[i + 1 + a] = (char*)malloc((strlen(unique_set[i]) + 1) * sizeof(char));
strcpy(a_new_sumset[i + a + 1], unique_set[i]);
puts(unique_set[i]);
//strcpy(a_new_sumset[i + a + 1], unique_set[i]);
}
printf("%d \n", k);
for (int i = 0; i <= k; ++i) {//freeing the arrays
free(unique_set[i]);
}
free(unique_set);//freeing the top pointer
printf("freed the pointers\n");
unique_set = (char**)malloc((a + b) * sizeof(char*));//allocating memory to unique_set for computing the union of the sets
k = unique(sum_set, 0, (maxx1 + maxx2) - 1, 0);
printf("The set of the string A+B in arranged order is:\n");
for (int i = 0; i <= k; i++)
puts(unique_set[i]);
for (int i = 0; i <= k; ++i) {//freeing the arrays
free(unique_set[i]);
}
printf("freed the pointers\n");
free(unique_set);
printf("freed the pointers\n");
printf("The intersection_set of the string A+B in arranged order is:\n");
common_set = (char**)malloc((maxx1 + maxx2) * sizeof(char*));
printf("The intersection_set of the string A+B in arranged order is:\n");
char **p;
p = intersection(a_new_sumset, (maxx1 + maxx2) - 1);
printf("The intersection_set of the string A+B in arranged order is:\n");
for (int i = 0; i <maxx1 + maxx2; i++) {
puts(p[i]);
}
}
There are many issues in the code:
in function dict, you should not copy the strings, but swap the pointers instead, which is simpler and works for any string length. Furthermore the name dict is confusing for this function, prefer a more informative name such as sort_strings:
void sort_strings(char **str, int man) {
for (int i = 0; i < man; ++i) {
for (int j = i + 1; j < man; ++j) {
if (strcmp(str[i], str[j]) > 0) {
char *temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
}
}
global variables are not necessary for this task, prefer local variables, pass destination arrays as arguments and return the number of strings as the return value.
the string allocation code is duplicated everywhere... using the strdup() function or a wrapper that tests for memory allocation failure would improve readability.
avoid variable name l, which looks confusingly similar to 1
the uniq function does not return a value in the else branch, it is unclear what it does and why it is recursive.
the loop to add the strings from the second group has a bug: strcpy(sum_set[i + maxx2], str_g2[i]); should be strcpy(sum_set[i + maxx1], str_g2[i]);. This mistake has no effect now because maxx1 == maxx2 but should be fixed.
in function intersection, you compare strings at offset d + 1 and d + 2 before testing if these index values are within the array boundaries.
selecting duplicated strings in intersection makes the assumption that strings are not duplicated in group 1 and 2.
the number of common strings is not returned, nor stored into a global variable, so main() cannot determine this number and uses maxx1 + maxx2 which is always wrong for non trivial cases.
Here is a modified version using functions to read lines, sort an array, find unique values, compute the union and intersection of sets and free the string arrays:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// allocate a copy of a string or die
char *xstrdup(const char *str) {
char *p = strdup(str);
if (p == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
return p;
}
// read a line of at most max bytes
char *get_line(int max) {
char buf[max + 1];
int i = 0;
int c;
while ((c = getchar()) != EOF && c != '\n') {
if (i < max) {
buf[i++] = (char)c;
}
}
buf[i] = '\0';
return xstrdup(buf);
}
// free the strings in an array of size n
void free_strings(char **strings, int n) {
for (int i = 0; i < n; i++) {
free(strings[i]);
}
}
// copy strings in lexicographical order
int sort_strings(char **sorted, char **str, int n) {
int i, j;
for (i = 0; i < n; i++) {
for (j = i; j > 0 && strcmp(sorted[j - 1], str[i]) > 0; j--) {
sorted[j] = sorted[j - 1];
}
sorted[j] = xstrdup(str[i]);
}
return n;
}
// copy unique strings in lexicographical order
int uniq_strings(char **unique_strings, char **strings, int n) {
int i, k = 0;
for (i = 0; i < n; i++) {
if (i == 0 || strcmp(strings[i - 1], strings[i]) != 0) {
unique_strings[k++] = xstrdup(strings[i]);
}
}
return k;
}
int intersection_strings(char **intersection_set, char **str1, int n1, char **str2, int n2) {
int i = 0, j = 0, k = 0;
while (i < n1 && j < n2) {
int c = strcmp(str1[i], str2[j]);
if (c < 0) {
i++;
} else
if (c > 0) {
j++;
} else {
intersection_set[k++] = xstrdup(str1[i]);
i++;
j++;
}
}
return k;
}
int union_strings(char **union_set, char **str1, int n1, char **str2, int n2) {
int i = 0, j = 0, k = 0;
while (i < n1 && j < n2) {
int c = strcmp(str1[i], str2[j]);
if (c < 0) {
union_set[k++] = xstrdup(str1[i]);
i++;
} else
if (c > 0) {
union_set[k++] = xstrdup(str2[j]);
j++;
} else {
union_set[k++] = xstrdup(str1[i]);
i++;
j++;
}
}
while (i < n1) {
union_set[k++] = xstrdup(str1[i++]);
}
while (j < n2) {
union_set[k++] = xstrdup(str2[j++]);
}
return k;
}
void print_set(char **set, int n, const char *desc) {
if (desc) {
printf("%s", desc);
}
for (int i = 0; i < n; i++) {
puts(set[i]);
}
}
#define maxx1 3 // number of words in grp 1
#define maxy1 10 // word limit for the first group of words
#define maxx2 3 // number of words in grp2
#define maxy2 10 // word limit for the second group of words
int main() {
char *str_g1[maxx1];
char *str_s1[maxx1];
char *str_u1[maxx1];
char *str_g2[maxx2];
char *str_s2[maxx2];
char *str_u2[maxx2];
char *union_set[maxx1 + maxx2];
char *intersection_set[maxx1 < maxx2 ? maxx1 : maxx2];
printf("Enter the first group:\n");
for (int i = 0; i < maxx1; i++) {
str_g1[i] = get_line(maxy1);
}
printf("Enter the second group:\n");
for (int i = 0; i < maxx2; i++) {
str_g2[i] = get_line(maxy2);
}
sort_strings(str_s1, str_g1, maxx1);
int n1 = uniq_strings(str_u1, str_s1, maxx1);
sort_strings(str_s2, str_g2, maxx1);
int n2 = uniq_strings(str_u2, str_s2, maxx2);
print_set(str_u1, n1, "The set of the string A in arranged order is:\n");
print_set(str_u2, n2, "The set of the string B in arranged order is:\n");
// compute union and intersection
int nu = union_strings(union_set, str_u1, n1, str_u2, n2);
int ni = intersection_strings(intersection_set, str_u1, n1, str_u2, n2);
print_set(union_set, nu, "The set of the string A+B in arranged order is:\n");
print_set(intersection_set, ni, "The intersection_set of the string A*B in arranged order is:\n");
free_strings(str_g1, maxx1);
free_strings(str_g2, maxx2);
free_strings(str_s1, maxx1);
free_strings(str_s2, maxx2);
free_strings(str_u1, n1);
free_strings(str_u2, n2);
free_strings(union_set, nu);
free_strings(intersection_set, ni);
return 0;
}
To improve on this code, you can define a structure string_set to combine the string array along with variables describing its allocated size and active count. Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct string_set {
int count, size;
char **array;
} string_set;
// allocate a copy of a string or die
char *xstrdup(const char *str) {
char *p = strdup(str);
if (p == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
return p;
}
void *xalloc(size_t n) {
void *p = malloc(n);
if (p == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
return p;
}
string_set *string_set_allocate(int size) {
string_set *set = xalloc(sizeof(*set));
set->count = 0;
set->size = size;
set->array = xalloc(sizeof(*set->array) * size);
return set;
}
void string_set_free(string_set *set) {
while (set->count --> 0) {
free(set->array[set->count]);
}
free(set->array);
free(set);
}
// read a line of at most max bytes
char *get_line(int max) {
char buf[max + 1];
int i = 0;
int c;
while ((c = getchar()) != EOF && c != '\n') {
if (i < max) {
buf[i++] = (char)c;
}
}
buf[i] = '\0';
return xstrdup(buf);
}
string_set *string_set_clone(string_set *set) {
string_set *copy = string_set_allocate(set->size);
for (int i = 0; i < set->count; i++) {
copy->array[i] = xstrdup(set->array[i]);
}
copy->count = set->count;
return copy;
}
void string_set_sort(string_set *set) {
for (int i = 0; i < set->count; i++) {
for (int j = i + 1; j < set->count; j++) {
if (strcmp(set->array[i], set->array[j]) > 0) {
char *temp = set->array[i];
set->array[i] = set->array[j];
set->array[j] = temp;
}
}
}
}
void string_set_uniq(string_set *set) {
if (set->count > 0) {
int j = 1;
for (int i = 1; i < set->count; i++) {
if (strcmp(set->array[i], set->array[j - 1]) == 0) {
free(set->array[i]);
set->array[i] = NULL;
} else {
set->array[j++] = set->array[i];
}
}
set->count = j;
}
}
string_set *string_set_intersection(string_set *set1, string_set *set2) {
int n1 = set1->count;
int n2 = set2->count;
string_set *res = string_set_allocate(n1 < n2 ? n1 : n2);
int i = 0, j = 0, k = 0;
while (i < n1 && j < n2) {
int c = strcmp(set1->array[i], set2->array[j]);
if (c < 0) {
i++;
} else
if (c > 0) {
j++;
} else {
res->array[k++] = xstrdup(set1->array[i]);
i++;
j++;
}
}
res->count = k;
return res;
}
string_set *string_set_union(string_set *set1, string_set *set2) {
int n1 = set1->count;
int n2 = set2->count;
string_set *res = string_set_allocate(n1 + n2);
int i = 0, j = 0, k = 0;
while (i < n1 && j < n2) {
int c = strcmp(set1->array[i], set2->array[j]);
if (c < 0) {
res->array[k++] = xstrdup(set1->array[i]);
i++;
} else
if (c > 0) {
res->array[k++] = xstrdup(set2->array[j]);
j++;
} else {
res->array[k++] = xstrdup(set1->array[i]);
i++;
j++;
}
}
while (i < n1) {
res->array[k++] = xstrdup(set1->array[i++]);
}
while (j < n2) {
res->array[k++] = xstrdup(set2->array[j++]);
}
res->count = k;
return res;
}
void string_set_print(string_set *set, const char *desc) {
if (desc) {
printf("%s", desc);
}
for (int i = 0; i < set->count; i++) {
puts(set->array[i]);
}
}
#define maxx1 3 // number of words in grp 1
#define maxy1 10 // word limit for the first group of words
#define maxx2 3 // number of words in grp2
#define maxy2 10 // word limit for the second group of words
int main() {
string_set *str_g1 = string_set_allocate(maxx1);
string_set *str_g2 = string_set_allocate(maxx2);
printf("Enter the first group:\n");
for (int i = 0; i < maxx1; i++) {
str_g1->array[str_g1->count++] = get_line(maxy1);
}
printf("Enter the second group:\n");
for (int i = 0; i < maxx2; i++) {
str_g2->array[str_g2->count++] = get_line(maxy2);
}
string_set *str_u1 = string_set_clone(str_g1);
string_set_sort(str_u1);
string_set_uniq(str_u1);
string_set *str_u2 = string_set_clone(str_g2);
string_set_sort(str_u2);
string_set_uniq(str_u2);
string_set_print(str_u1, "The set of the string A in arranged order is:\n");
string_set_print(str_u2, "The set of the string B in arranged order is:\n");
string_set *union_set = string_set_union(str_u1, str_u2);
string_set *intersection_set = string_set_intersection(str_u1, str_u2);
string_set_print(union_set, "The set of the string A+B in arranged order is:\n");
string_set_print(intersection_set, "The intersection set of the string A*B in arranged order is:\n");
string_set_free(str_g1);
string_set_free(str_g2);
string_set_free(str_u1);
string_set_free(str_u2);
string_set_free(union_set);
string_set_free(intersection_set);
return 0;
}
The last part of the code where I am allocating memory to common_set is not working ...
The problem in this part is not the allocation, but the implementation of the intersection function. You haven't provided a means for this function to return the number of elements in the intersection set, and you haven't initialized the allocated set space - you just try to access maxx1 + maxx2 set elements, most of which are undefined. Fixing this design would be a start.

To mimic sort command of linux, to sort lines of a text file

Sort command of linux must sort the lines of a text file and transfer the output to another file. But my code gives a runtime error. Please rectify the pointer mistakes so that output.
In which line exactly should I make changes? Because there is no output after all.
I'm pasting the whole code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void sortfile(char **arr, int linecount) {
int i, j;
char t[500];
for (i = 1; i < linecount; i++) {
for (j = 1; j < linecount; j++) {
if (strcmp(arr[j - 1], arr[j]) > 0) {
strcpy(t, arr[j - 1]);
strcpy(arr[j - 1], arr[j]);
strcpy(arr[j], t);
}
}
}
}
int main() {
FILE *fileIN, *fileOUT;
fileIN = fopen("test1.txt", "r");
unsigned long int linecount = 0;
int c;
if (fileIN == NULL) {
fclose(fileIN);
return 0;
}
while ((c = fgetc(fileIN)) != EOF) {
if (c == '\n')
linecount++;
}
printf("line count=%d", linecount);
char *arr[linecount];
char singleline[500];
int i = 0;
while (fgets(singleline, 500, fileIN) != NULL) {
arr[i] = (char*)malloc(500);
strcpy(arr[i], singleline);
i++;
}
sortfile(arr, linecount);
for (i = 0; i < linecount; i++) {
printf("%s\n", arr[i]);
}
fileOUT = fopen("out.txt", "w");
if (!fileOUT) {
exit(-1);
}
for (i = 0; i < linecount; i++) {
fprintf(fileOUT, "%s", arr[i]);
}
fclose(fileIN);
fclose(fileOUT);
}
The problem in your code is you do not rewind the input stream after reading it the first time to count the number of newlines. You should add rewind(fileIN); before the next loop.
Note however that there are other problems in this code:
the number of newline characters may be less than the number of successful calls to fgets(): lines longer than 499 bytes will be silently broken in multiple chunks, causing more items to be read by fgets() than newlines. Also the last line might not end with a newline. Just count the number of successful calls to fgets().
You allocate 500 bytes for each line, which is potentially very wasteful. Use strdup() to allocate only the necessary size.
Swapping the lines in the sort routine should be done by swapping the pointers, not copying the contents.
allocating arr with malloc is safer and more portable than defining it as a variable sized array with char *arr[linecount];
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void sortfile(char **arr, int linecount) {
for (;;) {
int swapped = 0;
for (int j = 1; j < linecount; j++) {
if (strcmp(arr[j - 1], arr[j]) > 0) {
char *t = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = t;
swapped = 1;
}
}
if (swapped == 0)
break;
}
}
int main() {
FILE *fileIN, *fileOUT;
char singleline[500];
int i, linecount;
fileIN = fopen("test1.txt", "r");
if (fileIN == NULL) {
fprintf(stderr, "cannot open %s\n", "test1.txt");
return 1;
}
linecount = 0;
while (fgets(singleline, 500, fileIN)) {
linecount++;
}
printf("line count=%d\n", linecount);
char **arr = malloc(sizeof(*arr) * linecount);
if (arr == NULL) {
fprintf(stderr, "memory allocation failure\n");
return 1;
}
rewind(fileIN);
for (i = 0; i < linecount && fgets(singleline, 500, fileIN) != NULL; i++) {
arr[i] = strdup(singleline);
if (arr[i] == NULL) {
fprintf(stderr, "memory allocation failure\n");
return 1;
}
}
fclose(fileIN);
if (i != linecount) {
fprintf(stderr, "line count mismatch: i=%d, lilnecount=%d\n",
i, linecount);
linecount = i;
}
sortfile(arr, linecount);
for (i = 0; i < linecount; i++) {
printf("%s", arr[i]);
}
fileOUT = fopen("out.txt", "w");
if (!fileOUT) {
fprintf(stderr, "cannot open %s\n", "out.txt");
return 1;
}
for (i = 0; i < linecount; i++) {
fprintf(fileOUT, "%s", arr[i]);
}
fclose(fileOUT);
for (i = 0; i < linecount; i++) {
free(arr[i]);
}
free(arr);
return 0;
}
To get a different sort order, you would change the comparison function. Instead of strcmp() you could use this:
#include <ctype.h>
int my_strcmp(const char *s1, const char *s2) {
/* compare strings lexicographically but swap lower and uppercase letters */
unsigned char c, d;
while ((c = *s1++) == (d = *s2++)) {
if (c == '\0')
return 0; /* string are equal */
}
/* transpose case of c */
if (islower(c)) {
c = toupper(c);
} else {
c = tolower(c);
}
/* transpose case of d */
if (islower(d)) {
d = toupper(d);
} else {
d = tolower(d);
}
/* on ASCII systems, we should still have c != d */
/* return comparison result */
if (c <= d)
return -1;
} else {
return 1;
}
}

double free or corruption when using char**

I'm allocating memory for my int *occurrences int *wordCounts and char **uniqueWords pointers and then at the end of the function that allocates the memory, i free them. However, when i compile the program i get an double free or corruption (!prev) aborting error. Is it caused by malloc,free or could it be due to how i initialize them inside the for loop ?
PS: I'm talking about the sortedCount() method, located towards the end
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define MAX_STRING_SIZE 512 /* each line in the file can have up to 512 chars */
void populateWordsArray(int);
void reverse(int);
void first(int);
void middle(int);
void last(int);
int count(int, char*, int);
void sortedCount(int);
void determineUniqueWords(int *,char **, int);
void *malloc_or_end(size_t);
void* malloc_or_end(size_t sz) {
void *pointer;
pointer = malloc(sz);
if(pointer == NULL) {
printf("Out of memory, terminating.\n");
exit(-1);
}
return pointer;
}
/* turn into local */
FILE *file;
char **wordList;
void determineUniqueWords(int *occurrences, char **word, int N) {
int i = 0;
int j = 0;
for(i = 0; i < N; i++) {
if(occurrences[i] < 1) {
continue;
}
for(j = i + 1; j < N; j++) {
if(occurrences[j] == 1 && (strcmp(word[i],word[j])) == 0) {
occurrences[i]++;
occurrences[j] = 0;
}
}
}
}
/**
* Function populateWordsArray: reads N words from
* the given file and populates the wordList array with them.
* Has one argument: int N - the number of words to read.
* */
void populateWordsArray(int N) {
int i = 0;
while(i < N && (fscanf(file,"%s",wordList[i]) == 1)) { /* fscanf returns the number of successfully read items. If it's not 1, the read failed. Same as checking if fscanf reads the eof char. */
i++;
}
}
/**
* Function reverse: prints the words of the
* text file in reverse order.
* */
void reverse(int N) {
int i = 0;
for(i = N-1; i >= 0; i--) {
if(i == 0) {
printf("%s \n",wordList[i]);
} else if(strcmp(wordList[i],"") == 0) { /* improve this in main-> memory allocation */
continue;
}else {
printf("%s ",wordList[i]);
}
}
return;
}
/**
* Function first: Prints the first char of each
* word in the file.
* */
void first(int N) {
char firstChar;
int i = 0;
for(i = 0; i < N; i++) {
firstChar = *wordList[i];
printf("%c",firstChar);
}
printf("\n");
return;
}
/**
* Function middle: Prints the middle char of each word
* from the given file.
* */
void middle(int N) {
int middleIndex = 0;
int i = 0;
char midChar;
for(i = 0; i < N; i++) {
if((strlen(wordList[i]) % 2) == 0) { /* artios */
middleIndex = ((strlen(wordList[i]) / 2) - 1);
midChar = wordList[i][middleIndex];
}
else { /* peritos */
middleIndex = (int) ceil((strlen(wordList[i]) / 2));
midChar = wordList[i][middleIndex];
}
printf("%c",midChar);
}
printf("\n");
return;
}
/**
* Function last: Prints the last char of each
* word from the given file.
* */
void last(int N) {
int i = 0;
char lastChar;
int lastPos;
for(i = 0; i < N; i++) {
lastPos = strlen(wordList[i]) - 1;
lastChar = wordList[i][lastPos];
printf("%c",lastChar);
}
printf("\n");
return;
}
/**
* Function count: Prints the number of times
* that the selected word is found inside the N first words
* of the file.
* */
int count(int N, char *word, int callID) {
int i = 0;
int count = 0;
for(i = 0; i < N; i++) {
if(strcmp(word,wordList[i]) == 0) {
count++;
}
}
if(callID == 0) { /* if callID == 0 (main called count and we want the output) */
printf("%d",count);
printf("\n");
}
return count;
}
void sortedCount(int N) {
int i,j = 0;
int *occurrences;
int *wordCounts;
char **uniqueWords;
/* mem allocation */
uniqueWords = malloc_or_end(N * sizeof(char*)); /* worst case: every word is unique */
wordCounts = malloc_or_end(N * sizeof(int));
occurrences = malloc_or_end(N * sizeof(int));
/* initialize rootWord and occurrences for the "each word is unique and occurs only once" scenario */
for(i = 0; i < N; i++) {
uniqueWords[i] = malloc_or_end(MAX_STRING_SIZE * sizeof(char));
occurrences[i] = 1;
}
determineUniqueWords(occurrences,wordList,N);
/* populate the wordCounts & uniqueWords "arrays" with the appropriate data in order to sort them successfully */
for(i = 0; i < N; i++) {
if(occurrences[i] > 0) {
wordCounts[i] = count(N,wordList[i],1);
uniqueWords[i] = wordList[i];
}
}
for(i = 0; i < N; i++) {
free(uniqueWords[i]);
}
free(uniqueWords);
free(occurrences);
free(wordCounts);
return;
}
int main(int argc,char *argv[]) { /* argv[1] = op argv[2] = name argv[3] = <word> */
int N = -1;
int i = 0;
int spaceNum,nlNum = -1;
file = fopen(argv[2],"r");
if(file == (FILE *) NULL) { /* check if the file opened successfully */
fprintf(stderr,"Cannot open file\n");
}
fscanf(file,"%d",&N); /* get the N number */
wordList = malloc_or_end(N * sizeof(char *)); /* allocate memory for pointers */
for(i = 0; i < N; i++) {
wordList[i] = malloc_or_end(MAX_STRING_SIZE * sizeof(char)); /* allocate memory for strings */
}
populateWordsArray(N);
if(strcmp(argv[1],"-reverse") == 0) {
reverse(N);
} else if(strcmp(argv[1],"-first") == 0) {
first(N);
} else if(strcmp(argv[1],"-middle") == 0) {
middle(N);
} else if(strcmp(argv[1],"-last") == 0) {
last(N);
} else if((strcmp(argv[1],"-count") == 0) && argv[3] != NULL) {
i = count(N,argv[3],0);
} else if((strcmp(argv[1],"-sorted") == 0) && (strcmp(argv[3],"-count") == 0)) {
sortedCount(N);
} else {
/* i only wish i could print something here */
}
/* End of program operations */
for(i = 0; i < N; i++) {
free(wordList[i]);
}
free(wordList);
fclose(file);
return 0;
}
You are overwriting the value of a pointer to heap memory on line 185:
uniqueWords[i] = wordList[i];
This means that when you free it later, you are actually freeing the allocated rows in wordList. Now you have two problems:
When you free the wordList rows on lines 244-246, it will be a double-free
You are losing your reference to the uniqueWords rows.
Use strcpy to assign to a dynamically-allocated string rather than the = operation.

Leaking memory in word frequency program

I'm leaking a very small amount of memory in a wf function I'm writing, and I can't seem to exactly locate it. I'm using a hash table to hold freqency, but my testing makes it look like it's not in the hashing functions. Here are my functions to open/read files and free the data at the end. I'm sure it's a simple bug, but I've been looking at this code too long to be able to see it.
typedef struct {
int noInFiles, numFiles, numToPrint;
char** fileNames;
FILE** files;
Hash hash;
} Freq;
void handleInput(int argc, char* argv[], Freq* freq) {
int num = 0, i, j = 0;
char* crap;
printf("memcurrent pre fileName alloc: %d\n\n", memCurrent());
freq->fileNames = calloc(argc - 1, sizeof(char**));
printf("memcurrent post filename alloc: %d\n\n", memCurrent());
freq->numToPrint = 10;
if(argc < 2) {
freq->noInFiles = 1;
freq->numFiles = 0;
return;
}
for(i = 1; i < argc; i++) {
if(argv[i][0] == '-') {
if(argv[i][1] == 'n') {
num = strtol(argv[i] + 2, &crap, 10);
freq->numToPrint = num;
}
else {
fprintf(stderr, "Usage: wf [-nX] [file...]\n");
exit(EXIT_FAILURE);
}
}
else {
freq->fileNames[j] = calloc(strlen(argv[i]) + 1 ,sizeof(char));
strcpy(freq->fileNames[j], argv[i]);
j++;
freq->numFiles++;
}
}
}
void openFiles(Freq* freq) {
int i;
char* str;
printf("Memcurrent pre open: %d\n",memCurrent());
freq->files = calloc(freq->numFiles,sizeof(FILE**));
printf("Memcurrent post open: %d\n",memCurrent());
for(i = 0; i < freq-> numFiles; i++) {
freq->files[i] = fopen(freq->fileNames[i],"r");
if(freq->files[i] == NULL) {
str = malloc(strlen(freq->fileNames[i]) + 5);
sprintf(str,"wf: %s",freq->fileNames[i]);
perror(str);
free(str);
exit(EXIT_FAILURE);
}
}
}
void freeFreq(int argc, Freq* freq) {
int i;
for(i = 0; i < argc - 1 ; i++) {
free(freq->fileNames[i]);
}
free(freq->fileNames);
free(freq->files);
}
Hash functions
typedef struct {
Entry* arr;
int size, numValid;
} Hash;
void initHash(Hash* hash) {
hash->arr = calloc(BASESIZE, sizeof(Entry));
TOTALALLOC =+ (BASESIZE * sizeof(Entry));
hash->size = BASESIZE;
hash->numValid = 0;
}
void freeTable(Hash* hash) {
int i;
for(i = 0; i < hash->numValid - 1; i++) {
if(hash->arr[i].correct == 1 && hash->arr[i].valid == 1) {
wordsFreed++;
free(hash->arr[i].word);
}
}
free(hash->arr);
}
This might be it:
for(i = 0; i < hash->numValid - 1; i++) {
If you have numValid set to 0 at start, I'm presuming that you increment it each time you add an entry to the array.
So if numValid is 1, then you will never loop, which means you will leak one of your entries. It seems that each time you free the hash, you will leak one entry, unless the hash has no entries at all.
This may not fix your problem, but ..
There is a mismatch between the number of allocations and deallocations for freq->fileNames.
Allocation:
else {
freq->fileNames[j] = calloc(strlen(argv[i]) + 1 ,sizeof(char));
strcpy(freq->fileNames[j], argv[i]);
j++;
freq->numFiles++;
}
Deallocation:
for(i = 0; i < argc - 1 ; i++) {
free(freq->fileNames[i]);
}
Assuming the logic for allocation is correct, the logic for deallocation needs to be:
for(i = 0; i < freq->numFiles ; i++) {
free(freq->fileNames[i]);
}
PS
I noticed that you have calls to fopen but no calls to fclose in your posted code.

Resources