I am using strtod() to convert string to decimal. Since I need to throw an error for incorrect input/invalid characters in i have no other choice.
However the problem is that strtod() is affected by locales. So a '.' becomes an invalid character when the program is run in a different locale
If I use a solution like this:
std::istringstream text(iterator->second.c_str());
text.imbue(std::locale::classic());
double result;
text >> result;
it is not possible to check for invalid input at all.
Which solution can provide me both?
Yes you can determine if the value provided to the stringstream was completely converted or not. You can test the stream to see if it reached the end of file. If it has then you have read everything and converted it to a double. If not then there was invalid input.
Here is a little sample demonstrating the conversion:
bool convert(const std::string & text)
{
if (text = "")
return false;
std::istringstream iss(text);
iss.imbue(std::locale::classic());
double result;
iss >> result;
if (iss.eof())
return true;
else
return false;
}
int main()
{
std::cout << convert("123.456") << std::endl;
std::cout << convert("123.456abc") << std::endl;
std::cout << convert("123.456e5") << std::endl;
std::cout << convert("e5") << std::endl;
return 0;
}
Output:
1
0
1
0
Live Example
You can check for invalid input by testing text for errors and that it reached the end of input:
text >> result;
bool ok = text && text.eof();
Example: http://coliru.stacked-crooked.com/a/758b769f26567d1e
Related
Under windows10 and VS2017:
I was trying to read a double number 1.1 from keyboard using istream and put it into a int type variable, say temp. In reason temp is 1 but the istream seems to be stuck in some error status. In expectancy istream should stop and wait for keyboard input but it continues another round read-from-buffer and error occurs this time.
I had checked the rdstate() and it was equal to 2 after the 2nd round read-from-buffer. I know it was abnormal but why?
To replicate, run the code, type 1.1 in console and hit enter, the error will show up.
Actually, I used int32 to try to store double for some reasons. The program is supposed to print valid input from keyboard. Here valid refers to that the input should not exceed the range of int32 or be double/readable character. Otherwise the program should print Invalid input on the screen.
#include <iostream>
std::istream& f(std::istream &in) {
int temp = 0;
while(true) {
while (in >> temp) {
if (temp == -1) {
break;
}
std::cout << temp << std::endl;
}
if (in.eof()|| temp == -1) break;
if (!in) {
std::cout << "Invalid input" << std::endl;
in.clear();
in.ignore(10000,32);
}
}
in.seekg(0, std::ios::beg);
return in;
}
int main(){
std::cout << "Please input some integers and end with ^Z or -1" << std::endl;
f(std::cin);
return 0;
}
Keep in mind that when you read 1.1 from the keyboard you're reading text. The program looks at that text and decides what value it represents, depending on the type of the variable that you're reading into. If you're reading into an int, the input routine reads the first '1', then sees '.', which can't be part of the text representation of an int, and it stops reading. Your variable gets the value 1. If you try to read another int from the same input stream, that '.' will stop the read immediately, since it can't be part of an int, and the attempted input fails.
Short answer: don't do that. If your input text looks like floating-point, read it as floating-point.
Try this:
#include <iostream>
std::istream& f(std::istream &in) {
std::string temp = "";
while(true) {
while (in >> temp) {
if (temp == "-1") {
break;
}
std::cout << temp << std::endl;
}
if (in.eof()|| temp == "-1") break;
if (!in) {
std::cout << "Invalid input" << std::endl;
in.clear();
in.ignore(10000,32);
}
}
in.seekg(0, std::ios::beg);
return in;
}
int main(){
std::cout << "Please input some integers and end with ^Z or -1" << std::endl;
f(std::cin);
return 0;
}
You are parsing character by character from the buffer. You cannot put a character into an integer. You are assuming you are reading 1.1 from the stream, but you're instead reading 1,.,1 from the buffer, and the . is throwing the error. The above portion works as you are reading characters and keeping them in a string.
I want this code to check whether the input is an int or not, and it works fine until I enter a float type number. I need a program that won't let through float numbers.
bool error;
int x;
string s;
do
{
cout <<"number: ";
cin >> x;
error=cin.fail();
if(error)
{
cout << "error" <<endl;
cin.clear();
}
getline(cin,s);
}while(error);
cout << x;
Read in all of the user's input line as a string, then convert the string to an integer with std::stoi.
Unfortunately std::stoi will happily stop converting when it hits the end of convertible characters, but it allows you to pass in a pointer to a position to be updated with the character that ended the conversion. If this position is not the end of the string, there was garbage on the line.
bool error = true; // assume user input is wrong
while (error)
{
if (std::getline(std::cin, s)) // grab the whole line
{
std::size_t end;
try
{
x = std::stoi(s, &end);
if (end == s.length()) // converted all user input
{
error == false; // good data
}
}
catch(std::invalid_argument &) // user input is complete garbage
{
}
catch(std::std::out_of_range &) // converted user input is too big for int.
{
}
}
}
^
I recommend turning the input loop into a function. 1) It's easily reusable should you need to convert an int again. 2) It gets rid of some of the logic above because you can return when the input is tested and good.
int gimmieInt(std::istream& in) // Me eat input stream! Om nom nom nom!
{
std::string s;
int x;
while (true) // consider instead using a maximum number of retries.
// This increases complexity, so consider it *after* you have
// the basic version working
{
if (std::getline(in, s))
{
std::size_t end;
try
{
x = std::stoi(s, &end);
if (end == s.length())
{
return x;
}
}
catch(std::invalid_argument &) // user input is complete garbage
{
}
catch(std::std::out_of_range &) // user input is too big for int.
{
}
}
}
}
std::istream& operator(std::istream&, int) will read a valid integer number up to any non matching character like '.', and no error state is set for the stream up to this point.
You better should read complete (whitespace separeated) chunks as std::string, and inspect them if these contain the desired format (e.g. using std::regex).
std::stoi() should also fail with an exception, if you're trying to convert the chunk.
I think, you are looking for something like this (C++11):
auto s = std::string{};
std::cin >> s;
if( std::all_of(std::begin(s), std::end(s),
[](char c) -> bool {
return c <= '0' && c <= '9';
}) ) {
std::cout << "you have entered an integer" << std::endl;
}
Somehow I thought, that the standard library contains a predicate that checks if a given char is a digit, but I could not find it now. Such a hypothetic is_digit() would allow make the code more readable:
if( std::all_of(std::begin(s), std::end(s), std::hypothetic::is_digit) ) {
std::cout << "you have entered an integer" << std::endl;
}
I wanted to know if there is any efficient method of getting a string as input and then performing some operation on its characters individually?
Also, after performing operations (check that length of string may also increase or decrease), can we output the new string (string got after performing operations) instead of outputting the characters individually using a for loop?
Note that time is a crucial factor, please provide fastest methods.
Is there is any efficient method of getting a string as input and then performing some operation on its characters individually?
Yes, there is: read std::string as usual (say, with std::getline or the >> operator of an input stream), and then access the individual characters in a loop.
std::string str;
std::getline(std::cin, str);
for (int i = 0 ; i != str.size() ; i++) {
std::cout << "Code of character " << i << " is " << (int)str[i] << std::endl;
}
First demo on ideone.
Also, after performing operations, can we output the new string (string got after performing operations) instead of outputting the characters individually using a for loop?
Yes, you can: std::string is mutable, meaning that you can change it in place.
std::string str;
std::getline(std::cin, str);
for (int i = 0 ; i != str.size() ; i++) {
if (!std::isalpha(str[i])) {
str[i] = '#';
}
}
std::cout << str << std::endl;
Second demo on ideone.
You can do it like this:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string in;
cout << "Input please\n";
cin >> in;
if(in.size() >= 5)
in[5] = 'A';
cout << in << "\n";
return 0;
}
Or you can use std::getline(), instead of std::cin.
Output:
Input please
samaras
samarAs
However, are you sure this is the bottleneck of your program? You can check this with some profiling tools, like the one I use.
[EDIT]
Since OP is asking about efficiency, I did some testing. However, you can to take into account the time that user takes to type the input, but since I am the same person, we can assume this is constant.
So, I did modified a bit a code from another answer, like this:
std::string str;
cout << "Input please\n";
std::getline(std::cin, str);
if (str.size() >= 5) {
str[5] = '#';
}
std::cout << str << "\n";
Output:
Input please
Samaras
Samar#s
It took me 1.04237 seconds.
And with my code, I got
Input please
Samaras
SamarAs
It took me 0.911217 seconds.
Which actually show that they are pretty close and I would say the difference is due to my typing speed.
I did the timings with std::chrono, like the code I have in my pseudo-site.
Basic operation... Some search on the internet could have helped you but here you go...
std::string processStr(const std::string &str)
{
for (std::string::iterator it = str.begin(); it != str.end(); ++it)
// process your string (getting a char is done by dereferencing the iterator
// like this: *it
return (str);
}
I need to convert a wide string to double number. Presumably, the string is holding a number and nothing else (maybe some whitespaces). If the string contains anything else, an error should be indicated. So I can't use stringstream - it will extract a number without indicating an error if the string contained something else.
wcstod seems like a perfect solution, but it works wrong on Android (GCC 4.8, NDK r9). What other options can I try?
You can use stringstream, then use std:ws to check that any remaining characters on the stream are only whitespace:
double parseNum (const std::wstring& s)
{
std::wistringstream iss(s);
double parsed;
if ( !(iss >> parsed) )
{
// couldn't parse a double
return 0;
}
if ( !(iss >> std::ws && iss.eof()) )
{
// something after the double that wasn't whitespace
return 0;
}
return parsed;
}
int main()
{
std::cout << parseNum(L" 123 \n ") << '\n';
std::cout << parseNum(L" 123 asd \n ") << '\n';
}
prints
$ ./a.out
123
0
(I've just returned 0 in the error case as something quick and easy for my example. You probably want to throw or something).
There are of course other options too. I just felt your assessment was unfair on stringstream. By the way, this is one of the few cases where you actually do want to check eof().
Edit: Ok, I added the ws and Ls to use wchar_ts.
Edit: Here's what the second if conceptually looks like expanded out. May help to understand why it is correct.
if ( iss >> std::ws )
{ // successfully read some (possibly none) whitespace
if ( iss.eof() )
{ // and hit the end of the stream, so we know there was no garbage
return parsed;
}
else
{ // something after the double that wasn't whitespace
return 0;
}
}
else
{ // something went wrong trying to read whitespace
return 0;
}
I am making a statistics collector that reads the log of a music player and lets the user show top ten most played etc. As a noob project.
A line from the log looks like: "20:42:03 start E:\ROTATION\A\HÃ¥kan Lidbo - Dammlunga.mp3"
I have put this in a string using ifstream and getline.
Then making an array of chars of the string using
const char *charveqtur = newline.c_str();
Then I tried to sort i out with sscanf:
sscanf (charveqtur, "%d:%d:%d\tstart\t%s", &this->hour, &this->minute, &this->second, &this->filename);
The problem is that the filename is cut at the first space. I have also tried using istringstream instead but no breakthrough so far.
Which is the most convinient way of doing this? Thanks.
You can use some input stream to read the first integers and colons, and because the filename is the last entity, you can then use std::getline. However, even if your filename is not the last part, note that std::getline is quite a versatile function that accepts any delimiter.
A more advanced method would be to define your own type for filenames and overload operator>>(std::istream &, T const &) on it.
Here is a complete example using std::getline and stringstream with basic diagnostics and some reformatting:
#include <sstream> // for istringstream
#include <iostream> // for cout and cerr
#include <iomanip> // for setprecision
#include <cmath>
bool read (std::string const &line) {
char c = 0;
double length;
double rating;
std::string title;
std::istringstream ss;
ss.str (line);
ss >> length;
if (!ss.good()) { std::cerr << "invalid length\n"; return false; }
if (ss.get()!=':') { std::cerr << "expected colon\n"; return false; }
ss >> rating;
if (!ss.good()) { std::cerr << "invalid rating\n"; return false; }
if (ss.get()!=':') { std::cerr << "expected colon\n"; return false; }
std::getline (ss, title);
double sink;
std::cout << title << " ("
<< int(length) << ':' << 60*std::modf (length,&sink)
<< " min), your rating: " << rating << '\n';
return true;
}
int main () {
read ("30.25:5:Vivaldi - The four seasons.ogg");
read ("3.5:5:Cannibal Corpse - Evisceration Plague.ogg");
read ("meh");
return 0;
}
Output:
Vivaldi - The four seasons.ogg (30:15 min), your rating: 5
Cannibal Corpse - Evisceration Plague.ogg (3:30 min), your rating: 5
invalid length
Important: When parsing, you are sailing close to the security risks. Always be conscious and sensible and try to use tested and proven libraries where possible. This also implies that you do not use sscanf, which is not typesafe, error-prone and sometimes hard to get right.
Don't use C if you have C++, and used correctly, iostreams are even more convenient than printf/scanf+co.
You could perhaps do something like
int lastpos = 0;
if sscanf (charveqtur, "%d:%d:%d\tstart\t%n", &this->hour,
&this->minute, &this->second,
&lastpos) > 3 && lastpos >0) {
std::string filename = newline.substr(lastpos);
/* do something with filename */
}