I have a code that I'm trying to learn how to parse in C++. I understood everything I did, but I don't understand how to work with the likes of atoi(), atof (), strtod (). I know what it's supposed to do, but I don't understand why the compiler doesn't like it. My focus on the error is "scores[line_count] = strtod (score);"
#include <iostream>
#include <fstream>
#include <string>
#include <stdlib.h>
#include <iomanip>
using namespace std;
int readScores(string inputFile, string name[], float scores[], int array_size)
{
//delcare variables
ifstream infile;
int line_count = 0;
string line;
string named;
float score;
char character;
int word_index;
string names[array_size];
// open input file
infile.open(inputFile);
//Check if file opens succesfully.
if(infile.fail())
{
cout << "File cannot open!!" << endl;
return -1;
}
while(getline(infile, line))
{
cout << line << endl;
// PARSING GOES HERE
word_index = 0;
for(int i=0; i < (int)line.length(); i++)
{
character = line[i];
if (character == ',')
{
names[line_count] = named;
named = "";
word_index++;
}
else
{
if(word_index == 0)
{
named += character;
}
else if (word_index == 1)
{
score += character;
cout << character << " " << endl;
}
}
}
scores[line_count] = strtod (score);
line_count++;
}
//close file
infile.close();
//return line count
return line_count;
cout << line_count << endl;
}
int main(void)
{
int array_size = 50;
string inputFile = "Test.txt";
string name [array_size];
float scores [array_size];
readScores(inputFile, name, scores, array_size);
}
The function strtod() takes the form
double strtod (const char* str, char** endptr);
But you only give it the string.
As you can see it takes two parameters, the string you wish to convert to a double, and an "endptr". The endptr is described here as a
Reference to an already allocated object of type char*, whose value is set by > the function to the next character in str after the numerical value.
This parameter can also be a null pointer, in which case it is not used.
So you need to declare a char pointer to save the next character after the decimal, even if there wont be one. This allows you to pull multiple doubles from a single string, much like a tokenizer.
char * endPtr;
scores[line_count] = strtod(score, &endPtr);
Edit
As Alex Lop pointed out, you aren't even passing a string to strtod, you're passing a float. It appears you would like to cast the float to a double?
Of course the compiler doesn't like it. Please read the description of strtod.
double strtod (const char* str, char** endptr);
Convert string to
double.
Parses the C-string str interpreting its content as a floating
point number (according to the current locale) and returns its value
as a double. If endptr is not a null pointer, the function also sets
the value of endptr to point to the first character after the number.
The function first discards as many whitespace characters (as in
isspace) as necessary until the first non-whitespace character is
found. Then, starting from this character, takes as many characters as
possible that are valid following a syntax resembling that of floating
point literals (see below), and interprets them as a numerical value.
A pointer to the rest of the string after the last valid character is
stored in the object pointed by endptr.
And in your code you pass to strtod only one parameter which is of type float and store the returned result of double into an array of floats. If you want to move the value of float from one variable to another, you don't need any "convertion" function:
scores[line_count] = score;
NOTE: I didn't really review your code as you asked specifically about scores[line_count] = strtod (score);. But after I looked how you modify score, maybe it should have been string and not float. If so, then it is the another point to fix.
Related
I want to read an input string and connect their values to variables in my class.
Some example Inputs might be:
78 C 15.48
3 B
87 P 15
0
..
The first argument is an int from 0-100, second a char and third int or float. A String can consist of one, two or three arguments which are separated by space. After reading a line, this program does some calculations and then expects another input until a 0 is entered or a break occurred.
To read an input String, i'm currently using
std::string str;
std::getline(std::cin, str);
My program already has the variables
int firstArgument;
char secondArgument;
float thirdFloatArgument;
int thirdIntArgument;
now, lets say str is: 46 C 87.3
after reading the line my variables should be:
firstArgument = 46;
secondArgument = 'C';
thirdFloatArgument = 87.3;
How can I extract the Information from the input String?
I was thinking about counting the spaces to see how much values are given and then separating the string via this delimiter,as the amount of arguments might vary. So:
int count = 0;
int length = str.length();
for(int i = 0; i < length; i++){
int c = str[i];
if(isspace(c)){
count++;
}
}
with space count being 2 I now know that 3 arguments were passed, but I don't know how to go on from there. Using std:istringstream might be an option but from what I've seen online it is mostly used in a while loop to print each word of a string in a new line or like that. But my input can vary in the amount of arguments so a loop would not work.
I think I need something like: "String before first ' ' is firstArgument, String between first and second ' ' is secondArgument, string after second ' ' is either thirdFloatArgument or thirdIntArgument (respectively if only one or two arguments are given, which can be determined with the amount of spaces). But how would I do this? Or are there some easier approaches?
Big thanks in advance!
As Some programmer dude mentioned it is a good idea to use std::istringstream to convert values from string to other data types. It allows you to treat input string the same way as you treat std::cin. Here is a code snippet that you can use in your case:
#include <string>
#include <iostream>
#include <algorithm>
#include <sstream>
struct Arguments {
int first{};
char second{};
double third{};
};
Arguments parseArgs(const std::string& inputLine) {
Arguments args;
const int argc = std::ranges::count(inputLine, ' ');
std::istringstream stream(inputLine);
if (argc >= 0) {
stream >> args.first;
}
if (argc >= 1) {
stream >> args.second;
}
if (argc >= 2) {
stream >> args.third;
}
return args;
}
int main() {
std::string inputLine{};
std::getline(std::cin, inputLine);
const auto args = parseArgs(inputLine);
std::cout << args.first << ", " << args.second << ", " << args.third << "\n";
}
Note that you have to compile this example using C++20 because I used std::ranges. If you do not have compiler that supports this standard you can use std::count(inputLine.cbegin(), inputLine.cend(), ' ');
// See the code below and help me why i did not getting the right result. Or suggest any other C++ function to convert a C-string like "$567,789,675.89" into long double
long double mstold( char s[] )
{
int len = strlen(s);
long double cash;
int n=0;
char amount[ 100 ];
for( int i=0; i<len; i++) // for copying the passed C-String into another C-string
{
amount[n] = s[i];
n++;
if( s[i] == '$' || s[i] == ',') // Because the C-String has been passed in format: "$567,789,564.987"
n--;
}
cash = _atold( amount ); // This does not gives the right result
return cash;
}
Use strtold() function, since _atold() is a non standard function. I am posting the code which works in compiler explorer. You were not terminating amount array with '\0'. Perhaps that's the reason _atold not worked.
#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;
long double mstold(const char* s)
{
int len = strlen(s);
long double cash;
int n = 0;
char* amount = new char[len+1];
for (int i = 0; i<len; i++) // for copying the passed C-String into another C-string
{
amount[n] = s[i];
n++;
if (s[i] == '$' || s[i] == ',') // Because the C-String has been passed in format: "$567,789,564.987"
n--;
}
amount[n] = '\0';
cash = strtold(amount, NULL); // This does not gives the right result
delete[] amount;
return cash;
}
int main()
{
long double cash = mstold("$567,789,675.89");
std::cout << cash << std::endl;
}
First note. Please do not use C-Style strings. In C++ we use std::string. Anyway, also C-style strings will do and can be converted automatically.
Then, for newbies it is the best to transform the input monetary-string to a number-string with just one decimal digit and then use function stold for conversion. You may read here about it.
But in the real C++ world, you would do 2 things:
use dedicated C++ facilities
use localization
Unfortunately this is a rather complex topic and you need a while to understand.
You need to read about the localization library. Here you will learn about 2 major concepts:
locales
facets
In general textual representation of dates, monetary values or number formats are governed by regional or cultural conventions. All this properties are contained in a std::locale object.
But the std::locale does not offer much functionality. The real localization facilities are offered in the form of facets. And a std::locale encapsulates several facets. And one of them is about the monetary formatting.
You really can do many things with that and in the end get fully customized behaviour. But, as said, not that easy to understand.
I will use the std::money_get class in my below example.
Please note. This will convert your number into units, without a fraction. In financial calculations we basically should not use fractions, because double or even long double cannot store all "reaal" values". Please read about this as well.
Anyway. I will show you an example how such a monetary value would be converted in C++. You maybe shocked by the complexity, but flexibility has its price . . .
Please see:
#include <iostream>
#include <iomanip>
#include <limits>
#include <string>
#include <locale>
#include <sstream>
int main() {
// Input String
char valueAsCString[] = "$567,789,675.89";
// Put it in an istringstream for formatted extraction
std::istringstream iss{ valueAsCString };
// Assume US currency format (On Posix machines, please use "en-US") and set it for the stream
std::locale myLocale("en_US");
iss.imbue(myLocale);
// Assume that everthing is good
std::ios_base::iostate ioError{ std::ios_base::goodbit };
// Here we will get the value in UNITS, so without a fraction!!!
long double value{};
// Parse the money string and get the result
std::use_facet<std::money_get<char>>(myLocale).get(std::istreambuf_iterator<char>(iss), {}, false, iss, ioError, value);
// Check Error state
iss.setstate(ioError);
if (iss)
// Show result
std::cout << std::setprecision(std::numeric_limits<long double>::digits10 + 1) << std::setw(25) << value / 100 << '\n';
else
std::cerr << "\nError during conversion\n";
}
i want to extract number string values of a char array. Actually I want to extract numbers embeded in file names for some file management. For example if there is a file name as file21 then i want the decimal number 21 from this file name.
How can i extract these values?
I tried the following but it results in an unexpected value. I think it is as a result of the implicit typecasting from the char to int while doing the arthimetic operation.
char * fname;
cout<<"enter file name";
cin>>fname;
int filenum=fname[4]%10+fname[5];
cout<<"file number is"<<filenum;
NOTE:
The filenamse are strictly in the format fileXX, XX being numbers between 01 and 99
You need to subtract '0' to get the decimal value of a digit character:
int filenum=(fname[4]-'0')*10+(fname[5]-'0');
Better yet, you should use atoi:
int filenum = atoi(fname+4);
You're getting undefined behavior because you're never allocating memory for the char* you read into:
char * fname = new char[16]; //should be enough for your filename format
or better yet
char fname[16];
Also, what do you expect:
fname[4]%10+fname[5];
to do? Magically concatenate the numbers?
First, you convert the first char to an int, multiply it by 10, convert the second char to an int and add to the first one. A simple google search for char to int would get you there.
How can i extract these values?
There are an infinite number of ways. One way is to use std::istringstream:
#include <string>
#include <sstream>
#include <iostream>
int main () {
std::string fname;
std::cout << "Enter file name: ";
std::getline(std::cin, fname);
int filenum;
std::istringstream stream(fname.substr(4,2));
if(stream >> filenum)
std::cout << "file number is " << filenum << "\n";
else
std::cout << "Merde\n";
}
Here is the simplest code:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
int filenum;
string fname;
cout<<"enter file name";
cin>>fname;
string str2 = fname.substr(4,2);
istringstream(str2) >> filenum;
cout<<"file number is"<<filenum;
return 0;
}
If your input is that much defined, the simplest solution is scanf:
int main()
{
int theNumber = 0;
scanf("file%d.txt", &theNumber);
printf("your number is %d", theNumber);
}
Check it out in action, reading from char* instead of stdio: http://codepad.org/JFqS70yI
scanf (and sscanf) also throws in checking for proper input format: returns number of fields read successfully. In this case if return value is any other than 1, the input was wrong.
I have a string which actually contains a number and a string, separated by ,, for instance "12,fooBar".
I would like to put it into separated variables, i.e. the number into unsigned int myNum and the string into std::string myStr.
I have the following snipped of code:
size_t pos1=value.find(',');
std::cout << value.substr(0, pos1) << " and "
<< (value.substr(0, pos1)).c_str() << std::endl;
This yields 12 and 1. Anything I missed here? What happend to the 2 in the second part?
Note: I isolated the problem to this snipped of code. I need c_str() to pass it to atoi to get the unsigend int. Here I don't want to print the second part.
Update: I actually get the string from levelDB Get. If I put a test string like I put here, it works.
The posted code produces the same substring: value.substr(0, pos1). Note that std::string::substr() does not modify the object, but returns a new std::string.
Example:
#include <iostream>
#include <string>
int main ()
{
std::string value ="12,fooBar";
unsigned int myNum;
std::string myStr;
const size_t pos1 = value.find(',');
if (std::string::npos != pos1)
{
myNum = atoi(value.substr(0, pos1).c_str());
myStr = value.substr(pos1 + 1);
}
std::cout << myNum << " and "
<< myStr << std::endl;
return 0;
}
Output:
12 and fooBar
EDIT:
If the unsigned int is the only piece required then the following will work:
unsigned int myNum = atoi(value.c_str());
as atoi() will stop at the first non-digit character (excluding optional leading - or +), in this case the ,.
The cleanest C++ style solution to this problem is to use a stringstream.
#include <sstream>
// ...
std::string value = "12,fooBar";
unsigned int myNum;
std::string myStr;
std::stringstream myStream(value);
myStream >> myNum;
myStream.ignore();
myStream >> myStr;
Your second substr should be value.substr(pos1+1,value.length())
One more option is using std::from_chars function from the 17th standard (< charconv > header):
int x;
from_chars(&s[i], &s.back(), x); // starting from character at index i parse
// the nearest interger till the second char pointer
There are different overloads for different types of value x (double etc.).
Usually when I write anything in C++ and I need to convert a char into an int I simply make a new int equal to the char.
I used the code(snippet)
string word;
openfile >> word;
double lol=word;
I receive the error that
Code1.cpp cannot convert `std::string' to `double' in initialization
What does the error mean exactly? The first word is the number 50. Thanks :)
You can convert char to int and viceversa easily because for the machine an int and a char are the same, 8 bits, the only difference comes when they have to be shown in screen, if the number is 65 and is saved as a char, then it will show 'A', if it's saved as a int it will show 65.
With other types things change, because they are stored differently in memory. There's standard function in C that allows you to convert from string to double easily, it's atof. (You need to include stdlib.h)
#include <stdlib.h>
int main()
{
string word;
openfile >> word;
double lol = atof(word.c_str()); /*c_str is needed to convert string to const char*
previously (the function requires it)*/
return 0;
}
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << stod(" 99.999 ") << endl;
}
Output: 99.999 (which is double, whitespace was automatically stripped)
Since C++11 converting string to floating-point values (like double) is available with functions:
stof - convert str to a float
stod - convert str to a double
stold - convert str to a long double
As conversion of string to int was also mentioned in the question, there are the following functions in C++11:
stoi - convert str to an int
stol - convert str to a long
stoul - convert str to an unsigned long
stoll - convert str to a long long
stoull - convert str to an unsigned long long
The problem is that C++ is a statically-typed language, meaning that if something is declared as a string, it's a string, and if something is declared as a double, it's a double. Unlike other languages like JavaScript or PHP, there is no way to automatically convert from a string to a numeric value because the conversion might not be well-defined. For example, if you try converting the string "Hi there!" to a double, there's no meaningful conversion. Sure, you could just set the double to 0.0 or NaN, but this would almost certainly be masking the fact that there's a problem in the code.
To fix this, don't buffer the file contents into a string. Instead, just read directly into the double:
double lol;
openfile >> lol;
This reads the value directly as a real number, and if an error occurs will cause the stream's .fail() method to return true. For example:
double lol;
openfile >> lol;
if (openfile.fail()) {
cout << "Couldn't read a double from the file." << endl;
}
If you are reading from a file then you should hear the advice given and just put it into a double.
On the other hand, if you do have, say, a string you could use boost's lexical_cast.
Here is a (very simple) example:
int Foo(std::string anInt)
{
return lexical_cast<int>(anInt);
}
The C++ way of solving conversions (not the classical C) is illustrated with the program below. Note that the intent is to be able to use the same formatting facilities offered by iostream like precision, fill character, padding, hex, and the manipulators, etcetera.
Compile and run this program, then study it. It is simple
#include "iostream"
#include "iomanip"
#include "sstream"
using namespace std;
int main()
{
// Converting the content of a char array or a string to a double variable
double d;
string S;
S = "4.5";
istringstream(S) >> d;
cout << "\nThe value of the double variable d is " << d << endl;
istringstream("9.87654") >> d;
cout << "\nNow the value of the double variable d is " << d << endl;
// Converting a double to string with formatting restrictions
double D=3.771234567;
ostringstream Q;
Q.fill('#');
Q << "<<<" << setprecision(6) << setw(20) << D << ">>>";
S = Q.str(); // formatted converted double is now in string
cout << "\nThe value of the string variable S is " << S << endl;
return 0;
}
Prof. Martinez
Coversion from string to double can be achieved by
using the 'strtod()' function from the library 'stdlib.h'
#include <iostream>
#include <stdlib.h>
int main ()
{
std::string data="20.9";
double value = strtod(data.c_str(), NULL);
std::cout<<value<<'\n';
return 0;
}
#include <string>
#include <cmath>
double _string_to_double(std::string s,unsigned short radix){
double n = 0;
for (unsigned short x = s.size(), y = 0;x>0;)
if(!(s[--x] ^ '.')) // if is equal
n/=pow(10,s.size()-1-x), y+= s.size()-x;
else
n+=( (s[x]-48) * pow(10,s.size()-1-x - y) );
return n;
}
or
//In case you want to convert from different bases.
#include <string>
#include <iostream>
#include <cmath>
double _string_to_double(std::string s,unsigned short radix){
double n = 0;
for (unsigned short x = s.size(), y = 0;x>0;)
if(!(s[--x] ^ '.'))
n/=pow(radix,s.size()-1-x), y+= s.size()-x;
else
n+=( (s[x]- (s[x]<='9' ? '0':'0'+7) ) * pow(radix,s.size()-1-x - y) );
return n;
}
int main(){
std::cout<<_string_to_double("10.A",16)<<std::endl;//Prints 16.625
std::cout<<_string_to_double("1001.1",2)<<std::endl;//Prints 9.5
std::cout<<_string_to_double("123.4",10)<<std::endl;//Prints 123.4
return 0;
}