Ответ 1
Ты почти там. temp
сам не будет пустым, но он укажет на нулевой символ, если вся строка будет преобразована, поэтому вам нужно разыменовать ее:
if (*temp != '\0')
Программа ниже преобразует строку в длинную, но, основываясь на моем понимании, она также возвращает ошибку. Я полагаюсь на то, что если strtol
успешно преобразовать строку в long, то второй параметр strtol
должен быть равен NULL. Когда я запускаю приложение ниже с 55, я получаю следующее сообщение.
./convertToLong 55
Could not convert 55 to long and leftover string is: 55 as long is 55
Как я могу успешно обнаружить ошибки из strtol? В моем приложении нуль является допустимым значением.
код:
#include <stdio.h>
#include <stdlib.h>
static long parseLong(const char * str);
int main(int argc, char ** argv)
{
printf("%s as long is %ld\n", argv[1], parseLong(argv[1]));
return 0;
}
static long parseLong(const char * str)
{
long _val = 0;
char * temp;
_val = strtol(str, &temp, 0);
if(temp != '\0')
printf("Could not convert %s to long and leftover string is: %s", str, temp);
return _val;
}
Ты почти там. temp
сам не будет пустым, но он укажет на нулевой символ, если вся строка будет преобразована, поэтому вам нужно разыменовать ее:
if (*temp != '\0')
Обратите внимание, что имена, начинающиеся с символа подчеркивания, зарезервированы для реализации; лучше избегать использования таких имен в вашем коде. Следовательно, _val
должен быть просто val
.
Полная спецификация обработки ошибок для strtol()
и ее родственников сложна, на удивление сложная, когда вы сначала сталкиваетесь с ней. Единственное, что вы делаете абсолютно правильно, это использовать функцию для вызова strtol()
; использование его "raw" в коде, вероятно, неверно.
Поскольку вопрос отмечен как C, так и С++, я приведу цитату из стандарта C2011; вы можете найти соответствующую формулировку в стандарте С++ для себя.
ISO/IEC 9899: 2011 §7.22.1.4 Функции
strtol
,strtoll
,strtoul
иstrtoull
long int strtol(const char * restrict nptr, char ** restrict endptr, int base);
¶2 [...] Во-первых, они разлагают входную строку на три части: начальную, возможно пустую, последовательность символы пробела (как указано в функции isspace), последовательность объектов напоминающий целое число, представленное в некотором радиусе, определяемое значением базы, и конечная строка одного или нескольких непризнанных символов, включая завершающий нуль символ входной строки. [...]
¶7 Если последовательность объектов пуста или не имеет ожидаемой формы, преобразование не является выполнено; значение
nptr
сохраняется в объекте, на который указываетendptr
, при условии, что чтоendptr
не является нулевым указателем.Возврат
¶8 Функции
strtol
,strtoll
,strtoul
иstrtoull
возвращают преобразованные ценность, если таковая имеется. Если преобразование не может быть выполнено, возвращается ноль. Если правильное значение находится вне диапазона представляемых значений, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX или ULLONG_MAX (в соответствии с типом возврата и знак значения, если таковой имеется), а значение макроса ERANGE хранится вerrno
.
Помните, что никакая стандартная функция библиотеки C никогда не устанавливает errno
в 0. Поэтому, чтобы быть надежным, вы должны установить errno
в ноль перед вызовом strtol()
.
Итак, ваша функция parseLong()
может выглядеть так:
static long parseLong(const char *str)
{
errno = 0;
char *temp;
long val = strtol(str, &temp, 0);
if (temp == str || *temp != '\0' ||
((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
fprintf(stderr, "Could not convert '%s' to long and leftover string is: '%s'\n",
str, temp);
// cerr << "Could not convert '" << str << "' to long and leftover string is '"
// << temp << "'\n";
return val;
}
Обратите внимание, что при ошибке это возвращает 0 или LONG_MIN или LONG_MAX, в зависимости от возвращаемого strtol()
. Если ваш код вызова должен знать, было ли преобразование успешным или нет, вам нужен другой функциональный интерфейс - см. Ниже. Также обратите внимание, что ошибки следует печатать на stderr
, а не на stdout
, а сообщения об ошибках должны быть завершены с помощью новой строки \n
; Если это не так, они не гарантированно появятся своевременно.
Теперь, в коде библиотеки, вы, вероятно, не хотите печати, и ваш код вызова может захотеть узнать, было ли преобразование успешным, так что вы также можете пересмотреть интерфейс. В этом случае вы, вероятно, измените функцию, чтобы она отображала индикацию успеха/отказа:
bool parseLong(const char *str, long *val)
{
char *temp;
bool rc = true;
errno = 0;
*val = strtol(str, &temp, 0);
if (temp == str || *temp != '\0' ||
((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE))
rc = false;
return rc;
}
который вы можете использовать следующим образом:
if (parseLong(str, &value))
…conversion successful…
else
…handle error…
Если вам нужно различать "trailing junk", "invalid numeric string", "value too large" и "value too small" (и "no error" ), вы должны использовать целое число или enum
вместо логического кода возврата. Если вы хотите разрешить пробел в пробеле, но никаких других символов, или если вы не хотите разрешать какое-либо ведущее белое пространство, у вас есть больше работы в этой функции. Код позволяет восьмеричные, десятичные и шестнадцатеричные; если вы хотите строго десятичное, вам нужно изменить 0 на 10 при вызове strtol()
.
Если ваши функции маскируются как часть стандартной библиотеки, они не должны постоянно устанавливать errno
на 0
, поэтому вам нужно будет обернуть код для сохранения errno
:
int saved = errno; // At the start, before errno = 0;
…rest of function…
if (errno == 0) // Before the return
errno = saved;
Вам не хватает уровня косвенности. Вы хотите проверить, является ли символ завершающим NUL
, а не если указатель NULL
:
if (*temp != '\0')
Кстати, это не очень хороший подход для проверки ошибок. Правильный метод проверки ошибок семейства функций strto*
не выполняется путем сравнения выходного указателя с концом строки. Это нужно сделать, проверив нулевое возвращаемое значение и получив возвращаемое значение errno
.
Вы должны проверить
*temp != '\0'
Вы также можете проверить значение errno после вызова strotol в соответствии с этим:
RETURN VALUES
The strtol(), strtoll(), strtoimax(), and strtoq() functions return the result
of the conversion, unless the value would underflow or overflow. If no conver-
sion could be performed, 0 is returned and the global variable errno is set to
EINVAL (the last feature is not portable across all platforms). If an overflow
or underflow occurs, errno is set to ERANGE and the function return value is
clamped according to the following table.