0

I've been working on this for a few hours now and it's stating to test my sanity.

The assignment is to write a program that when it reads a name it checks through the given struct's names and looks for a match, then displays the other data from the structure. It is supposed to work despite the input string's case and if nothing matches the name it should tell the user.

Of course everything compiles perfectly fine, but when I input a name that should hit (because it is in the struct) it doesn't see it.

I know the issue is somewhere in the strcmp calling.

Any help would be greatly appreciated.

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>



typedef struct solar    
{
    char name[50];
    char type[50];
    double radius;
    double mass;
    double distsun;

}solar_obj;

void main()
{
    char input_name[50];

    solar_obj solarobj[] = { { "CERES", "DWARF PLANET", 471, 9.5E20, 413.7E6    },
                            { "EARTH", "PLANET", 6380, 5.974E24, 149.6E6 },
                            { "ERIS", "DWARF PLANET", 1163, 1.7E22, 10210E6 },   
                            { "HAUMEA", "DWARF PLANET", 650, 4E21, 6484E6 },
                            { "JUPITER", "PLANET", 71500, 1.899E27, 778.4E6 },    
                            { "MAKEMAKE", "DWARF PLANET", 715, 2.2E21, 6850E6 },
                            { "MARS", "PLANET", 3400, 6.419E23, 227.9E6 },
                            { "MERCURY", "PLANET", 2440, 3.302E23, 57.9E6 },
                            { "NEPTUNE", "PLANET", 24770, 1.024E26, 4498.3E6 },    
                            { "PLUTO", "DWARF PLANET", 1184, 1.3E22, 5906.4E6},
                            { "SATURN", "PLANET", 60270, 5.685E26, 1426.7E6 },
                            { "SUN", "STAR", 696000, 1.9891E30, 0 },
                            { "URANUS", "PLANET", 25560, 8.685E25, 2870.9E6 },  
                            { "VENUS", "PLANET", 6050, 4.869E24, 108.2E6 }
                          };

    printf("Please enter the name of the solar object:");

    fgets(input_name, sizeof(input_name), stdin);

    //to make all inputed string chars into uppercase
    for (int i = 0; input_name[i] != '\0'; i++){
        input_name[i] = toupper(input_name[i]);
    }

    //search through struct for matching name then print the data
    for (int j = 0; j < 14; j++)
        {
            if (strcmp(solarobj[j].name, input_name) == 0)
                {
                printf("\nObject name is :  %s \n", solarobj[j].name);
                printf("%s is a %s \n", solarobj[j].name, solarobj[j].type);
                printf("Radius (km) : %f \n ", solarobj[j].radius);
                printf("Mass (kg) : %f\n", solarobj[j].mass);
                printf("Distance from sun (km) : %f \n", solarobj[j].distsun);
                }

            else // if a name that doesnt match anything in the struct tell the user
            {
               if (j == 13){
                  printf("No objects in our solar system have the name: %s\n", input_name);
                  break;
               }    

               continue;
            }     
        }    


    getch();
}
axiac
  • 68,258
  • 9
  • 99
  • 134
  • 1
    I wonder if you have a problem with an extra newline on the end of your input, interfering with your strcmp – hymie Mar 05 '15 at 20:05
  • 1
    My top tip would be to learn how to use a debugger. Say gdb if you are using gcc. I would guess a newline too. You could try using strncmp instead on the no. Of chars in your struct string – bph Mar 05 '15 at 20:05
  • Also, your "else" doesn't work the way you think it does. You will always get the "no match" error whether there's a match or not. – hymie Mar 05 '15 at 20:10
  • The answers regarding `fgets()` explain the problem. You could use `scanf("%49s",input_name)` instead, which will read as input a word (up to any space), rather than a line. The 49 indicates that at most 49 characters will be read, leaving enough room for the terminating '\0' character. – halfflat Mar 05 '15 at 20:16
  • 1
    @halfflat `scanf("%49s",input_name)` will leave `input_name` uninitialized if user enters `"\n"` and leave `"\n"` in `stdin` . Recommend staying with `fgets()` or `getline()`. – chux - Reinstate Monica Mar 05 '15 at 20:23
  • Fair enough! Probably easier to reason about than making sure `input_name` is properly initialised. I admit, these sorts of issues, together with ambiguity in the state of the input stream after the call, make me leery of using `scanf`/`fgets` at all in this sort of context, and if I'm stuck using C, I write a trivial scanner that reads the word up to the array length, space, eol or eof and then throws away anything until the eol or eof. Overkill for a homework problem though, probably! – halfflat Mar 05 '15 at 20:43
  • Why store type as string? What's wrong with enum? – phuclv Feb 26 '16 at 15:41

3 Answers3

2

First, fgets() keeps the newline at the end of the entry. You can get rid of it like this.

char *strp = strchr(input_name, '\n');  // look for newline
if (strp) *strp = '\0';                 // terminate string

Next, you can use stricmp() for case insensitive compare.

if (stricmp(solarobj[j].name, input_name) == 0)

And your loop could be simplified to be like this

