0

I have a file and I am trying to multiply the amount of unit entered with the price if the price is int, it is ok but it does not have a decimal value of 0.0000 result. Thank you.

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

int main()
{
    int birim;    // unit
    float birim_fiyat; // unit price
    char barkod[10]; // barcode
    char kategori[10], urunadi[10], istenen[10];
    float ucret=0.00;  //price
    setlocale(LC_ALL,"Turkish");
    FILE *fp=fopen("barkod.txt","r+");
    if(fp==NULL)
    {
        puts("cannot open file");
        exit(1);
    }
    printf("Enter the Product Barcode Number to be Called:");
    scanf("%s",&istenen);  // find barcode

    printf("How many:");
    scanf("%d",&birim);

    while(!feof(fp))
    {
        fscanf(fp,"%s %s %s %f\n",barkod,urunadi,kategori,&birim_fiyat);
        if(strcmp(istenen,barkod)==0)
        {
          printf("Aranan Ürün Bulundu: %s %s\n", urunadi,kategori);
          ucret +=birim*birim_fiyat;
    }
}

This is my barcode file:

    825 Meyve sebze 12.50
    403 Cikolata Tatli 5.50
    902 Havu‡ Meyve 15.50
    881 S A 15.50
    866 Zemzem suyu 10.50
    300 Tiner Icecek 10.50

m0hithreddy
  • 1,752
  • 1
  • 10
  • 17
Marilyn
  • 1
  • 1
  • 2
    `it does not have a decimal` Where? The posted code does not print the total cost anywhere. – dxiv May 24 '20 at 02:44
  • Who suggest using [`while(!feof(fp))`](https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong)? – chux - Reinstate Monica May 24 '20 at 02:49
  • @dxiv I can't see where the mistake is float price:12.50 birim=10 (example) 12.5(output) – Marilyn May 24 '20 at 03:00
  • 1
    @Marilyn `12.5(output)` Again, where? There isn't one single floating point value printed anywhere in the code you posted. – dxiv May 24 '20 at 03:06
  • @dxiv Inside the 12.50 barcode file. I am trying to multiply the price of the barcode number I am looking for with the price I have determined, but it gives blank output when it is float value but it is 0.0000 but it works smoothly when it is an integer. – Marilyn May 24 '20 at 03:10
  • 1
    @Marilyn Where is the line of code that "*gives blank output*"? I don't see that code in what you posted. – dxiv May 24 '20 at 03:14
  • I suspect the line of output that gives blank results is the result of `while(!feof(fp))` leading to *Undefined Behavior* when `fscanf(fp,"%s %s %s %f\n",barkod,urunadi,kategori,&birim_fiyat);` returns `EOF` but then the values are blindly used in `printf("Aranan Ürün Bulundu: %s %s\n", urunadi,kategori);` – David C. Rankin May 24 '20 at 03:16

3 Answers3

3

I have no idea about locale, but it seems to be the cause of the problem in your case. I took your code and removed all Turkish letters, and tried to run. It ran fine ! Below is the quick and dirty modifications I did to your code.

Code

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

int main()
{
    int birim;    // unit
    float birim_fiyat; // unit price
    char barkod[10]; // barcode
    char kategori[10], urunadi[10], istenen[10];
    float ucret=0.00;  //price
    FILE *fp=fopen("barkod.txt","r+");
    if(fp==NULL)
      {
        puts("cannot open file");
        exit(1);
      }
      printf("Enter the Product Barcode Number to be Called:");
      scanf("%s",&istenen);  // find barcode

  printf("How many:");
  scanf("%d",&birim);

  while(!feof(fp))
  {
  fscanf(fp,"%s %s %s %f\n",barkod,urunadi,kategori,&birim_fiyat);
  printf("%s %s %s %f\n",barkod,urunadi,kategori,birim_fiyat);
  if(strcmp(istenen,barkod)==0)
  {
  printf("Aranan: %s %s\n", urunadi,kategori);
  ucret +=birim*birim_fiyat;

 }
  }

  printf("%f\n", ucret);

}

barkod.txt

825 Me e 12.5
403 Ci e 5.50
902 Hav e 15.50
881 S e 15.50
866 Zemz e 10.50
300 Tinev Icecek 10.50

Output

Enter the Product Barcode Number to be Called:825
How many:2
825 Me e 12.500000
Aranan: Me e
403 Ci e 5.500000
902 Hav e 15.500000
881 S e 15.500000
866 Zemz e 10.500000
300 Tinev Icecek 10.500000
25.000000

It printed 25, I hope this is the result you are expecting. In investigation further I tried to print a decimal value of one turkish letter.

Code

#include <stdio.h>

int main() {
    printf("%d\n",'‡');
    return 0;
}

Compiler Warnings

base.c: In function ‘main’:
base.c:4:16: warning: multi-character character constant [-Wmultichar]
    4 |  printf("%d\n",'‡');
      |                ^~~~~

Output

14844065

As others mentioned, you may be not having appropriate checks atfscanf(), I am not talking about it pls go through fscanf() man page. And, someother needs to answer your question about how to have turkish letters in file and how to read them successfully.

EDIT

For completeness of the answer. From the man page of fscanf()

Upon successful completion, these functions shall return the number of successfully matched and assigned input items; this number can be zero in the event of an early matching failure. If the input ends before the first conversion (if any) has completed, and without a matching failure having occurred, EOF shall be returned. If an error occurs before the first conversion (if any) has completed, and without a matching failure having occurred, EOF shall be returned and errno shall be set to indicate the error. If a read error occurs, the error indicator for the stream shall be set.

You dont know whether the file is complete (all 4 variables are indeed there) or when the file ends or when the error happens in underlying read operation. So it mandatory to have a success check after fscanf(). Man page says if no error happens, it returns the number of variables it read. So you can have a check like this.

while(fscanf(fp,"%s %s %s %f\n",barkod,urunadi,kategori,&birim_fiyat) == 4) {
    ....... 
}

As @dxiv suggested the Turkish decimal separator is , not .. Based on that your barkod.txt should be something like this

barkod.txt

825 Meyve sebze 12,50
403 Cikolata Tatli 5,50
902 Havu‡ Meyve 15,50
881 S A 15,50
866 Zemzem suyu 10,50
300 Tiner Icecek 10,50

Then the program runs fine outputting 25,000000 (whose English equivalent is 25.000000) when run with the inputs provided as before (825, 2)

m0hithreddy
  • 1,752
  • 1
  • 10
  • 17
  • A very nicely formatted answer and shows good effort -- but you really need to drive home [Why is while ( !feof (file) ) always wrong?](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong). And yes, while I agree with the subtle reference to the benefit of reading with `fgets()` and then parsing with `sscanf()` -- it is best to not leave the `while (!feof(fp))` in the answer. (fixing that and I will be happy to UV the answer) – David C. Rankin May 24 '20 at 03:48
  • Thanks ! I will make it complete – m0hithreddy May 24 '20 at 03:49
  • 1
    The only other comment I should have made in my answer as well is a caution about the array size of `10` for the barcode input string. If they are guaranteed to be 9-char or less -- that is fine, but it is a risk. The common rule is *"Don't Skimp on Buffer Size!"*. Far better to be 500 too-long than 1 too-short. Generally largest anticipated input and then double the size is a good rule of thumb (if you are on an embedded system with limited memory then 1.2 - 1.5 times may be okay) – David C. Rankin May 24 '20 at 03:53
  • @MohithReddy I would guess the Turkish decimal separator is a `,` comma, not a `.` dot. – dxiv May 24 '20 at 04:27
  • @dxiv So we need to represent ``12.5`` as ``12,5`` ? I am new to using ``locale``, but ``fscanf()`` man page has a note on ``LC_NUMERIC`` does fscanf() respects the current ``locale`` ? – m0hithreddy May 24 '20 at 04:44
  • @MohithReddy Right, that's what I am thinking. For a quick test, you could change the locale back to Turkish and edit the data file to use commas instead of dots. – dxiv May 24 '20 at 04:48
  • Yes, I tried what you have said. Its perfectly working. The output is something like ``25,000000`` , and this should be meaning ``25.000000`` in English – m0hithreddy May 24 '20 at 04:55
2

You have a number of problems:

  1. You cannot use any input function correctly unless you check the return;
  2. See: Why is while ( !feof (file) ) always wrong?;
  3. Including the & before istenen in scanf("%s", istenen is wrong

Why?

When taking any input, regardless whether it is from the user or a file, you must check the return to determine whether the input succeeded or failed before proceeding to use those values filled by the input in the rest of your code. Otherwise, you invite Undefined Behavior by blindly using the variables that will be left indeterminate if your input fails. At minimum, you need, e.g.

    fputs ("Enter the Product Barcode Number to be Called: ", stdout);
    if (scanf("%s", istenen) != 1) { // NO & - istened already a pointer
        fputs ("error: user canceled input.\n", stderr);
        exit (EXIT_FAILURE);
    }

    fputs ("How many: ", stdout);        /* just use fputs() unless a conversion needed */
    if (scanf("%d", &birim) != 1) {
        fputs ("error: invalid integer input.\n", stderr);
        exit (EXIT_FAILURE);
    }

Which leads to why while (!feof(fp)) is always wrong. As detailed in the answer, the problem is after your last good read of values from your file, EOF is NOT set, you check while (!feof(fp)) and it tests true and you loop again and you attempt fscanf(fp,"%s %s %s %f\n",barkod,urunadi,kategori,&birim_fiyat); which fails returning EOF, but since you do not check for EOF before printf("Aranan Ürün Bulundu: %s %s\n", urunadi,kategori); you invoke Undefined Behavior using urunadi and kategori while there values are indeterminate.

You fix that by using the result of the read as the test condition for you loop, e.g.

    while(fscanf(fp,"%s %s %s %f\n", barkod, urunadi, kategori, &birim_fiyat) == 4) {
        if (strcmp (istenen,barkod) == 0) {
            printf ("Aranan Ürün Bulundu: %s %s\n", urunadi, kategori);
            ucret += birim * birim_fiyat;
        }
    }

Finally as noted in the comment above, you do not place an & before istenen in scanf("%s", istenen). Why? istenen is already a pointer by virtue of an array being converted to a pointer to the first element, C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)

Putting it altogether (and commenting the setlocale for testing on my box), you would have:

#include <stdio.h>
// #include <conio.h>
#include <stdlib.h>
#include <string.h>
// #include <locale.h>

int main (void) {

    int birim;    // unit
    float birim_fiyat; // unit price
    char barkod[10]; // barcode
    char kategori[10], urunadi[10], istenen[10];
    float ucret=0.00;  //price
    // setlocale(LC_ALL,"Turkish");
    // FILE *fp=fopen("barkod.txt","r+");
    FILE *fp=fopen("dat/barcode.txt","r");

    if (fp == NULL) {
        fputs("cannot open file\n", stderr);
        exit (EXIT_FAILURE);
    }

    fputs ("Enter the Product Barcode Number to be Called: ", stdout);
    if (scanf("%s", istenen) != 1) { // NO & - istened alread a pointer
        fputs ("error: user canceled input.\n", stderr);
        exit (EXIT_FAILURE);
    }

    fputs ("How many: ", stdout);
    if (scanf("%d", &birim) != 1) {
        fputs ("error: invalid integer input.\n", stderr);
        exit (EXIT_FAILURE);
    }

    while(fscanf(fp,"%s %s %s %f\n", barkod, urunadi, kategori, &birim_fiyat) == 4) {
        if (strcmp (istenen,barkod) == 0) {
            printf ("Aranan Ürün Bulundu: %s %s\n", urunadi, kategori);
            ucret += birim * birim_fiyat;
        }
    }
}

(note: you have nothing that requires conio.h so do not include that old DOS header. It makes your code 100% non-portable)

Also, unless you are writing back to the file, just open the file with "r" instead of "r+".

Example Use/Output

$ ./bin/barcode
Enter the Product Barcode Number to be Called: 902
How many: 10
Aranan Ürün Bulundu: Havu‡ Meyve

Look things over an let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • I am completely aware of how to read a string into character array (not having ``&`` before the variable). But in a hurry I did not rectify it in my modified code and it still ran fine, now I am interested in how ? Do you have any thoughts ? Pls go though my answer once – m0hithreddy May 24 '20 at 03:47
  • 1
    @MohithReddy How is because both `istenen` and `&istenen` resolve to the same address **but are completely different types** (and thus have completely different pointer arithmetic). With [6.3.2.1(p3)](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.1p3) `istenen` is a pointer to the first char in the array of type `char *` while `&istenen` is a *pointer-to-array of* `char [10]` which is type `char (*)[10]`. So HOW it works is due to both resolving to the same address -- but you will note the compiler warning about the case with `&`. – David C. Rankin May 24 '20 at 04:01
  • How ``istenen`` and the character array of 10 bytes resides in memory ? Is it not like pointers ? If suppose ``istenen`` is a pointer it would be residing somewhere else, but having the 10 bytes array address (start value of the address) as its value, then ``&istenen`` will give the address where ``istenen`` is residing in the memory. So the memory layout of ``istenen[10]`` is different from that of pointer ``*istenen`` ? – m0hithreddy May 24 '20 at 04:18
  • 1
    Since `lstenen` is an array or 10-bytes, those 10-bytes are guaranteed to be sequential in memory. When it is converted to a pointer, e.g. `char *p = lstenen;`, `p` is a pointer to the first character in `lstenen` and `p++;` advances by 1-byte to point to the next character. However when `&lstenen` is used, the pointer is `char (*p)[10] = &lstenen;` which points to the same address but has type *pointer to array* `char[10]` and `p++;` would advance by 10-bytes to point to the beginning of the next `char (*)[10]` array in memory. Just writing to the address writes to the same place. – David C. Rankin May 24 '20 at 05:05
  • Turkish locale uses comma as decimal separator which just might be the problem alone. – Antti Haapala -- Слава Україні May 24 '20 at 05:09
  • @DavidC.Rankin I got you. These are pointer basics that I have been taught,sry I was confused . Thanks for the clarification. – m0hithreddy May 24 '20 at 05:16
  • @AnttiHaapala -- Thank you -- the difference in local separator didn't even click given the proposed input file. – David C. Rankin May 24 '20 at 05:34
1

The main problem here is that the Turkish locale uses comma as decimal separator. This applies even to parsing file contents with fscanf - it expects to find a decimal , in the number but sees only . and stops at that, and after that the parsing goes off-sync.

If you're building for a POSIX-2008+ compliant system, you can use uselocale to temporarily change the locale when reading/writing to/from file.

Something like:

locale_t clocale = newlocale(LC_ALL, "C", NULL);
locale_t old_locale = uselocale(clocale);
// read/write to file using the C locale which uses `.` as the decimal separator
// finally restore the previous locale
uselocale(old_locale);