How to read decimal numbers from a file which has non-English (Turkish) content in C

随声附和 提交于 2021-02-11 12:32:20

问题


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


回答1:


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)




回答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.




回答3:


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);


来源:https://stackoverflow.com/questions/61980995/how-to-read-decimal-numbers-from-a-file-which-has-non-english-turkish-content

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!