int j;
for (j = 0; j < 14; j++)
{
    if (stricmp(solarobj[j].name, input_name) == 0)
        {
        printf("\nObject name is :  %s \n", solarobj[j].name);
        printf("%s is a %s \n", solarobj[j].name, solarobj[j].type);
        printf("Radius (km) : %f \n ", solarobj[j].radius);
        printf("Mass (kg) : %f\n", solarobj[j].mass);
        printf("Distance from sun (km) : %f \n", solarobj[j].distsun);
        break;
    }     
}    
if (j == 14)
    printf("No objects in our solar system have the name: %s\n", input_name);

Notice I declared j outside the loop so its scope is still valid when I test if a match was found.

Weather Vane
  • 33,872
  • 7
  • 36
  • 56
1

The documentation of function fgets() says:

Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.

A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.

The value you have in variable input_name ends with a newline character ('\n') and, of course, it will never match a solar object name from your list.

You can easily fix this by removing it from the string (if it's there) before you start searching in the list:

void main() {
    char input_name[50];
    int i;

    solar_obj solarobj[] = { 
        /* the big list here */
    };

    printf("Please enter the name of the solar object:");
    fgets(input_name, sizeof(input_name), stdin);

    // make all inputed string chars into uppercase
    // stop when reach a new line or the end of the string
    for (i = 0; input_name[i] != '\n' && input_name[i] != '\0'; i++) {
        input_name[i] = toupper(input_name[i]);
    }
    // end the string here (replace the newline, if it's there)
    input_name[i] = '\0';

    //search through struct for matching name then print the data
    for (int j = 0; j < 14; j++) {
        /* the rest of the original code here */
Community
  • 1
  • 1
axiac
  • 68,258
  • 9
  • 99
  • 134
  • 2
    You might want to check whether it actually is a newline before removing it. If the input is longer than the specified size, the string won't end in a newline (and the rest of a line will be left waiting for the next `fgets` or other input call). – Keith Thompson Mar 05 '15 at 20:13
  • You're right. I changed the answer. – axiac Mar 06 '15 at 10:07
0
the following code will perform the desired functionality
contains no no-portable function calls,
checks for input errors
compiles cleanly

#include <stdio.h>
//#include <conio.h> // <-- non portable, do not use
#include <stdlib.h>
#include <string.h>
#include <ctype.h>



struct solar   // < remove clutter from struct definition  
{
    char name[50];
    char type[50];
    double radius;
    double mass;
    double distsun;

};

// use valid main declaration
int main()
{
    char input_name[50] = {'\0'};  // initialize input field

    // use struct name, not typedef name
    // use vertical alignment of braces 
    //     rather than georgian formatting
    //     for readability
    struct solar solarobj[] = 
    { 
        { "CERES", "DWARF PLANET", 471, 9.5E20, 413.7E6 },
        { "EARTH", "PLANET", 6380, 5.974E24, 149.6E6 },
        { "ERIS", "DWARF PLANET", 1163, 1.7E22, 10210E6 },   
        { "HAUMEA", "DWARF PLANET", 650, 4E21, 6484E6 },
        { "JUPITER", "PLANET", 71500, 1.899E27, 778.4E6 },    
        { "MAKEMAKE", "DWARF PLANET", 715, 2.2E21, 6850E6 },
        { "MARS", "PLANET", 3400, 6.419E23, 227.9E6 },
        { "MERCURY", "PLANET", 2440, 3.302E23, 57.9E6 },
        { "NEPTUNE", "PLANET", 24770, 1.024E26, 4498.3E6 },    
        { "PLUTO", "DWARF PLANET", 1184, 1.3E22, 5906.4E6},
        { "SATURN", "PLANET", 60270, 5.685E26, 1426.7E6 },
        { "SUN", "STAR", 696000, 1.9891E30, 0 },
        { "URANUS", "PLANET", 25560, 8.685E25, 2870.9E6 },  
        { "VENUS", "PLANET", 6050, 4.869E24, 108.2E6 }
    };

    // calculate number of entries in table
    int tableCount = sizeof(solarobj) / sizeof(struct solar);

    printf("Please enter the name of the solar object:");

    // always check to assure input operation is successful
    if( NULL == fgets(input_name, sizeof(input_name), stdin) )
    { // then fgets failed
        perror( "fgets for planet name failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, fgets successful

    char *strp = strchr(input_name, '\n');  // look for newline
    if (strp) *strp = '\0';                 // remove newline, if present

    // change all upper case charactgers to lower case 
    for( int i=0; input_name[i]; i++ ) input_name[i] = tolower(input_name[i]);

    int found = 0; // indicate name not found

    //search through table for matching name then print the data
    for (int j = 0; j < tableCount; j++)
    {
        if (strcmp(solarobj[j].name, input_name) == 0)
        {
            printf("\nObject name is :  %s \n", solarobj[j].name);
            printf("%s is a %s \n", solarobj[j].name, solarobj[j].type);
            printf("Radius (km) : %f \n ", solarobj[j].radius);
            printf("Mass (kg) : %f\n", solarobj[j].mass);
            printf("Distance from sun (km) : %f \n", solarobj[j].distsun);
            found = 1; // indicate name found
            break;
        } // end if 
    } // end for

    if( !found ) printf("No objects in our solar system have the name: %s\n", input_name);

    // use common system function, not windows specific function
    getchar();
    return( 0 );
} // end function: main
user3629249
  • 16,402
  • 1
  • 16
  • 17
  • The declaration `int main()` is not described by the Standard. You may want to change it to `int main(void)`. – pmg Mar 06 '15 at 10:13