Home > Enterprise >  How to detect duplicate string using strcmp()
How to detect duplicate string using strcmp()

Time:03-31

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

struct stud
{
    char nam[20];
    int num;
    char letter[5];
};

int main()
{
    struct stud s[5];
    int i, j;

    for(i = 0; i < 5; i  ){
        printf("Enter the name of student #%d: ", i 1);
        scanf("%s", s[i].nam);
        printf("Enter the number grade of student #%d: ", i 1);
        scanf("%d", &s[i].num);
    }
    
    for (j = 0; j < i; j  ) { 
            if (strcmp(s[i].nam, s[j].nam) == 0)
                printf("Error. Duplicate name detected.");
        
    }
    
    for(i = 0; i < 5; i  ){
    
        if(s[i].num >= 90 )
            strcpy(s[i].letter, "A");
        else if(s[i].num >= 80)
            strcpy(s[i].letter, "B");
        else if(s[i].num >= 70)
            strcpy(s[i].letter, "C");
        else if(s[i].num >= 60)
            strcpy(s[i].letter, "D");
        else
            strcpy(s[i].letter, "F");
    }

    for(i = 0; i < 5; i  )
        printf("\n%s has a %s ", s[i].nam, s[i].letter);
    return 0;
 }

This program has the user enter 5 names and 5 numeric grades, which will then result in the output of their respective letter grades for that student. I'm trying to make it so if the user enters a duplicate name, and message will print saying they can't do that. My attempt in trying to do this is as follows:

for (j = 0; j < i; j  ) { 
            if (strcmp(s[i].nam, s[j].nam) == 0)
                printf("Error. Duplicate name detected.");

    }

Where I believe that s[j] is the previous string, and compare to see if it equals 0(duplicate) and prints a message. This obviously doesn't work however so I would like to know how to fix this so it can correctly detect duplicate names. Thank you.

Also I have posted this question before but the person that provided an explanation deleted their response before I could provide further questions and ask for clarification. So I am posting this again with an attempt in seeking further aid in what I did wrong in my code.

CodePudding user response:

  1. At the start of the detection loop, i is already 5, so using s[i] is undefined behavior
  2. In your detection loop, i is invariant. you are just comparing a name against the last one [except for the UB, of course].

You need two loops to compare all names against one another.

Also, using 5 everywhere is a "magic number". Better to use a #define (e.g. SMAX)


In the code below, I use cpp conditionals to denote old vs. new code:

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

Here is the corrected code. It is annotated with the bugs and fixes:

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

struct stud {
    char nam[20];
    int num;
    char letter[5];
};

#define SMAX    5                       // maximum number of students

int
main()
{
    struct stud s[SMAX];
    int i, j;

    for (i = 0; i < SMAX; i  ) {
        printf("Enter the name of student #%d: ", i   1);
        scanf("%s", s[i].nam);
        printf("Enter the number grade of student #%d: ", i   1);
        scanf("%d", &s[i].num);
    }

// NOTE/BUG: i is already SMAX, so using s[i] is UB (undefined behavior)
// NOTE/BUG: i never changes
#if 0
    for (j = 0; j < i; j  ) {
        if (strcmp(s[i].nam, s[j].nam) == 0)
            printf("Error. Duplicate name detected.");
    }
#else
    for (i = 0; i < (SMAX - 1); i  ) {
        for (j = i   1; j < SMAX; j  ) {
            if (strcmp(s[i].nam, s[j].nam) == 0)
                printf("Error. Duplicate name detected -- %s\n",s[j].nam);
        }
    }
#endif

    for (i = 0; i < SMAX; i  ) {
        if (s[i].num >= 90)
            strcpy(s[i].letter, "A");
        else if (s[i].num >= 80)
            strcpy(s[i].letter, "B");
        else if (s[i].num >= 70)
            strcpy(s[i].letter, "C");
        else if (s[i].num >= 60)
            strcpy(s[i].letter, "D");
        else
            strcpy(s[i].letter, "F");
    }

// NOTE/BUG: newline should go at the end of the printf to prevent a hanging
// last line
#if 0
    for (i = 0; i < SMAX; i  )
        printf("\n%s has a %s ", s[i].nam, s[i].letter);
#else
    for (i = 0; i < SMAX; i  )
        printf("%s has a %s\n", s[i].nam, s[i].letter);
#endif

    return 0;
}

