Принуждение String к функции int для использования всей строки

Учитывая строку, которая должна представлять число, я хотел бы поместить ее в функцию преобразования, которая будет уведомлять, если вся строка не была преобразована.

Для ввода: "12":

  • istringstream::operator>> выходов 12
  • atoi выходов 12
  • stoi выходов 12

Для ввода "1X" я бы хотел получить ответ об ошибке, но получаю:

  • istringstream::operator>> выходов 1
  • atoi выходы 1
  • stoi выходов 1

Для ввода "X2":

  • istringstream::operator>> выводит 0 и устанавливает флаг ошибки
  • atoi выходов 0
  • stoi выдает ошибку

[Живой пример]

Есть ли способ спровоцировать ошибочное поведение на входе "1X"?


person Jonathan Mee    schedule 07.10.2015    source источник
comment
Думаю, у вас есть ответы на SO: stackoverflow.com/questions/2844817/ и stackoverflow.com/questions/1243428/   -  person masoud    schedule 07.10.2015
comment
@deepmax Да, для целей минимального примера я не включил его, но в моем локальном тестовом коде я делаю это в верхней части цикла: cout << "\tinput string: " << i << (all_of(i, i + strlen(i), bind(isdigit, placeholders::_1)) ? " is good\n" : " is bad\n"); Однако, хотя я могу проверять так я не хочу. Все остальные функции также должны проходить через каждого персонажа, и я хотел бы иметь способ использовать то, что они уже знают.   -  person Jonathan Mee    schedule 07.10.2015
comment
@deepmax Вы ошибаетесь, это не дубликат, в этих ответах не адрес, подтверждающий, что вся строка была прочитана.   -  person Jonathan Mee    schedule 07.10.2015
comment
Я согласен, что это не дубликат. Он задает другой вопрос, на который нет ответа в связанных ответах. @deepmax вы должны снять отметку.   -  person Klitos Kyriacou    schedule 07.10.2015
comment
В любом случае, вот ответ: int pos; int n = stoi (моя строка, & pos); if (pos! = mystring.length ()) это не числовое значение.   -  person Klitos Kyriacou    schedule 07.10.2015
comment
Я повторно открыл вопрос, но я считаю, что это обман, поскольку они отвечают на вопрос прямо по существу.   -  person masoud    schedule 07.10.2015
comment
@KlitosKyriacou Это именно то, что мне было нужно, можете ли вы сформулировать это в качестве ответа?   -  person Jonathan Mee    schedule 07.10.2015
comment
@JonathanMee: Комментарий, который предоставил KlitosKyriacou, находится в ответах на ссылки, которые я опубликовал (и более подробно). Прочтите этот ответ: stackoverflow.com/a/1243531/952747   -  person masoud    schedule 07.10.2015
comment
Вы правы, deepmax, хотя в этих ответах используется strtol вместо stoi (это то, что я искал). В некоторых библиотеках stoi реализована в терминах strtol, поэтому эти ответы можно адаптировать.   -  person Klitos Kyriacou    schedule 07.10.2015
comment
@deepmax Ни один из ваших связанных ответов даже не содержит текста stoi. Мне удалось найти решение, использующее strtol, которое также является жизнеспособным. Однако тот факт, что ответ, отвечающий на мой вопрос, предоставлен по отдельному вопросу, не делает его дубликатом. Я искал на stackoverflow.com вопрос о преобразовании всей строки в число, но не нашел ни одного . Этот вопрос избавляет следующего человека от необходимости повторять его.   -  person Jonathan Mee    schedule 07.10.2015
comment
@KlitosKyriacou Я пошел дальше и добавил ответ, чтобы вопрос не был повторно закрыт, поскольку я не думаю, что там есть хорошо задокументированный ответ на этот вопрос. Я использовал stoi, как вы предложили, и поэтому, если вы хотите набрать ответ, я приму ваш, поскольку вы сначала были справа от него.   -  person Jonathan Mee    schedule 07.10.2015
comment
Ничего страшного, Джонатан, твой собственный ответ намного более подробен и полезен, чем все, что я написал бы.   -  person Klitos Kyriacou    schedule 07.10.2015
comment
@deepmax Спасибо за отмену закрытия. Я считаю, что информация, представленная в этом вопросе и ответе, будет очень полезна всем, кто страдает от проблемы, аналогичной моей.   -  person Jonathan Mee    schedule 07.10.2015


