У вас есть три основных варианта: (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 */
}
}
Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.