Проверка, чтобы убедиться, что argv[1] является целым числом С++

Для моей программы я должен убедиться, что пользователь вводит только положительный INTEGER. например, если пользователь ввел 12hi, он не должен запускать программу и печатать стандартную ошибку. Я не совсем уверен, как это реализовать.

int main(int argc, char *argv[])   
{ 
    if(atoi(argv[1]) < 1)
    {
        cerr << "ERROR!"<< endl;
        return 1;
    }
    return 0;
}

person Ryan Mann    schedule 22.04.2013    source источник
comment
Что-то не так с вашей текущей реализацией? Если да, расскажите, чем ваша реализация отличается от ваших требований.   -  person Oswald    schedule 23.04.2013
comment
atoi недостаточно сложен, чтобы дать здесь правильный ответ. Посмотрите на strtol.   -  person Pete Becker    schedule 23.04.2013
comment
Это только проверяет, является ли это положительным числом, я не могу найти способ проверить, является ли оно целым числом, например: если вы получили 28 из argv, main должен вернуть 0, но если вы получили 28abc, main должен вернуть false.   -  person Ryan Mann    schedule 23.04.2013
comment
Вам нужно проверить значение argc, прежде чем вы даже подумаете о проверке argv[1]. Подумайте, что произойдет, если вы запустите программу без аргументов. И вам не хватает как минимум пары обязательных директив #include.   -  person Keith Thompson    schedule 23.04.2013
comment
я просто не включил их все, argc уже проверен и хорошо, и все #includes находятся в файле .h   -  person Ryan Mann    schedule 23.04.2013


Ответы (5)


Передайте его в std::istringstream и убедитесь, что все данные обработаны:

if (a_argc > 1)
{
    std::istringstream in(a_argv[1]);
    int i;
    if (in >> i && in.eof())
    {
        std::cout << "Valid integer\n";
    }
}

См. онлайн-демонстрацию по адресу http://ideone.com/8bEYJq.

person hmjd    schedule 22.04.2013
comment
Это именно то, что я искал! - person kayleeFrye_onDeck; 10.07.2017

Хорошо, мой пересмотренный ответ. sscanf вел себя не так, как я думал, а strtol предоставляет лучшее решение, похожее на C, которое очень портативно.

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

int main(int argc, char* argv[])
{
  for (int i=1; i < argc; i++){
      char* end;
      long val = strtol(argv[i], &end, 10);
      if (argc >= 2 && !end[0] && val >= 0){
          printf("%s is valid\n", argv[i]);
      } else {
          printf("%s is invalid\n", argv[i]);
      }
  }
  return 0;
}

Пример вывода: ./a.out 10 -1 32 1000 f -12347 +4 --10 10мусор

10 is valid
-1 is valid
32 is valid
1000 is valid
f is invalid
-12347 is valid
+4 is invalid
--10 is invalid
10rubbish is invalid

Это работает, потому что strtol преобразует аргумент в длинное целое число. Затем, если end[0] не находится в конце строки, он будет ненулевым, что означает, что он выдаст ошибку для 10rubbish, но будет в порядке для таких значений, как 10. Тогда, конечно, нам нужны только положительные целые числа, и я включил значение 0 в этот набор.

atoi() сама по себе недостаточно хороша, так как возвращает ноль в случае ошибки. 0 может быть допустимым вводом.

sscanf() сам по себе тоже недостаточно хорош, потому что он успешно преобразует такие строки, как 10rubbish, и возвращает значение 10.

Я понимаю, что op хочет только argv[1], этот ответ сканирует все предоставленные аргументы, чтобы показать вывод множества действительных и недействительных записей.

person hookenz    schedule 22.04.2013
comment
Без спецификатора формата % в "u" это не слишком клевета. - person WhozCraig; 23.04.2013
comment
strtol будет работать нормально, так как вы передаете указатель на указатель, он будет заполнен концом переданной информации. Подтвердите, что это конец строки. Если endptr не равен NULL, функция strtol() сохраняет адрес первого недопустимого символа в *endptr. Однако, если цифр вообще не было, strtol() сохраняет исходное значение str в *endptr. (Таким образом, если *str не равно \0' but **endptr is \0' при возврате, вся строка была допустимой.) - person Alan Shutko; 23.04.2013
comment
ошибаюсь, я очень давно не использовал strtol. Исправлен отсутствующий % - упс. - person hookenz; 23.04.2013
comment
Решение не позволяет программе обнаруживать отрицательные целочисленные аргументы, которые OP желает отклонить. Отрицательный целочисленный аргумент будет принят и преобразован в беззнаковый, например. -12567 даст val = 4294954729 - person Mike Kinghan; 23.04.2013

