0

So I have a problem that strcmp not working even the string is same. The cmp between destination and source string is have same string, but when strcmp it, it not working. See the code for explanation:

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

int main()
{
    FILE *fp;
    fp = fopen("test.txt", "r");
    char namalengkap[100], gender[100], charnamafile[50], tempat[50], temp[1000], tdeeprint[100], gendercheck;
    int umur, tb, bb, x;
    int cmp1, cmp2, cmp3, cmp4, cmp5, cmp6, check;
    float tdeenum;
    fscanf(fp, "Username : %s\nPassword : %s", tempat, temp);
    fscanf(fp, "\n\n\n=============================DATA PRIBADI USER===============================\n");
    fscanf(fp, "= Nama Lengkap       : %52[^\n] =\n", namalengkap);
    fscanf(fp, "= Jenis Kelamin      : %52[^\n] =\n", gender);
    fscanf(fp, "= Umur               : %52d =\n", &umur);
    fscanf(fp, "= Tinggi Badan (CM)  : %52d =\n", &tb);
    fscanf(fp, "= Berat  Badan (KG)  : %52d =\n", &bb);
    fscanf(fp, "= Rutinitas Olahraga : %52[^\n] =\n", tdeeprint);       
 
    fscanf(fp, "=============================================================================\n");
    cmp1 = strcmp(tdeeprint, "Jarang Berolahraga");
    cmp2 = strcmp(tdeeprint, "1 Hingga 3 Hari Dalam Seminggu");
    cmp3 = strcmp(tdeeprint, "3 Hingga 5 Hari Dalam Seminggu");
    cmp4 = strcmp(tdeeprint, "6 Hingga 7 Hari Dalam Seminggu");
    cmp5 = strcmp(tdeeprint, "Pekerjaan Fisik Berat Atau Olahraga 2x Dalam Sehari");
    if (cmp1 == 0) {
        tdeenum = 1.2;
        printf("tde1work : %f\n", tdeenum);
    } else
    if (cmp2 == 0) {
        tdeenum = 1.375;
        printf("tde2work : %f\n", tdeenum);
    } else
     if (cmp3 == 0) {
        tdeenum = 1.55;
        printf("tde3work : %f\n", tdeenum);
    } else
    if (cmp4 == 0) {
        tdeenum = 1.725;
        printf("tde4work : %f\n", tdeenum);
    } else
    if (cmp5 == 0) {
        tdeenum = 1.9;
        printf("tde5work : %f\n", tdeenum);
    }
    printf("\nUsername : %s\n", tempat);
    printf("\nPassword : %s\n", temp);
    printf("\nName : %s\n", namalengkap);
    printf("\nGender : %s\n", gender);
    printf("\nUmur : %d\n", umur);
    printf("\nTB : %d\n", tb);
    printf("\nBB : %d\n", bb);
    printf("\ntdeeprint : %s\n", tdeeprint);
    printf("\ntdeepnum : %f\n", tdeenum);
    fclose(fp);
    return 0;
}

When I run it:


Username : hitunga

Password : 12345

Name : a

Gender : Perempuan

Umur : 30

TB : 155

BB : 50

tdeeprint : Jarang Berolahraga

tdeepnum : 0.000000

Process returned 0 (0x0)   execution time : 0.043 s
Press any key to continue.

The test.txt file content:

Username : hitunga
Password : 12345

=============================DATA PRIBADI USER===============================
= Nama Lengkap       : a                                                    =
= Jenis Kelamin      : Perempuan                                            =
= Umur               : 30                                                   =
= Tinggi Badan (CM)  : 155                                                  =
= Berat  Badan (KG)  : 50                                                   =
= Rutinitas Olahraga : Jarang Berolahraga                                   =
= TDEE               : 1.200                                                = 
=============================================================================

So what wrong on my code? I really confused why isn't working. It is literally same string comparison but not working. Thank you

Edit: So I replace the cmp section with fscanf, but still not getting the number

fscanf(fp, "= TDEE               : %52.3f =\n", &datusr.tdeenum);
chqrlie
  • 131,814
  • 10
  • 121
  • 189
