C++非C++11环境下字符串转数值:如何检测无符号数值的负数输入?
Hey there! Let's break down your two C++ questions and solve them step by step, especially focusing on that legacy C++03 code requirement.
1. Detecting negative values when converting std::istream to unsigned numeric types
When reading into an unsigned type using std::istream (like unsigned int), the stream won't set the failbit if you input a negative number—instead, it will silently convert the negative value to the equivalent unsigned value via modulo arithmetic (e.g., -1 becomes UINT_MAX). To catch this, you need to explicitly check for a leading minus sign before attempting to read the value.
Here's a reusable function that handles this properly, plus extra checks for invalid trailing characters:
#include <iostream> #include <sstream> #include <limits> #include <cctype> bool readUnsigned(std::istream& is, unsigned int& val) { // Skip leading whitespace first char c; while (is.get(c) && std::isspace(static_cast<unsigned char>(c))) { // Continue skipping spaces } // Check if we encountered a minus sign if (c == '-') { is.setstate(std::ios::failbit); return false; } // Put the non-whitespace, non-minus character back into the stream is.putback(c); // Attempt to read the unsigned value is >> val; // Check for read failures AND any remaining non-whitespace characters if (is.fail() || is.bad()) { return false; } // Verify no invalid trailing characters are left while (is.get(c)) { if (!std::isspace(static_cast<unsigned char>(c))) { is.setstate(std::ios::failbit); return false; } } return true; }
How this works:
- We first skip any leading whitespace to handle inputs like
" -123". - If the first non-whitespace character is
-, we immediately mark the stream as failed and return false. - After reading the value, we check for any leftover non-whitespace characters (like
"123abc") to avoid invalid conversions.
2. C++03-compatible template function for string-to-numeric conversion (with unsigned negative detection)
Your existing template has gaps: it doesn't catch negative inputs for unsigned types, and it doesn't validate trailing characters. Let's fix that while keeping it compatible with C03 (no C11 features allowed).
Here's the refined template:
#include <iostream> #include <sstream> #include <string> #include <limits> #include <cctype> template<typename T> void checkValid(const std::string& val) { std::stringstream str1(val); T temp1; bool isValid = true; // Handle unsigned types: check for leading minus sign if (!std::numeric_limits<T>::is_signed) { char c; // Skip leading whitespace while (str1.get(c) && std::isspace(static_cast<unsigned char>(c))) { // Do nothing, skip spaces } if (c == '-') { std::cout << "error, " << val << " is not a valid string value (negative input for unsigned type)" << std::endl; return; } // Put the valid character back into the stream for reading str1.putback(c); } // Attempt to read the numeric value str1 >> temp1; // Check for stream failure (e.g., non-numeric input) if (str1.fail() || str1.bad()) { isValid = false; } else { // Check for any remaining non-whitespace characters char leftover; while (str1.get(leftover)) { if (!std::isspace(static_cast<unsigned char>(leftover))) { isValid = false; break; } } } // Output result if (!isValid) { std::cout << "error, " << val << " is not a valid string value" << std::endl; } else { std::cout << "ok, " << val << " is converted to " << temp1 << std::endl; } }
Key improvements:
- Uses
std::numeric_limits<T>::is_signed(available in C++03) to distinguish signed/unsigned types and add negative checks for unsigned ones. - Validates trailing characters to reject inputs like
"123xyz"or"45.67"when converting to integer types. - Provides clear error messages, including a specific one for negative inputs to unsigned types.
Test it out:
int main() { checkValid<unsigned int>("123"); // ok, 123 is converted to 123 checkValid<unsigned int>("-456"); // error, -456 is not a valid string value (negative input for unsigned type) checkValid<int>("-789"); // ok, -789 is converted to -789 checkValid<unsigned int>("123abc"); // error, 123abc is not a valid string value checkValid<float>(" 3.14 "); // ok, 3.14 is converted to 3.14 return 0; }
内容的提问来源于stack exchange,提问作者jpo38