Ответы (2)


Изменить: в c ++ 17 или новее from_chars предпочтительнее. Подробнее см. Здесь: https://topanswers.xyz/cplusplus?q=724#a839


Для данного string str есть несколько способов добиться этого, каждый со своими преимуществами и недостатками. Я написал здесь живой пример: https://ideone.com/LO2Qnq и обсудите каждый ниже:

strtol

Как было предложено, здесь strtol out-параметр может использоваться для получения количества прочитанных символов. strtol фактически возвращает long, а не int, поэтому при возврате выполняется приведение.

char* size;
const int num = strtol(str.c_str(), &size, 10);

if(distance(str.c_str(), const_cast<const char*>(size)) == str.size()) {
    cout << "strtol: " << num << endl;
} else {
    cout << "strtol: error\n";
}

Обратите внимание, что здесь используется str.c_str() для ссылки на ту же строку. c_str Возвращает указатель на базовый массив, служащий хранилищем символов, а не временным если у вас C ++ 11:

c_str() и data() выполняют одну и ту же функцию

Также обратите внимание, что указатель, возвращаемый c_str, будет действителен между вызовами strtol и distance, если:

  • Передача не const ссылки на string любой стандартной библиотечной функции
  • Вызов функций-членов, отличных от const, на string, за исключением operator[], at(), front(), back(), begin(), rbegin(), end() и rend()

Если вы нарушите любой из этих случаев, вам нужно будет сделать временную копию i, лежащего в основе const char*, и выполнить на ней проверку.

sscanf

sscanf может использовать %zn для возврата количества прочитанных символов, что может быть более интуитивно понятным, чем сравнение указателей. Если база важна, sscanf может быть не лучшим выбором. В отличие от strtol и stoi, которые поддерживают базы 2–36, sscanf предоставляет спецификаторы только для восьмеричного (%o), десятичного (%d) и шестнадцатеричного (%x).

size_t size;
int num;

if(sscanf(str.c_str(), "%d%zn", &num, &size) == 1 && size == str.size()) {
    cout << "sscanf: " << num << endl;
} else {
    cout << "sscanf: error\n";
}

stoi

Как было предложено здесь stoi's выходной параметр работает как sscanf %n, возвращая количество прочитанных символов. В соответствии с C ++ для этого требуется string, и в отличие от описанных выше реализаций C stoi выдает _47 _ если первый непробельный символ не считается цифрой для текущей базы, и это, к сожалению, означает, что, в отличие от реализаций C, он должен проверять наличие ошибки как в блоках try, так и catch.

try {
    size_t size;
    const auto num = stoi(str, &size);

    if(size == str.size()) {
        cout << "stoi: " << num << endl;
    } else {
        throw invalid_argument("invalid stoi argument");
    }
} catch(const invalid_argument& /*e*/) {
    cout << "stoi: error\n";
}
person Jonathan Mee    schedule 07.10.2015

В качестве альтернативы вы можете использовать std::istringstream, как вы упомянули, но убедитесь, что он проанализирован до конца потока. Предполагая, что у вас есть постоянная ссылка, вы можете сделать что-то вроде следующего

T parse(const std::string& input) {
    std::istringstream iss(input);
        T result;
        iss >> result;
        if (iss.eof() || iss.tellg() == int(input.size())) {
            return result;
        } else {
            throw std::invalid_argument("Couldn't parse entire string");
    }
}

Преимущество этого подхода в том, что вы анализируете все, что перегружает operator>>. Примечание: я не совсем уверен, достаточно ли условия, но с моим тестированием, похоже, хватило. По какой-то причине поток получал бы отметку об ошибке, если бы он был проанализирован до конца.

person Erik    schedule 31.01.2016
comment
Я поставил вам +1, потому что это, возможно, очень полезный способ выполнить преобразование для тех, кому нужно продолжить работу с istringstream и может включить ваше решение, однако в общем случае я считаю, что это одно из самых легких преобразований в следует использовать мой ответ. - person Jonathan Mee; 01.02.2016