Как мне разобрать строку чисел в массив целых чисел? - PullRequest
0 голосов
/ 20 ноября 2018

Я некоторое время боролся с этим и подумал, что с тем же успехом мог бы попросить о помощи, вместо того, чтобы биться сильнее головой о стену.

Допустим, у вас есть строка "10 10 10 4 4 4 9 9 9 2" и вы хотите пройти через это, вынуть числа по одному и добавить его в массив целых чисел для использования.

Я создал прототип LOT и продолжаю делать для себя больше работы, чем необходимо. Сначала я использовал strtok(), но потом люди сказали, что это устарело, и было бы проще использовать strsep()

Как бы я поступил так?

Любая помощь будет принята с благодарностью!

Кажется, моя функция всегда возвращает массив int, полный нулей. Почему это?

int *parse_line(char *line){
    char sNumArray[MAX];
    strcpy(sNumArray, line);
    char *tokens = NULL;
    int *numbers = malloc(sizeof(int) * MAX);
    tokens = strtok(sNumArray, " ");
    for(int i = 0; ; i++) {
        numbers[i] = atoi(tokens);
        printf("%d \n", atoi(tokens));
        tokens = strtok(NULL, " ");
        if (tokens == NULL)
            break;
    }
    return numbers;
}

Это мои переменные, которые я определяю в главном и вызываю свою функцию с помощью ...

int *skyline;
skyline = parse_line(line);
for (int j = 0; j < 100 ; ++j) {
    printf("%d \n", skyline[j]);
}

Ответы [ 4 ]

0 голосов
/ 20 ноября 2018

У вас есть три основных варианта: (1) используйте strtol так, как это было задумано, используя параметр *endptr, чтобы переместить текущую позицию чтения в строке на одну позицию после последней преобразованной цифры или (2) проход sscanf с использованием спецификатора "%n" для отчета о количестве символов, использованных при преобразовании в int (или любой другой тип), и использования этого значения для продвижения позиции чтения таким же образом; или (3) маркировать строку с помощью strtok и затем использовать strtol (так как atoi не должен использоваться, поскольку он обеспечивает абсолютно нулевую проверку ошибок). На самом деле нет необходимости использовать и strtok, и strtol, поскольку strtol уже предоставляет способ для перехода к преобразованным цифрам. По сути, вы дублируете то, что уже сделано с помощью strtol, используя вызов strtok - но это правильный путь.

Например, используя strtol, вы можете сделать что-то вроде следующего:

#include <stdio.h>
#include <stdlib.h>     /* for strtol */
#include <string.h>     /* for strncpy */
#include <errno.h>      /* for errno */

#define MAXC 1024   /* constant - max chars in line */

int main (void) {

    char str[MAXC] = "";    /* str to hold line, initialized all zero */

    while (fgets (str, MAXC, stdin)) {  /* read each line of input */
        char *p = str,      /* pointer for strtol */
            *endptr = NULL; /* end pointer for strtol */

        while (*p) {    /* work down str parsing integer or hex values */
            long val = strtol (p, &endptr, 0);  /* convert from p */

            /* validate conversion */
            if (p != endptr) {  /* were digits converted? */
                if (!errno) {   /* if errno 0, successful conversion */
                    char ascii[MAXC] = "";  /* for string converted */

                    strncpy (ascii, p, endptr - p); /* copy to ascii */
                    ascii[endptr-p] = 0;    /* nul-terminate ascii */

                    /* test whether string begins "0x" or "0X", output */
                    if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X'))
                        printf ("hex conversion:  %-10s %10lu  0x%lx\n",
                                ascii, val, val);
                    else
                        printf ("int conversion:  %-10s % ld\n",
                                ascii, val);
                }
                p = endptr; /* advance p to 1-past end of converted string */
            }

            /* find start of next valid number in str, including (+/-) */
            for (; *p; p++) {
                if ('0' <= *p && *p <= '9')  /* positive value */
                    break;          /* explicitly signed value */
                if ((*p == '+' || *p == '-') && '0' <= *(p+1) && *(p+1) <= '9')
                    break;
            }
        }
    }

    return 0;
}

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

$ echo "10 10 10 4 4 4 9 9 9 2" | ./bin/fgets_strtol_any
int conversion:  10          10
int conversion:  10          10
int conversion:  10          10
int conversion:  4           4
int conversion:  4           4
int conversion:  4           4
int conversion:  9           9
int conversion:  9           9
int conversion:  9           9
int conversion:  2           2

или преобразование всех целых чисел в грязный файл, например

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

$ 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 <dat/10intmess.txt
int conversion:  8572        8572
int conversion:  -2213      -2213
int conversion:  6434        6434
int conversion:  16330       16330
int conversion:  3034        3034
int conversion:  12346       12346
int conversion:  4855        4855
int conversion:  16985       16985
int conversion:  11250       11250
int conversion:  1495        1495

Использование sscanf

Аналогично, вы можете использовать sscanf, но имейте в виду, что он не обеспечивает уровень или степень обработки ошибок - это означает, что вы можете только знать, что он либо успешно преобразовал текст, либо потерпел неудачу. Нет промежуточных данных, нет сообщений о переполнении или недостаточном объеме через errno. Но, тем не менее, он наряду с strtok является другим допустимым средством синтаксического анализа целых чисел из строки текста, например,

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

