Reputation: 191
I require assistance with my C programming and since my professor is too busy to respond (6th day now can't be found in his office, nor replying to emails). Suppose I have a structure defined as
typedef struct {
char whole[1000];
char decimal[1000];
double number;
} record;
where whole
represents the whole part of the number (ex. 10, 20, 301, 123, 1005...)
and the decimal
represents the decimal part of the number (10.123, 20.22123, 301.99181, 123.558123...)
. the numbers are treated as strings and written in a binary file, ex.
10 1234 (10 is the whole part and 1234 is the decimal part of the number = 10.1234)
20 211291 (20.211291)
301 2102190 (301.2102190)
1 56615 (1.56615)
988 001 (988.001)
etc.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char whole[1000];
char decimal[1000];
double number;
} record;
int cmp(record *a, record *b) {
????
return ???;
}
main()
{
FILE *f = fopen("records.bin", "r+b");
if (!f)
exit(1);
fseek(f, 0, SEEK_END);
long size = ftell(f);
long N = size / sizeof(record);
fseek(f, 0, SEEK_SET);
record *z = malloc(size);
fread(z, sizeof(record), N, f);
qsort(z, N, sizeof(record), (int(*)(const void*, const void*))cmp);
/* fopen (wb) fwrite(...) flush, free, fclose*/
...
}
How would I write the compare function to take those 2 strings (whole and decimal) and combine them into one double which I would then sort ascending?
I've tried using atof
and sscanf
but not so sure on how to exactly do it with a binary file structure in the compare function.
Upvotes: 0
Views: 179
Reputation: 26335
Instead of using this struct
:
typedef struct {
char whole[1000];
char decimal[1000];
double number[1000];
} record;
You would be much better off using this:
typedef struct {
char whole[1000];
char decimal[1000];
double number;
} record;
Then creating an array of record
structs, something like this:
size_t starting_size = 10;
record *records = malloc(starting_size * sizeof(record));
Which allocates an initial 10
records. If there happens to be more lines in the file, you can realloc()
more space if needed.
realloc()
resizes the memory block pointed to, which was previously allocated bymalloc()
.
You can simply read the file using fscanf()
, as pointed out in the other answer. Once these values are read into strings, you can simply concatenate everything into one string, then convert the string to a double
using atof()
.
Here is an example:
char *whole = 123;
char *decimal = 456;
char *point = ".";
double number;
char *whole_number = malloc(strlen(whole) + strlen(decimal) + 2);
/* check return value */
*whole_number = '\0';
strcat(whole_number, whole);
strcat(whole_number, point);
strcat(whole_number, decimal);
/* string "123.456" created */
number = atof(whole_number);
/* double number = 123.456 created */
/* free() whole_number later */
Once this is done, you can then qsort
your array of structs in ascending order. This does need a more complete cmp
function. You function can possibly be this:
int cmp_func(const void *a, const void *b) {
const record *num1 = (const record *)a;
const record *num2 = (const record *)b;
if (num1->number > num2->number) {
return +1;
} else if (num1->number < num2->number) {
return -1;
}
return 0;
}
Then you can simply sort your array with a call like this:
qsort(records, N, sizeof(record), cmp_func);
Note: This code is just here to give an example of how to write this program. Simply following the above steps will simply allow you to fix your approach.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUMSIZE 1000
#define INITIALSIZE 10
typedef struct {
char whole[NUMSIZE+1];
char decimal[NUMSIZE+1];
double number;
} record_t;
typedef struct {
record_t *rec;
size_t nrec;
size_t currsize;
} records_t;
char *create_number(records_t *R, size_t numbytes);
int cmp_func(const void *a, const void *b);
void read_records(FILE *filestream, records_t *R);
void print_records(records_t *R);
records_t *create_records(void);
int main(void) {
records_t *R;
FILE *fp;
fp = fopen("doublec.txt", "r");
if (fp == NULL) {
fprintf(stderr, "%s\n", "Error reading file");
return 1;
}
R = create_records();
read_records(fp, R);
printf("\nOriginal Records:\n");
print_records(R);
qsort(R->rec, R->nrec, sizeof(record_t), cmp_func);
printf("\nSorted Records:\n");
print_records(R);
free(R->rec);
R->rec = NULL;
free(R);
R = NULL;
return 0;
}
void read_records(FILE *filestream, records_t *R) {
char *number;
size_t w_size, d_size, numbytes;
while (fscanf(filestream, "%999s %999s", R->rec[R->nrec].whole, R->rec[R->nrec].decimal) == 2) {
w_size = strlen(R->rec[R->nrec].whole);
d_size = strlen(R->rec[R->nrec].decimal);
numbytes = w_size + d_size + 2;
number = create_number(R, numbytes);
R->rec[R->nrec].number = atof(number);
if (R->currsize == R->nrec) {
R->currsize *= 2;
R->rec = realloc(R->rec, R->currsize * sizeof(record_t));
if (R->rec == NULL) {
printf("Cannot reallocate %zu members.\n", R->currsize);
exit(EXIT_FAILURE);
}
}
free(number);
number = NULL;
R->nrec++;
}
}
char *create_number(records_t *R, size_t numbytes) {
char *result;
const char *decimal = ".";
result = malloc(numbytes);
if (result == NULL) {
printf("Cannot allocate %zu bytes for number.\n", numbytes);
exit(EXIT_FAILURE);
}
*result = '\0';
strcat(result, R->rec[R->nrec].whole);
strcat(result, decimal);
strcat(result, R->rec[R->nrec].decimal);
return result;
}
void print_records(records_t *R) {
size_t i;
for (i = 0; i < R->nrec; i++) {
printf("Whole Number: %-5s Decimal: %-8s Number: %-7f\n", R->rec[i].whole,
R->rec[i].decimal,
R->rec[i].number);
}
}
int cmp_func(const void *a, const void *b) {
const record_t *num1 = (const record_t *)a;
const record_t *num2 = (const record_t *)b;
if (num1->number > num2->number) {
return +1;
} else if (num1->number < num2->number) {
return -1;
}
return 0;
}
records_t *create_records(void) {
records_t *R = malloc(sizeof(*R));
if (R == NULL) {
printf("Cannot allocate struct.\n");
exit(EXIT_FAILURE);
}
R->nrec = 0;
R->currsize = INITIALSIZE;
R->rec = malloc(R->currsize * sizeof(record_t));
if (R->rec == NULL) {
printf("Cannot allocate initial %zu members.\n", R->currsize);
exit(EXIT_FAILURE);
}
return R;
}
Upvotes: 0
Reputation: 145317
Your attempt has some issues:
number
field in the record
structure seems to serve no useful purpose.reading the file in binary mode seems wasteful since it could be parsed easily as test strings this way:
fscanf(input, "%999s %999s", z->whole, z->decimal);
You could even make the source file completely readable by separating the whole and decimal parts with a .
and would then use "%999s.%999s"
as a format string.
You can compare the numbers in string format by taking the following steps:
strcmp(a->whole, b->whole)
.return strcmp(a->decimal, b->decimal);
Writing the code is your task now. Ideally, your code should implement the above steps without changing the records.
Upvotes: 3