Есть ли функция, которая может преобразовывать числа, введенные из массива char в массив int? - PullRequest
0 голосов
/ 23 января 2019

Я пытаюсь передать массив через перегруженную функцию.Массив представляет собой строку чисел, разделенных пробелами.Я пытаюсь преобразовать массив char в массив int, так как у меня есть другая функция, которая вызывается с указателем на символ.

Вот мое текущее решение.Я попытался скопировать каждый элемент отдельно в массив int, однако, когда я пытаюсь читать из этого массива, я получаю кучу мусорных чисел.Он переходит в правильную перегруженную функцию, которую я создал.

  char str[100];             //string with inputted ints
int numStr[100];
int i = 0;
int count = 0;

fgets(str, 100, stdin);

while (str[i] != '\0')
{
    if (str[i] == ' ')
    {
        count++;
    }
    i++;
}
if (count >= 1)       //checks if there is more then one int
{
    for (int i = 0; str[i] != '\0'; i++)
    {
        numStr[i] = str[i];
        numStr[i] = numStr[i] - '/0';
    }
    printf("Going into the int array function\n");
    assessGrade(numStr);    //passing int array
}

Программа должна получить каждое число между пробелами для копирования в другой массив int вместе.

Ответы [ 4 ]

0 голосов
/ 23 января 2019
char str[100];
int numStr[100]; 
char *token;
int count = 0;
int spaceCounter = 0;

fgets(str, 100, stdin);

int i = 0;
while (str[i] != '\0')
{
    if (str[i] == ' ')
    {
        spaceCounter++;
    }
    i++;
}
if (spaceCounter >= 1)       //checks if there is more then one int
{
    token = strtok(str, " ");

    while (token != NULL)
    {
        //printf("%s \n", token);
        numStr[count] = atoi(token);
        token = strtok(NULL, " ");
        count++;
    }
 }
0 голосов
/ 23 января 2019
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

long *numbersToArray(const char *str, long *len) {
    const char *pos;
    char *endptr;
    long size, count, *ret, *tmp;
    size = 4;
    count = 0;
    ret = calloc(size, sizeof(long));
    if (!ret) return NULL;
    pos = str;
    while (isspace(pos[0])) ++pos;
    while (pos[0]) {
        if (count == size) {
            size += 4;
            tmp = realloc(ret, size * sizeof(long));
            if (tmp) ret = tmp;
            else {
                free(ret);
                return NULL;
            }
        }
        ret[count] = strtol(pos, &endptr, 0);
        // if (errno) ...
        ++count;
        pos = endptr;
        while (isspace(pos[0])) ++pos;
    }
    if (len) *len = count;
    return ret;
}

int main(int argc, char **argv) {
    if (argc < 2) {
        printf("usage: %s numbers\n", argv[0]);
        return 0;
    }
    long *arr, len, i;
    arr = numbersToArray(argv[1], &len);
    for (i = 0L; i < len; ++i) {
        printf("%ld\n", arr[i]);
    }
    return 0;
}
0 голосов
/ 23 января 2019

Продолжая ваш комментарий о возможности преобразования любого целочисленного представления значения в буфере в целое, ничто не заменит strtol. В то время как вы можете использовать sscanf, используя спецификатор %n, чтобы получить количество потребляемых символов, чтобы знать, сколько нужно продвигать указатель - почему? Любое из ваших scanf преобразований практически не имеет возможности сообщения об ошибках, кроме как успешно / неудачно. strtol имеет встроенную возможность работать с буфером, конвертируя значения в long по мере продвижения.

Как? Прототип для strtol:

long int strtol(const char *nptr, char **endptr, int base);

Если строка, содержащая числа, предоставлена ​​nptr, endptr будет установлен на один символ после последней цифры, использованной после успешного преобразования, а base предоставляет основание числа преобразований (например, основание). 2, 8, 10, 16 и т. Д.), Где основание 0 позволит преобразовать из восьмеричного, десятичного или шестнадцатеричного числа в зависимости от того, начинается ли строковое представление: 0 (восьмеричное) или 0x (шестнадцатеричное) или 1-9 ( десятичное).

Таким образом, если вы передадите strtol строку, содержащую число, при успешном преобразовании возвращаемое значение будет long, которое можно проверить по INT_MIN/INT_MAX, чтобы определить, находится ли оно в диапазоне int, endptr будет установлен на единицу после последней преобразованной цифры (настроен на использование для следующего значения), и все, что вам нужно, это установить p = endptr; и продолжать.

Далее, вы знаете, если nptr == endptr после преобразования - цифры не были преобразованы. strtol также устанавливает errno при переполнении / недополнении, так что у вас есть способ проверить успешное преобразование в long. Кроме того, если вы хотите проверить, что будет дальше, endptr указывает на начальный символ остальной части содержимого буфера. Вы узнаете вплоть до персонажа, что произошло с конверсией и что еще предстоит конвертировать.