#define MAXC 1024

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

    char buf[MAXC] = "";    /* buffer to hold MAXC chars at a time */
    int nval = 0;           /* total number of integers found */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {

        char *p = buf;      /* pointer to line */
        int val,            /* int val parsed */
            nchars = 0;     /* number of chars read */

        /* while chars remain in buf and a valid conversion to int takes place
        * output the integer found and update p to point to the start of the
        * next digit.
        */
        while (*p) {
            if (sscanf (p, "%d%n", &val, &nchars) == 1) {
                printf (" %d", val);
                if (++nval % 10 == 0)     /* output 10 int per line */
                    putchar ('\n');
            }
            p += nchars;        /* move p nchars forward in buf */

            /* find next number in buf */
            for (; *p; p++) {
                if (*p >= '0' && *p <= '9') /* positive value */
                    break;
                if (*p == '-' && *(p+1) >= '0' && *(p+1) <= '9') /* negative */
                    break;
            }
        }
    }
    printf ("\n %d integers found.\n", nval);

    if (fp != stdin) fclose (fp);     /* close file if not stdin */

    return 0;
}

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

$ echo "10 10 10 4 4 4 9 9 9 2" | ./bin/fgets_sscanf_int_any_ex
 10 10 10 4 4 4 9 9 9 2

 10 integers found.

или с грязным вводом

$ echo "1, 2 buckle my shoe, 3, 4..." | ./bin/fgets_sscanf_int_any_ex
 1 2 3 4
 4 integers found.

Использование strtok будет просто «входным» для преобразования с strtol, показанным в первом примере (который предоставляет свой собственный способ токенизации числовых значений). Вы просто зацикливаетесь на буфере, вызывая strtok с разделителями " \n" (пробел, новая строка), а затем используете strtol для преобразования указанной строки. (здесь вы просто используете endptr для проверки того, что цифры были преобразованы, и игнорируете его использование для продвижения после преобразованных цифр. По сути, strtok дублирует то, что уже сделано strtok, но если это облегчает понимание, и Вы можете жить с повторяющимся вызовом, это нормально. Вы можете сделать что-то вроде следующего.

    while (fgets (buf, MAXC, fp)) {
        char *p = buf;  /* pointer to buf to use with strtok */
        /* 1st call using buffer, all remaining calls using NULL */
        for (p = strtok (p, " \n"); p; p = strtok (NULL, " \n")) {
            errno = 0;                          /* reset errno */
            char *endptr;                       /* end pointer */
            long tmp = strtol (p, &endptr, 0);  /* convert using long */
            if (p != endptr) {      /* validate digits converted */
                /* now validate value within range of int */
                if (!errno && INT_MIN <= tmp && tmp <= INT_MAX)
                    /* you have an integer! */
            }
            else if (tmp == 0)
                /* no digits were converted */
        }
    }

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 20 ноября 2018

Я думаю, что это будет делать то, что вы хотите.

#include "stdafx.h"

#include <stdio.h>
#include <iostream>
#include <math.h>
#include <stdlib.h>
using namespace std;

#define MAX 100

int *parse_line(char *line, int *numInts) {
    char sNumArray[MAX];
    strcpy(sNumArray, line);
    int *numbers = (int *) malloc(sizeof(int) * MAX);
    char *tokens = strtok(sNumArray, " ");
    for (int i = 0; ; i++) {
        numbers[i] = atoi(tokens);
        tokens = strtok(NULL, " ");
        if (tokens == NULL) {
            *numInts = i+1;
            break;
        }       
    }

    return numbers;
}

int main() {
    char *line = "10 10 10 4 4 4 9 9 9 2";
    int numIntsExtracted = 0;
    int *skyline = parse_line(line, &numIntsExtracted);

    for (int j = 0; j < numIntsExtracted; ++j) {
        printf("%d \n", skyline[j]);
    }
    return 0;
}

И вывод, который я получаю после запуска.

10
10
10
4
4
4
9
9
9
2
0 голосов
/ 20 ноября 2018

Мне нравится использовать для этого функцию strtol(), потому что, если вы передадите ей указатель, она вернет следующую точку для продолжения анализа. Существуют также неподписанные версии, например: strtoul(). Они стандартны с C99. Также strtol() может анализировать шестнадцатеричное значение и обрабатывает ошибки несколько лучше, чем старые функции, такие как atoi() (которая возвращает 0 при ошибке).

Важной частью кода ниже является результат strtol(). Когда next_number не изменяется после вызова, больше нет ввода (или произошла ошибка). Переменная ptr используется для отслеживания того, где находится синтаксический анализ в строке. Он присваивается strtol(), который изменяет next_number, указывая на следующий элемент, поэтому ptr переходит вперед - присваивается next_number (после только что проанализированного элемента), и процесс повторяется.

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

int main(void)
{
    char *number_str = "10 10 10 4 4 4 9 9 9 2";
    char *ptr;
    char *next_number;
    int   numbers[1000];
    int   number_count = 0;
    long  num;

    next_number = number_str;

    do
    {
        ptr = next_number;
        num = strtol(ptr, &next_number, 10);
        if (ptr != next_number) // found one
        {
            numbers[number_count] = (int)num;
            printf("Stored %3d into numbers[%d]\n", numbers[number_count], number_count);
            number_count += 1;
        }
    } while(ptr != next_number);

    return 0;
}
0 голосов
/ 20 ноября 2018

Просто используйте scanf (), чтобы получить каждое число по одному в цикле for или while.

for i = 0 to n
scanf(“%d”, &num);

Просьба ссылаться на поиск в Google или Bing онлайн, как это делается на многих доступных примерах.

...