Правильное использование strtol - программирование
Подтвердить что ты не робот

Правильное использование strtol

Программа ниже преобразует строку в длинную, но, основываясь на моем понимании, она также возвращает ошибку. Я полагаюсь на то, что если 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;
}
4b9b3361

Ответ 1

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

if (*temp != '\0')

Ответ 2

Обратите внимание, что имена, начинающиеся с символа подчеркивания, зарезервированы для реализации; лучше избегать использования таких имен в вашем коде. Следовательно, _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;

Ответ 3

Вам не хватает уровня косвенности. Вы хотите проверить, является ли символ завершающим NUL, а не если указатель NULL:

if (*temp != '\0')

Кстати, это не очень хороший подход для проверки ошибок. Правильный метод проверки ошибок семейства функций strto* не выполняется путем сравнения выходного указателя с концом строки. Это нужно сделать, проверив нулевое возвращаемое значение и получив возвращаемое значение errno.

Ответ 4

Вы должны проверить

*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.