LearnerC
  • 51
  • 6
  • Side note: You should always check the return value of `fscanf` before using the result. As far as I can tell, this is not the problem in this case, as it always returns `1`, but it is still a good idea to always check. – Andreas Wenzel Jun 18 '22 at 00:16
  • You have a [buffer overflow](https://en.wikipedia.org/wiki/Buffer_overflow). You are writing `53` bytes into `namalengkap` (`52` characters plus the terminating null character), but you only allocated space for `50` bytes. This causes [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior). You have the same problem with `gender` and `tdeeprint`. – Andreas Wenzel Jun 18 '22 at 00:45
  • @andreaswenzel i already change the buffer into 100, but still not working – LearnerC Jun 18 '22 at 00:52
  • Yes, that is not the only problem. The other problem is the one mentioned in the answer by someone else. – Andreas Wenzel Jun 18 '22 at 00:53

2 Answers2

2

If you change your printf statement to this, the problem will likely become clear to you:

 printf("\ntdeeprint : '%s'\n",tdeeprint);

Output snippet:

tdeeprint : 'Jarang Berolahraga                                  '

The same applies to namalengkap and gender. The %52[^\n] pattern includes all the space characters that follow what you want until you get to =, that's why it doesn't match the Jarang Berolahraga.

Erwin
  • 844
  • 4
  • 14
  • So its not possible for strcmp when use `%52?` – LearnerC Jun 18 '22 at 00:54
  • 1
    @LearnerC: You may want to consider using [`strncmp`](https://en.cppreference.com/w/c/string/byte/strncmp) instead of `strcmp`. I suggest that you change the line `cmp1=strcmp(tdeeprint,"Jarang Berolahraga");` to `cmp1=strncmp(tdeeprint,"Jarang Berolahraga",18);`. The number `18` is the length of the string `"Jarang Berolahraga"`. However, this solution is probably not perfect, because it will also match `"Jarang Berolahragasdgfesfhk"`. – Andreas Wenzel Jun 18 '22 at 00:57
  • @LearnerC: If you don't want it to match `"Jarang Berolahragasdgfesfhk"`, then you should not use `strncmp`. Instead, it would probably be best to strip all space characters from the end of the string, and then use `strcmp`. – Andreas Wenzel Jun 18 '22 at 01:03
  • 2
    You can use the `%52[^\n]` pattern and then trim the spaces from the string before doing the `strcmp()`. You can take inspiration from the answer to this question: https://stackoverflow.com/questions/122616/how-do-i-trim-leading-trailing-whitespace-in-a-standard-way – Erwin Jun 18 '22 at 01:14
  • @AndreasWenzel so i change not to use strcmp but instead `fscanf(fp,"= TDEE : %52.3f =\n",&datusr.tdeenum);` it applies too for `fprintf` but it still not get the number. – LearnerC Jun 18 '22 at 01:15
  • @LearnerC: In your most recent comment, you stated that it is working now. However, you now seem to have deleted that comment. Does that mean that you are still having the problem that you mentioned in the comment beforehand? – Andreas Wenzel Jun 18 '22 at 13:57
  • @LearnerC: `%52.3f` is an invalid conversion specifier, there is no precision field in `scanf` conversions. Just use `%52f` or simply `%f`. – chqrlie Jun 18 '22 at 21:07
0

Regarding the last question, fscanf(fp, "= TDEE : %52.3f =\n", &datusr.tdeenum) fails after consuming the input up to = TDEE : because %52.3f is not a valid conversion specifier: there is no precision field in scanf conversions. You should just use %52f or simply %f.

Note that your parser is very brittle and you do not check the return values of these fscanf() calls. Any mismatch will cause the input stream to get out of sync with your code and subsequent calls to fscanf() will fail and leave the destination variables unchanged, hence uninitialized, thus causing undefined behavior when you later compare them and print them.

You should change your parsing method: a safer approach is to read the file one line at a time with fgets(), prepare the line for parsing ie: remove the trailing = and trailing spaces, use sscanf() to try and match the expected data and check the return value to detect and report invalid input.

Here is a modified version:

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

// read the next non blank line from file fp, strip trailing spaces and =
char *nextline(FILE *fp, char *buf, size_t size, int c) {
    for (;;) {
        if (!fgets(buf, size, fp)) {
            *buf = '\0';
            return buf;
        }
        size_t len = strlen(buf);
        while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == ' ' || buf[len - 1] == c)) {
            buf[--len] = '\0';
        }
        if (*buf)
            return buf;
    }
}

int main() {
    FILE *fp = fopen("220618-test.txt", "r");
    if (fp == NULL) {
        fprintf(stderr, "cannot open test.txt: %s\n", strerror(errno));
        return 1;
    }
    char tempat[50], temp[1000];
    char namalengkap[100], gender[100], tdeeprint[100], ch[2];
    int umur, tb, bb;
    double tdeenum;
    char buf[200];
    if (sscanf(nextline(fp, buf, sizeof buf, 0), "Username : %49s", tempat) != 1
    ||  sscanf(nextline(fp, buf, sizeof buf, 0), "Password : %999s", temp) != 1
    ||  sscanf(nextline(fp, buf, sizeof buf, 0), "=============================DATA PRIBADI USER==============================%1[=]", ch) != 1
    ||  sscanf(nextline(fp, buf, sizeof buf, '='), "= Nama Lengkap       : %99[^\n]", namalengkap) != 1
    ||  sscanf(nextline(fp, buf, sizeof buf, '='), "= Jenis Kelamin      : %99[^\n]", gender) != 1
    ||  sscanf(nextline(fp, buf, sizeof buf, '='), "= Umur               : %d", &umur) != 1
    ||  sscanf(nextline(fp, buf, sizeof buf, '='), "= Tinggi Badan (CM)  : %d", &tb) != 1
    ||  sscanf(nextline(fp, buf, sizeof buf, '='), "= Berat  Badan (KG)  : %d", &bb) != 1
    ||  sscanf(nextline(fp, buf, sizeof buf, '='), "= Rutinitas Olahraga : %99[^\n]", tdeeprint) != 1
    ||  sscanf(nextline(fp, buf, sizeof buf, '='), "= TDEE               : %lf", &tdeenum) != 1
    ||  sscanf(nextline(fp, buf, sizeof buf, 0), "============================================================================%1[=]", ch) != 1) {
        fprintf(stderr, "invalid input: parse error at %s\n", buf);
        return 1;
    }
    fclose(fp);

    printf("Username : %s\n", tempat);
    printf("Password : %s\n", temp);
    printf("Name : %s\n", namalengkap);
    printf("Gender : %s\n", gender);
    printf("Umur : %d\n", umur);
    printf("TB : %d\n", tb);
    printf("BB : %d\n", bb);
    printf("tdeeprint : %s\n", tdeeprint);
    printf("tdeepnum : %f\n", tdeenum);

    return 0;
}

Output:

Username : hitunga
Password : 12345
Name : a
Gender : Perempuan
Umur : 30
TB : 155
BB : 50
tdeeprint : Jarang Berolahraga
tdeepnum : 1.200000
chqrlie
  • 131,814
  • 10
  • 121
  • 189