Is there a way to compare multiple strings in C? [ I input them through fgets() ]

112 Views Asked by At
int Distance() {
    char from[40], to[40];
    double val;
    double result;

    printf("Enter what you want to convert from: ");
    fflush(stdin);
    fgets(from, 40, stdin);

    printf("Enter what you want to convert to: \n");
    fflush(stdin);
    fgets(to, 40, stdin);

    printf("Enter the numerical value of conversion: \n");
    scanf("%lf", &val);

    (strcmp(from, "cm") == 0 && strcmp(to, "mm") == 0) ? printf("Answer is %lf MilliMeters", val / 10) : 
    (strcmp(from, "mm") == 0 && strcmp(to, "cm") == 0) ? printf("Answer is %lf CentiMeters", val * 10) :
    (strcmp(from, "cm") == 0 && strcmp(to, "km") == 0) ? printf("Answer if %lf KiloMeters", val * 100000) : 
    (strcmp(from, "km") == 0 && strcmp(to, "cm") == 0) ? printf("Answer is %lf CentiMeters", val / 100000) : 
    (strcmp(from, "mm") == 0 && strcmp(to, "km") == 0) ? printf("Anser is %lf KiloMeters", val  * 1000000) : 
    (strcmp(from, "km") == 0 && strcmp(to, "mm") == 0) ? printf("Answer is %lf  MilliMeters", val / 1000000) : 
    printf("Please enter valid conversion units");
}

I am trying to make a unit converter. So I have made different functions for different conversions - like int distance(), int time() and so on.

In my function to calculate distances, I used fgets() to get multi-char inputs like 'cm', 'mm, etc. What i am trying to achieve is If value of fgets in char from is "any string" and fgets in char to is "any other string": print result whose formula is -----(something)

It doesn't compute the result and directly jumps to the final printf("Please enter valid conversion units")

I am still learning at a beginner level, it could be a minor mistake, Please help.

4

There are 4 best solutions below

0
chqrlie On BEST ANSWER

There are multiple issues in the code:

  • fgets() stores the trailing newline at the end of the target array, so all comparisons with unit strings will fail.
  • you should use an intermediary unit to avoid having to test all combinations.

Here is a modified version:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct unit {
    double factor;
    const char *abbrev, *singular, *plural;
} const units[] = {
    { 1, "mm", "millimeter", "millimeters" },
    { 10, "cm", "centimeter", "centimeters" },
    { 100, "dm", "decimeter", "decimeters" },
    { 1000, "m", "meter", "meters" },
    { 10000, "dam", "decameter", "decameters" },
    { 100000, "hm", "hectometer", "hectometers" },
    { 1000000, "km", "kilometer", "kilometers" },
    { 25.4, "in", "inches", "inches" },
    { 304.8, "ft", "foot", "feet" },
    { 914.4, "yd", "yards", "yards" },
    { 201168, "fur", "furlong", "furlongs" },
    { 1609344, "mi", "mile", "miles" },
};

int get_unit(void) {
    char buf[40];
    while (fgets(buf, sizeof buf, stdin)) {
        buf[strcspn(buf, "\n")] = '\0';   // remove the newline if any
        for (int i = 0, n = sizeof(units) / sizeof(*units); i < n; i++) {
            if (!strcmp(buf, units[i].abbrev)
            ||  !strcmp(buf, units[i].singular)
            ||  !strcmp(buf, units[i].plural)) {
                return i;
            }
        }
        printf("unknown unit\n");
    }
    printf("unexpected end of file, aborting\n");
    exit(1);
}

int main(void) {
    int from, to;
    double val;

    printf("Enter what you want to convert from: ");
    from = get_unit();

    printf("Enter what you want to convert to: ");
    to = get_unit();

    printf("Enter the numerical value of conversion: ");
    if (scanf("%lf", &val) == 1) {
        double result = val * units[from].factor / units[to].factor;
        printf("Answer is %g %s\n", result, result == 1 ? units[to].singular : units[to].plural);
        return 0;
    }
    return 1;
}
1
hsuecu On
const char* mapper[6][3] = {
    {"cm", "mm", "millimeters"},
    {"cm", "km", "kilometers"},
    {"mm", "cm", "centimeters"},
    {"mm", "km", "kilometers"},
    {"km", "cm", "centimeters"},
    {"km", "mm", "millimeters"},
};

for(int i=0; i<6; i++)
{
    if(strcmp[from, mapper[i][0]] != 0) continue;
    if(strcmp[to, mapper[i][1] != 0) continue;
    printf("answer is %lf, %s", val, mapper[i][2]);
    break;
}

You can declare standard and constant messages. then use that array to sequentially search for it. It is more clean.

Also use scanf cause it doesn't adds the newline character to input or if you really want to use fgets. Then just use buffer size of 3. For e.g.


char from[3];
fgets(from, 3, stdin);


EDIT:

And to perform conversion

const char* mapper[6][4] = {
    {"cm", "mm", "millimeters", "10"},
    {"cm", "km", "kilometers", "0.00001"},
    {"mm", "cm", "centimeters", "0.1"},
    {"mm", "km", "kilometers", "0.000001"},
    {"km", "cm", "centimeters", "100000"},
    {"km", "mm", "millimeters", "1000000"},
};

for(int i=0; i<6; i++)
{
    if(strcmp[from, mapper[i][0]] != 0) continue;
    if(strcmp[to, mapper[i][1] != 0) continue;
    printf("answer is %lf, %s", val * atof(mapper[i][3]), mapper[i][2]);
    break;
}
2
Ray On

Instead of having a conversion ratio for every pair of units, do it in two steps: first convert the input to meters, then convert from meters to the output unit.

If you have N possible units, this approach will only require N conversion ratios, whereas doing each pair separately requires N^2.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

struct Ratio {
    const char *name;
    const char *abbrev;
    double meters;
};

// This should really be a hash table, but one thing at a time
struct Ratio ratios[] = {
    { "millimeters", "mm", 0.001 },
    { "centermeters", "cm", 0.01 },
    { "meters", "m", 1.0 },
    { "kilometers", "km", 1000.0 },
};

size_t get_ratio(const char *unit) {
    const size_t N = sizeof(ratios) / sizeof(struct Ratio);
    for (size_t idx = 0; idx < N; idx++) {
        if (0 == strcmp(unit, ratios[idx].abbrev)) return idx; 
    }
    fprintf(stderr, "Unrecognized unit: %s\n", unit);
    return SIZE_MAX;
}

void convert(const char *from_unit, const char *to_unit, double val) {
    size_t conv0 = get_ratio(from_unit);
    size_t conv1 = get_ratio(to_unit);
    if (SIZE_MAX == conv0 || SIZE_MAX == conv1) return;
    printf("answer is %f %s\n",
        val * ratios[conv0].meters / ratios[conv1].meters,
        ratios[conv1].name);
}

Note that this also correctly handles the case where your input and output units are the same.

0
Luis Colorado On

You can use a hash table and check if your string is contained in it. You'll be able to get which of the strings is the one selected. On each entry you can save also the multiplication facto to do unit conversion or whatever information is required. Search is fast (it's independent of the actual number of strings in the table) and will be very efficient.