Неожиданный результат при использовании atoi () для содержимого файла, читайте char за char - PullRequest
0 голосов
/ 29 июня 2018

У меня есть текстовый файл, в котором есть только цифры (0, 1, 2 и 3), и я хочу обработать данные, чтобы узнать, сколько раз появляется каждое число.

Следующая программа работает с небольшим текстовым файлом (<100 чисел), но с большими файлами (мне нужно обработать в итоге несколько тысяч данных), программа считывает числа, которых нет в текстовом файле. </p>

Вот мой код:

FILE *file;
char c;
int nb;
int th0 = 0, th1 = 0, th2 = 0, th3 = 0;

file = fopen("../data", "r");

if (file == NULL) {
    printf("ERROR FILE: %s", strerror(errno));
    return EXIT_FAILURE;
}

while (1) {
    if (fscanf(file, "%c", &c) == EOF)
        break;

    nb = atoi(&c);

    printf("%d", nb);

    switch (nb) {
      case 0:
        th0++;
        break;

      case 1:
        th1++;
        break;

      case 2:
        th2++;
        break;

      case 3:
        th3++;
        break;

      default:
        break;
    }
}

Буду признателен за любые предложения.

РЕДАКТИРОВАТЬ, ввод текста с выводом:

Файл данных (181 число):

001110120101010102012012021201021202102012012012012010210210210120120230103120130230123201320310231023102302301231203213210131032103210230120320310320213202301320123120312302321023

Выход: Конец чтения не совпадает с тем, что находится в файле данных и насчитывает 156 чисел

Ответы [ 3 ]

0 голосов
/ 29 июня 2018

Ваша проблема в том, что atoi ожидает строку, и вы называете ее так:

nb = atoi(&c);

с c является просто char. Иногда это может сработать, но вы в основном используете неопределенное поведение, так как не можете гарантировать, что память после c пуста.

Вместо этого вы хотите вычислить nb по-другому.

nb = c - '0';

Это зависит от того факта, что в таблице ASCII числа от 0 до 9 находятся в одном блоке. Вычитая значение '0' из c, вы получите числовое значение этого символа ... при условии, что это цифра.

И чтобы убедиться, что это цифра, вы должны обернуть это выражение if в код

if(isdigit(c)) // Check if c is a digit
    {
    nb = c - '0';

    switch(nb) {
        case 0: th0++;
        break;
    // rest of switch goes here....
         }
    }
0 голосов
/ 29 июня 2018

Вы вызываете atoi с указателем на char, который не указывает на правильную строку C, следовательно, ваш код имеет неопределенное поведение и может иногда работать как ожидалось, а в других случаях работать некорректно ...

Поскольку вы имеете дело с цифрами, вы можете вычислить значение, представленное цифрой, простым вычитанием c - '0'.

Вы также можете упростить код с массивом:

#include <stdio.h>

int main() {
    FILE *file;
    int c, i;
    int count[10] = { 0 };

    file = fopen("../data", "r");
    if (file == NULL) {
        printf("ERROR FILE: %s", strerror(errno));
        return EXIT_FAILURE;
    }

    while ((c = getc(file)) != EOF) {
        if (c >= '0' && c <= '9')
            count[c - '0']++;
    }
    fclose(file);

    printf("counts:\n");
    for (i = 0; i < 10; i++) {
        printf("%d: %d\n", i, count[i]);
    }
    return 0;
}
0 голосов
/ 29 июня 2018

Глядя на

https://en.cppreference.com/w/c/string/byte/atoi

Понятно

Параметры
str - указатель на байтовую строку с нулевым символом в конце для интерпретации

Но с

char c;
/* ... */
nb = atoi(&c);

вы используете указатель на один char, за которым следует кто-знает-что.
Для всего, что не является '\0', вы получите результат от atoi(), который равен

а) на основе доступа за пределы предполагаемой переменной
б) для следующих цифр - двузначное число или больше

Первый вариант означает, что ваш код нуждается в исправлении. Второй вариант может объяснить любое число> 9.

...