Dominik Car
Dominik Car

Reputation: 191

C language, convert and combine 2 strings into a double

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

Answers (2)

RoadRunner
RoadRunner

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 by malloc().

How to read your file successfully:

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);

Here is some example code that that demonstrates this:

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

chqrlie
chqrlie

Reputation: 145317

Your attempt has some issues:

  • The 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:

  • skip any initial zeroes in both whole parts
  • compare the resulting strings lengths: if they differ, return -1 if the first is shorter and 1 if it is longer.
  • if both strings have the same length, compare them. If they differ, the result of the comparison should be returned, ie return the result of strcmp(a->whole, b->whole).
  • if both whole parts are equal, normalize the factional parts by removing the trailing zeroes.
  • the comparison result is the result of comparing these strings, ie: 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

Related Questions