UPDATE:

Thanks for the tip! On a side note, how would I make it so while the user is entering the duplicate names, the error message appears and the program ends right there.For example: Enter the name of student 1: dan Enter grade: 87 Enter the name of student 2: dan Enter the grade: 78 Error. No duplicate names allowed. And then the program ends there. – User234567

Easy enough. I put the duplication detection code into functions.

But, I've added a few more enhancements so this may help you with your learning ;-)

  1. I added reprompting the user if they enter a duplicate.

  2. I hate scanf ;-) I reworked the prompting code by putting it into two functions. It will work better if input is a file. This is useful during testing

  3. I changed the conversion from grade number to grade letter to use a table.

Anyway, here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>

struct stud {
    char nam[20];
    int num;
    char letter[5];
};

struct letter {
    int num;
    const char *letter;
};

#define LET(_num,_let) \
    { .num = _num, .letter = _let }

struct letter letters[] = {
    LET(90,"A"),
    LET(80,"B"),
    LET(70,"C"),
    LET(60,"D"),
    LET(0,"F"),
    LET(0,NULL)
};

#define SMAX    5                       // maximum number of students

// chkall -- check entire array for duplicates
int
chkall(const struct stud *s,int smax)
{
    int i;
    int j;
    int dup = 0;

    for (i = 0; i < (smax - 1); i  ) {
        for (j = i   1; j < smax; j  ) {
            if (strcmp(s[i].nam, s[j].nam) == 0) {
                printf("Error. Duplicate name detected -- %s\n",s[j].nam);
                dup  = 1;
            }
        }
    }

    return dup;
}

// chkone -- check a given entry for duplicate (as they are added)
int
chkone(const struct stud *s,int i)
{
    int j;
    int dup = 0;

    for (j = 0; j < i; j  ) {
        if (strcmp(s[i].nam, s[j].nam) == 0) {
            printf("Error. Duplicate name detected -- %s\n",s[j].nam);
            dup  = 1;
        }
    }

    return dup;
}

// prompt_string -- prompt user for a string
char *
prompt_string(const char *what,int i,char *buf,size_t siz)
{
    static int tty = -1;

    // decide if our input is tty or file
    if (tty < 0) {
        struct winsize ws;
        tty = ioctl(0,TIOCGWINSZ,&ws);
        tty = (tty >= 0);
    }

    printf("Enter the %s of student #%d: ", what, i   1);
    fflush(stdout);

    char *cp = fgets(buf,siz,stdin);

    do {
        // handle EOF
        if (cp == NULL)
            break;

        buf[strcspn(buf,"\n")] = 0;

        // echo the data if input is _not_ a tty
        if (! tty)
            printf("%s\n",buf);
    } while (0);

    return cp;
}

// prompt_number -- prompt user for a number
long long
prompt_number(const char *what,int i)
{
    char *cp;
    char buf[100];
    long long val;

    while (1) {
        cp = prompt_string(what,i,buf,sizeof(buf));

        // handle EOF
        if (cp == NULL) {
            val = -1;
            break;
        }

        // decode the number
        val = strtoll(buf,&cp,10);
        if (*cp == 0)
            break;

        printf("invalid number syntax -- '%s'\n",cp);
    }

    return val;
}

int
main(void)
{
    struct stud s[SMAX];
    int i;

    for (i = 0; i < SMAX; i  ) {
        while (1) {
            prompt_string("name",i,s[i].nam,sizeof(s[i].nam));
            if (! chkone(s,i))
                break;
        }

        s[i].num = prompt_number("number grade",i);
    }

    // recheck all entries
    // this will _never_ report a duplicate because of the chkone above
    chkall(s,SMAX);

    for (i = 0; i < SMAX; i  ) {
        for (struct letter *let = letters;  let->letter != NULL;    let) {
            if (s[i].num >= let->num) {
                strcpy(s[i].letter,let->letter);
                break;
            }
        }
    }

    for (i = 0; i < SMAX; i  )
        printf("%s has a %s\n", s[i].nam, s[i].letter);

    return 0;
}
  • Related