Programs often require users to input data of various types. If a user enters the wrong data type or the correct type in the wrong format, the program can behave strangely or fail (e.g., loop infinitely or crash). How a program responds when the end-user enters invalid data depends on the type and format of data the program expects versus what the user enters, the programming language, and the host system (hardware and operating system). The following program illustrates one of these variations.
#include <iostream>
using namespace std;
int main()
{
int input;
cout << "Please enter an integer: ";
cin >> input;
cout << input;
return 0;
}
D:\>bulletproof
Please enter an integer: hello
4194048
D:\>
D:\>bulletproof
Please enter an integer: hello
2096640
D:\>
(a)
(b)
A data input error.
The ANSI standard requires that correct programs must always behave in the same, consistent way, but the standard does not specify the behavior of incorrect programs. Incorrect programs may crash or produce incorrect or inconsistent results.
A simple test program named bulletproof.cpp that expects the user to enter an integer.
Each time the program runs, a string is entered rather than an integer. Both examples enter the same string, but the program prints a different value: the address of the string in memory.
Bulletproof Code
bulletproof code, strings as a universal type, getline (string), getline (C-string), isdigit, regular expressions
Bulletproof code is code that catches or detects input errors, reports the error to the user, and "gracefully" deals with the error. Gracefully handling an error typically means that, after detecting the error and printing a diagnostic (i.e., an error message), the program either terminates or allows the user to re-enter the data. Detecting the error is usually the hardest part of this process, so we approach it by breaking it into three sub-problems.
Data input. Programs can represent all data entered through the console as a string, making it a "universal" data type.
Data validation. Programs can examine each string character, ensuring that, taken together, the characters form the expected data type. If the type is incorrect, programs report the error, and either repeat the input or terminate.
Data conversion. If the characters match the expected type, programs convert them into the expected data type.
Bulletproof data entry.
A string is just a sequence of characters whose meaning depends on what a program does with it. For example, the string "123" looks like an integer, but the string "hello" does not. Similarly, "July 4, 1779" is a valid date. However, the program can still fail if it "expects" the date in a different format, for example, "07/04/1778." Detecting the different date formats is challenging, so this chapter focuses on numerical data.
string class version
C-string version
string input;
getline(cin, input);
for (size_t i = 0; i < input.length(); i++)
if (!isdigit(input[i]))
{
cerr << "Invalid integer: " << input << endl;
exit(1);
}
char input[100];
cin.getline(input, 100);
for (size_t i = 0; i < strlen(input); i++)
if (!isdigit(input[i]))
{
cerr << "Invalid integer: " << input << endl;
exit(1);
}
Validating integer input.
The examples demonstrate the first two bulletproof data entry steps. isdigit, one of the cctype library functions, returns true whenever its argument is a decimal digit (i.e., '0' to '9'). For simplicity, the programs ignore a '-' sign, indicating a negative number, but detect and report any character that is not a digit. Using a loop to examine the characters is limited and cumbersome. Regular expressions (RE) are more flexible and elegant. The text introduces them in Intro To Regular Expressions in the last chapter.
If the string only contains digits, the program can safely convert it to an integer. Validating a correct floating-point number (either a float or a double) is more difficult because it may be negative, have a decimal point, or have an exponent. Nevertheless, converting a string to a floating-point number is almost as easy as converting a string to an integer.
Converting Strings To Numbers
converting strings to numbers, stoi, atoi
Programs can cast one numeric type to another. However, casting is possible only when the source and destination data types are "close." For example, an integer and a double are "close" in the sense that they are both numeric types. C-strings and string objects can represent various kinds of data, most of which are not numeric. Consequently, strings and numbers are not "close" enough to permit casting from one type to the other. Nevertheless, we know from the solutions developed for the palindrome-number problem (cpalnumber.cpp and palnumber.cpp) that is is possible to convert an integer into a string. Converting a correctly formed string into a number is equally as easy.
string Class
C-String
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main()
{
string input;
cout << "Please enter an integer: ";
getline(cin, input); // 1
for (size_t i = 0; i < input.length(); i++)
if (!isdigit(input[i])) // 2
{
cerr << "Invalid integer: " << input << endl;
exit(1);
}
cout << stoi(input) << endl; // 3
return 0;
}
#include <iostream>
#include <cstring>
#include <cctype>
using namespace std;
int main()
{
char input[100];
cout << "Please enter an integer: ";
cin.getline(input, 100); // 1
for (size_t i = 0; i < strlen(input); i++)
if (!isdigit(input[i])) // 2
{
cerr << "Invalid integer: " << input << endl;
exit(1);
}
cout << atoi(input) << endl; // 3
return 0;
}
(a)
(b)
D:\>bulletproof
Please enter an integer: 123
123
D:\>bulletproof
Please enter an integer: hello
Invalid integer: hello
(c)
(d)
Bulletproof input: Converting a string to an integer.
The examples demonstrate the three steps of bulletproof data entry: read user input as a string, validate its characters, and either convert a valid string to a number or report an error. The behavior of each version is independent of the string implementation.
A string class example
A C-string version
The program converts valid input to an integer
The program displays an error message when the user enters invalid data
stoi and atoi are short for string to integer and ASCII to integer, respectively. The following section describes these functions.
string Conversion Functions
converting string objects to numbers, stoi, stod
string to int
int stoi(const string& s, size_t* index = nullptr, int base = 10);
string to double
double stod(const string& s, size_t* index = nullptr);
string conversion functions.
C++ documentation typically provides information about how to use a function in the form of a function prototype. The prototypes for the string conversion functions stoi (string to integer) and stod (string to double) illustrate the data a client must provide to the functions and the value they return. Programs can call the the string class conversion functions in various ways, depending on which default arguments they accept and which they override. As illustrated, the functions only require the first argument - the string they convert - with the rest having default values.
s is the string object the function converts to a number. The program passes it by reference for efficiency.
index is a pointer to an int (i.e., it is an argument passed-by-pointer). The conversion functions save the index location of the first unconvertible character (i.e., where the conversion ended) in the integer. The following figure illustrates the feature's syntax and behavior. Setting index to nullptr disables the feature.
base is the base of the digit-characters in the string, for example, 2 (binary), 8 (octal), decimal (10), or 16 (hexadecimal) - other bases also work but may not be named. The default assumes base 10 (decimal).
Both functions return the numeric value represented as a string by their first argument.
string to int
string to double
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s = "1234Hello World";
size_t index = 0;
cout << stoi(s) << endl; // a
cout << stoi(s, nullptr) << endl; // b
cout << stoi(s, &index) << endl; // c
cout << index << endl; // d
return 0;
}
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s = "3.14Hello World";
size_t index = 0;
cout << stod(s) << endl; // a
cout << stod(s, nullptr) << endl; // b
cout << stod(s, &index) << endl; // c
cout << index << endl; // d
return 0;
}
Output
Output
1235
1235
1235
4
3.14
3.14
3.14
4
Advanced string conversion function examples.
Default arguments allow function programmers to create flexible functions that perform frequent, basic operations with minimal client input (i.e., with few arguments), while still allowing increased control when necessary. Clients may call both functions with a single string argument. For more complex processing or enhanced error handling, they can also pass the address of an integer index (i.e., a pointer to an integer). Pass-by-pointer is an INOUT argument-passing mechanism that allows a function to store the index of the first character that it could not convert to a number.
Simple call; defaults used for the second and (for stoi) third arguments.
Emphasizes, for illustration, that the second argument is a pointer. Programmers typically accept the default value rather than explicitly passing nullptr.
Passes a pointer to index. The function operates on the string from left to right, saving the position in the string of the first character it could not convert to a number in index.
string objects are zero-indexed, so the initial digit is at index location 0. In both examples, "H" is at index location 4 in the string. stoi converts "1234" to an integer, stopping at 'H' and storing its location, 4, in index. Similarly, stod converts "3.14" to a double and stores 4, the location of 'H,' in index.
C-String Conversion Functions
converting C-strings to numbers, atoi, strtol, atof, strtod
C-string to int & long
int atoi(const char* s); long strtol(const char* s, char** end, int base);
C-string conversion functions.
The figure illustrates prototypes for four representative C-string conversion functions. C++ inherits these functions from C, which has neither overloaded functions nor default arguments. Consequently, this API consists of numerous functions. Please see the C-string conversion functions for a complete list. The second argument of the multi-argument functions is a double character-pointer (i.e., a pointer to a C-string). Like the string function integer pointers demonstrated above, the functions use the double-character pointers to provide complex processing and error handling.
s is the C-string that the function converts to a number.
end is a pointer to a C-string, which is already a character pointer (thus the two asterisks). After the function call, end points to the first character in the original string that the function could not convert to a number - i.e., the beginning of the unconverted substring. Passing nullptr as the second argument switches off this behavior.
base is the base of the digit-characters in the string, for example, 2 (binary), 8 (octal), decimal (10), or 16 (hexadecimal) - other bases also work but may not be named.
int / long
double
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char* input = "1234Hello World";
cout << atoi(input) << endl; // a
char* end;
cout << strtol(input, nullptr, 10) << endl; // b
cout << strtol(input, &end, 10) << endl; // c
cout << end << endl; // d
return 0;
}
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char* input = "3.14Hello World";
cout << atof(input) << endl; // a
char* end;
cout << strtod(input, nullptr) << endl; // b
cout << strtod(input, &end) << endl; // c
cout << end << endl; // d
return 0;
}
Output
Output
1234
1234
1234
Hello World
3.14
3.14
3.14
Hello World
C-string conversion function examples.
A simple function call.
The function requires three parameters; if you wish to "ignore" the second parameter, you must explicitly pass a null pointer as the parameter.
Passes a C-string as the second parameter; passing nullptr disables the error handling behavior. When enabled, end points to the first character in the substring that the function could not convert to a number.
After the function call, end points to the C-string "Hello World"