Так как же вы используете это? Это действительно прямо вперед. Сначала давайте определим пару констант для числа целых чисел в массиве и максимального количества символов в вашем буфере, например,

#include <stdio.h>
#include <stdlib.h> /* for strtol   */
#include <limits.h> /* for INT_MIN/INT_MAX */
#include <errno.h>  /* for errno    */

#define ARSZ  100
#define MAXC 1024

Теперь объявите ваш массив, ваш буфер для хранения ввода и счетчик для отслеживания количества целочисленных значений, преобразованных и сохраненных в массиве:

    int arr[ARSZ] = {0};
    char buf[MAXC] = "";
    size_t n = 0;

Теперь давайте откроем файл для чтения с именем файла, предоставленным в качестве 1-го аргумента вашей программе (или прочитанным из stdin по умолчанию, если не указан аргумент), и подтвердим, что у нас есть действительный поток открытых файлов, например,

    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

Теперь пришло время приступить к чтению строк ввода и преобразованию любых символьных ссылок в числовые значения в long с strtol. Сначала прочитайте строку ввода в ваш буфер с помощью fgets и объявите ваши nptr (сокращенные до p), endptr и временное long значение tmp для хранения возврата из strtol, например,

    while (fgets (buf, MAXC, fp)) {     /* read each line of input */
        char *p = buf, *endptr;         /* nptr & endptr for strtol */
        long tmp;                       /* temp long for strtol */

В строке ввода вы теперь переходите к циклическому преобразованию значений с strtol, проверяя, что (1) цифры были преобразованы, (2) переполнение / недополнение не произошло (например, подгонка значения в long), ( 3) что значение находится в диапазоне int, или (4) предупреждают, что значение превышает размер int. (о вспомогательной функции nextdigit мы поговорим позже):

        /* protect array bounds, loop while not end of buf */
        while (n < ARSZ && *p && *p != '\n') {
            errno = 0;                      /* reset errno each iteration */
            tmp = strtol (p, &endptr, 0);   /* call strtol, update endptr */

            if (p == endptr)    /* validate digits converted */
                fputs ("error: no digits converted.\n", stderr);
            else if (errno)     /* validate conversion */
                fputs ("error: over/underflow occurred.\n", stderr);
            /* validate tmp is in range of integer */
            else if (INT_MIN <= tmp && tmp <= INT_MAX)
                arr[n++] = tmp;
            else
                fputs ("error: value exceeds range of int.\n", stderr);

            if (!(p = (char *)nextdigit (endptr)))  /* get next digit */
                break;
        }

( примечание: условия цикла while, n < ARSZ - не хранить больше целых чисел, чем у вас есть место, в то время как *p && *p != '\n' (ваш не в конце строки например, nul-символ или символ новой строки.) Вы можете опустить проверку новой строки, и она будет безобидно обрабатываться в теле цикла, но почему? Простая проверка полностью исключает тело цикла )

Что такое вспомогательная функция nextdigit()?Из прочтения страницы strtol (3) - Linux вы знаете, что strtol пропустит любой начальный пробел до начала следующего числа для преобразования, но что, если у вас есть запятая?отдельный файл или другие символы между вашими номерами?Очень прост в обращении, просто сканируйте вперед в вашем буфере, проверяя символы по мере продвижения, пока не найдете следующий 0-9, или вы не найдете следующий +/- и следующий символ после явного знака - 0-9.

Это все, что делает nextdigit, возвращая адрес начала следующего значения для преобразования, или NULL, если нет других цифр для преобразования.

/* scan forward in 'p' to find next valid signed integer beginning */
const char *nextdigit (const char *p)
{
    while (*p) {
        if (('0' <= *p && *p <= '9') || 
            ((*p == '-' || *p == '+') && '0' <= *(p + 1) && *(p + 1) <= '9'))
            return p;
        p++;
    }
    return NULL;
}

( примечание: вы можете и должны включить ctype.h и заменить чек ('0' <= *p && *p <= '9') простым isdigit(*p), и то же самое с isdigit(*(p+1)), но полный ручной тест был показан в целях иллюстрации)

Обратите внимание на код, как endptr (начальный адрес для сканирования вперед) передается в nextdigit(), а результат присваивается p и проверено не NULL до начала следующей итерации (с p, подготовленным для повторной передачи в strtol для следующего преобразования) - промойте и повторяйте, пока в вашем буфере не закончатся символы.

Подход к преобразованию буфера в целочисленные значения таким способом позволяет вам выбрать и преобразовать все целочисленные значения в строке - независимо от того, насколько грязен формат.

Если сложить все вместе, вы можете сделать:

#include <stdio.h>
#include <stdlib.h> /* for strtol   */
#include <limits.h> /* for INT_MIN/INT_MAX */
#include <errno.h>  /* for errno    */

#define ARSZ  100
#define MAXC 1024

/* scan forward in 'p' to find next valid signed integer beginning */
const char *nextdigit (const char *p)
{
    while (*p) {
        if (('0' <= *p && *p <= '9') || 
            ((*p == '-' || *p == '+') && '0' <= *(p + 1) && *(p + 1) <= '9'))
            return p;
        p++;
    }
    return NULL;
}

int main (int argc, char **argv) {

    int arr[ARSZ] = {0};
    char buf[MAXC] = "";
    size_t n = 0;
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {     /* read each line of input */
        char *p = buf, *endptr;         /* nptr & endptr for strtol */
        long tmp;                       /* temp long for strtol */

        /* protect array bounds, loop while not end of buf */
        while (n < ARSZ && *p && *p != '\n') {
            errno = 0;                      /* reset errno each iteration */
            tmp = strtol (p, &endptr, 0);   /* call strtol, update endptr */

            if (p == endptr)    /* validate digits converted */
                fputs ("error: no digits converted.\n", stderr);
            else if (errno)     /* validate conversion */
                fputs ("error: over/underflow occurred.\n", stderr);
            /* validate tmp is in range of integer */
            else if (INT_MIN <= tmp && tmp <= INT_MAX)
                arr[n++] = tmp;
            else
                fputs ("error: value exceeds range of int.\n", stderr);

            if (!(p = (char *)nextdigit (endptr)))  /* get next digit */
                break;
        }
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (size_t i = 0; i < n; i++) {    /* output results */
        if (i && i %10 == 0)            /* 10-values per row */
            putchar ('\n');
        printf (" %4d", arr[i]);
    }
    putchar ('\n');     /* tidy up with newline */
}

Теперь давайте посмотрим на строки, которые этот подход может обработать для преобразования в целочисленные значения, например

Пример входных файлов

Значения, разделенные пробелом:

$ cat dat/10int_space.txt
8572 -2213 6434 16330 3034 12346 4855 16985 11250 1495

Neзначения, разделенные строкой:

$ cat dat/10int_nl.txt
8572
-2213
6434
16330
3034
12346
4855
16985
11250
1495

Целые числа в беспорядке:

$ cat dat/10intmess.txt
8572,;a -2213,;--a 6434,;
a- 16330,;a

- The Quick
Brown%3034 Fox
12346Jumps Over
A
4855,;*;Lazy 16985/,;a
Dog.
11250
1495

Пример использования / Вывод

Как справедлива процедура преобразования?

Разделенные пробелами:

$ ./bin/fgets_strtol_any_fixed <dat/10int_space.txt
 8572 -2213 6434 16330 3034 12346 4855 16985 11250 1495

Разделенные строкой:

$ ./bin/fgets_strtol_any_fixed <dat/10int_nl.txt
 8572 -2213 6434 16330 3034 12346 4855 16985 11250 1495

Нечестивый беспорядок:

$ ./bin/fgets_strtol_any_fixed <dat/10intmess.txt
error: no digits converted.
error: no digits converted.
error: no digits converted.
error: no digits converted.
error: no digits converted.
 8572 -2213 6434 16330 3034 12346 4855 16985 11250 1495

Все значения, независимо от того, разделены ли пробелы,Отделенные символом новой строки, или посыпанные посередине «Быстрая коричневая лиса ...», были преобразованы правильно.

Найдите время, чтобы переварить справочную страницу для strtol, а затем разобраться, как это реализовано выше.После того, как вы овладеете strtolstrtoul (для unsigned long), strtod (для double) и т. Д. - все они работают практически одинаково, числовое преобразование практически невозможно)Я не знаю, если у вас есть дополнительные вопросы.

0 голосов
/ 23 января 2019

Позвольте упростить вашу проблему.

Сначала предположим, что у нас есть массив символов, содержащий числа: char str[100]="1 2 3 4 5 6"; вместо заполнения его из ввода.

выполнив это, и у нас есть небольшое изменение:


код

char str[100]="1 2 3 4 5 6";             //string with inputted ints
int numStr[100]={0};
int i = 0;
int count = 0;

while (str[i] != '\0')
{
    if (str[i] != ' ')
    {
        numStr[count]=str[i]-'0';
        count++;
    }
    i++;
}   
for (int i = 0; i<count; i++)
{
    printf("%d ", numStr[i]);
}

Теперь у нас есть числа, проиндексированные с [0: count] в numStr целочисленном массиве.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...