Поскольку вы, очевидно, не возражаете против использования стандартной библиотеки C, функция

long strtol (const char* str, char** endptr, int base)

from <cstdlib> вполне достаточно, чтобы аргумент командной строки был (длинным) целым числом с необязательным префиксом "-" или "+", и не более того. Вам просто нужно проверить, что char * хранится в endptr по адресам возврата '\0', что говорит вам о том, что функция использовала весь аргумент.

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])   
{
    if (argc < 2) {
        return 1;
    }

    char * endp;
    long i = strtol(argv[1],&endp,10);
    if (!*endp) {
        cout << "The value of \"" << argv[1] << "\" is " << i << endl;
        return 0;
    }
    cerr << "\"" << argv[1] << "\" is not an integer" << endl;
    return 1;
}

ПОЗЖЕ ...или комментарии Стива Джессопа:

#include <cstdlib>
#include <iostream>
#include <climits>

using namespace std;

int main(int argc, char *argv[])   
{
    if (argc < 2) {
        return 1;
    }

    char * endp;
    long i = strtol(argv[1],&endp,10);

    if (*endp) {
        cerr << "\"" << argv[1] << "\" is not an integer :(" << endl;
        return 1;
    }
    if (endp == argv[1]) {
        cerr << "Empty string passed :(" << endl;
        return 1;
    }
    if (i < 0) {
        cerr << "Negative " << i << " passed :(" << endl;
        return 1;
    }
    if (i <= INT_MAX) {
        cout << "Non-negative int " << i << " passed :)" << endl;
    } else {
        cout << "Non-negative long " << i << " passed :)" << endl;
    }
    return 0;

}

Для такой степени дискриминации подойдет функция-оболочка. И остается очень-очень крайний случай, когда ввод ULONG_MAX будет принят как LONG_MAX.

person Mike Kinghan    schedule 22.04.2013
comment
+1, но есть одна ошибка, заключающаяся в том, что вы неправильно обработали случай, когда argv[1] указывает на пустую строку. Существует также возможное улучшение, которое заключается в том, что вы можете захотеть напечатать другое сообщение в случае, когда входная строка представляет собой значение, выходящее за пределы диапазона long. - person Steve Jessop; 23.04.2013

Вы можете попробовать проверить, все ли символы в argv[1] являются цифрами (возможно, с ведущим знаком минус). Проверку можно выполнить с помощью стандартной библиотечной функции isdigit().

http://www.cplusplus.com/reference/cctype/isdigit/

Полное решение, основанное на фактическом коде OP (также доступно по адресу http://codepad.org/SUzcfZYp):

#include <stdio.h>          // printf()
#include <stdlib.h>         // atoi()
#include <ctype.h>          // isdigit()

int main(int argc, char *argv[])   
{ 
    if( argc != 2 ) {
        return 0;
    }

    char * pWord = argv[ 1 ];
    char c = 0;
    for( int i = 0; c = pWord[ i ], c ; ++i ) {
        if( ! isdigit( c ) ) {
            return 0;
        }
    }

    int argvNum = atoi( argv[ 1 ] );
    printf( "argc = %d, argv[ 1 ] = %s, argvNum = %d\n",
        argc, argv[ 1 ], argvNum );
}
person Arun    schedule 22.04.2013

Я новичок в C++, поэтому, пожалуйста, не ругайте меня, если это неправильно, но не могли бы вы создать исключение и позволить пользователю повторно исправить ввод?

Я узнал несколько способов борьбы с ошибками:

  1. Если/иначе обработка
  2. Утверждать
  3. Выбросить исключение

1.ЕСЛИ/ИНАЧЕ #include

int main(int argc, int **argv) {
    if (!isdigit(argv[1])) {
        // handle code if it's not a digit.
        return 0;
    }
}

Это, наверное, самый простой способ убедиться


2.УТВЕРДИТЬ #include

int main(int argc, int *argv[]) {
    assert(isdigit(argv[1]));
}

* Assert завершит программу, если argv[1] не является цифрой

3. БРОСЬТЕ #include

using namespace std;

class Except {};

int main(int argc, int **argv) {
    try {
        isdigit(argv[1]);
        throw Except();
        // this code will not be executed
        // if argv[1] is not a digit
    }
    catch (Except) {
        cout << "argv[1] is not a digit.";
        // handle exception or rethrow
    } 
}

Безусловно, стоит отметить, что генерация исключения создаст трассировку стека, а также весь код между генерируемым исключением и блоком, перехватывающим исключение, НЕ будет выполняться.

person Charles Addis    schedule 23.04.2013
comment
isdigit работает на уровне символа, а не на уровне массива символов. Итак, isdigit(argv[1]) не делает того, что вы думаете, даже если он компилируется. - person hookenz; 23.04.2013