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(), ' ');
Related
I want to write a program in which the names of n different chemical elements are read in the same line in the input (where 1 ≤ n ≤ 17 and n is also read in the input) (the names are made apart by a space). The names of the chemical elements should be stored in different strings for further uses.
As n is unknown, I don't know how to make something like an "array of strings". Of course I should not make 17 different strings st1,st2,st3,... :D.
Can you please help me? Any help will be high appreciated and they will help me a lot.
Thank you in advance.
It sounds like you want to read in a line and split it with spaces. Try something such as this:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
int main()
{
std::string input;
std::getline(std::cin, input); // takes one line, stops when enter is pressed
std::stringstream ss(input); // makes a stream using the string
std::vector<std::string> strings;
while (ss >> input) { // while there's data left in the stream, store it in a new string and add it to the vector of strings
strings.push_back(input);
}
for (std::string s : strings) {
std::cout << "string: " << s << std::endl;
}
}
You give input such as H He Li, terminated by hitting enter, and the strings are stored in strings (printed in last loop for demonstration).
Edit:
I now see that you want to read the number n in the input, too. In this case, you don't need the stringstream solution. You can do this instead:
int main()
{
int amount;
std::cin >> amount; // read in the amount
std::vector<std::string> strings;
for (int i = 0; i < amount; i++) {
std::string s;
std::cin >> s; // read in the nth string
strings.push_back(s); // add it to the vector
}
for (std::string s : strings) {
std::cout << "string: " << s << std::endl;
}
}
And pass input such as 3 H He Li.
I was given a code from my professor that takes multiple lines of input. I am currently changing the code for our current assignment and I came across an issue. The code is meant to take strings of input and separate them into sentences from periods and put those strings into a vector.
vector<string> words;
string getInput() {
string s = ""; // string to return
bool cont = true; // loop control.. continue is true
while (cont){ // while continue
string l; // string to hold a line
cin >> l; // get line
char lastChar = l.at(l.size()-1);
if(lastChar=='.') {
l = l.substr(0, l.size()-1);
if(l.size()>0){
words.push_back(s);
s = "";
}
}
if (lastChar==';') { // use ';' to stop input
l = l.substr(0, l.size()-1);
if (l.size()>0)
s = s + " " + l;
cont = false; // set loop control to stop
}
else
s = s + " " + l; // add line to string to return
// add a blank space to prevent
// making a new word from last
// word in string and first word
// in line
}
return s;
}
int main()
{
cout << "Input something: ";
string s = getInput();
cout << "Your input: " << s << "\n" << endl;
for(int i=0; i<words.size(); i++){
cout << words[i] << "\n";
}
}
The code puts strings into a vector but takes the last word of the sentence and attaches it to the next string and I cannot seem to understand why.
This line
s = s + " " + l;
will always execute, except for the end of input, even if the last character is '.'. You are most likely missing an else between the two if-s.
You have:
string l; // string to hold a line
cin >> l; // get line
The last line does not read a line unless the entire line has non-white space characters. To read a line of text, use:
std::getline(std::cin, l);
It's hard telling whether that is tripping your code up since you haven't posted any sample input.
I would at least consider doing this job somewhat differently. Right now, you're reading a word at a time, then putting the words back together until you get to a period.
One possible alternative would be to use std::getline to read input until you get to a period, and put the whole string into the vector at once. Code to do the job this way could look something like this:
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <iterator>
int main() {
std::vector<std::string> s;
std::string temp;
while (std::getline(std::cin, temp, '.'))
s.push_back(temp);
std::transform(s.begin(), s.end(),
std::ostream_iterator<std::string>(std::cout, ".\n"),
[](std::string const &s) { return s.substr(s.find_first_not_of(" \t\n")); });
}
This does behave differently in one circumstance--if you have a period somewhere other than at the end of a word, the original code will ignore that period (won't treat it as the end of a sentence) but this will. The obvious place this would make a difference would be if the input contained a number with a decimal point (e.g., 1.234), which this would break at the decimal point, so it would treat the 1 as the end of one sentence, and the 234 as the beginning of another. If, however, you don't need to deal with that type of input, this can simplify the code considerably.
If the sentences might contain decimal points, then I'd probably write the code more like this:
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <iterator>
class sentence {
std::string data;
public:
friend std::istream &operator>>(std::istream &is, sentence &s) {
std::string temp, word;
while (is >> word) {
temp += word + ' ';
if (word.back() == '.')
break;
}
s.data = temp;
return is;
}
operator std::string() const { return data; }
};
int main() {
std::copy(std::istream_iterator<sentence>(std::cin),
std::istream_iterator<sentence>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
Although somewhat longer and more complex, at least to me it still seems (considerably) simpler than the code in the question. I guess it's different in one way--it detects the end of the input by...detecting the end of the input, rather than depending on the input to contain a special delimiter to mark the end of the input. If you're running it interactively, you'll typically need to use a special key combination to signal the end of input (e.g., Ctrl+D on Linux/Unix, or F6 on Windows).
In any case, it's probably worth considering a fundamental difference between this code and the code in the question: this defines a sentence as a type, where the original code just leaves everything as strings, and manipulates strings. This defines an operator>> for a sentence, that reads a sentence from a stream as we want it read. This gives us a type we can manipulate as an object. Since it's like a string in other ways, we provide a conversion to string so once you're done reading one from a stream, you can just treat it as a string. Having done that, we can (for example) use a standard algorithm to read sentences from standard input, and write them to standard output, with a new-line after each to separate them.
I am making an application that deals with txt file data.
The idea is that txt files may come in different formats, and it should be read into C++.
One example might be 3I2, 3X, I3, which should be done as: "first we have 3 integers of length 2, then we have 3 empty spots, then we have 1 integer of length 3.
Is the best to iterate over the file, yielding lines, followed by iterating over the lines as strings? What would be an effective approach for iterating smartly leaving out the 3 spots to be ignored?
E.g.
101112---100
102113---101
103114---102
to:
10, 11, 12, 100
10, 21, 13, 101
10, 31, 14, 102
The link given by Kyle Kanos is a good one; *scanf/*printf format strings map pretty well onto fortran format strings. It's actually easier to do this using C-style IO, but using C++ style streams is doable as well:
#include <cstdio>
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream fortranfile;
fortranfile.open("input.txt");
if (fortranfile.is_open()) {
std::string line;
getline(fortranfile, line);
while (fortranfile.good()) {
char dummy[4];
int i1, i2, i3, i4;
sscanf(line.c_str(), "%2d%2d%2d%3s%3d", &i1, &i2, &i3, dummy, &i4);
std::cout << "Line: '" << line << "' -> " << i1 << " " << i2 << " "
<< i3 << " " << i4 << std::endl;
getline(fortranfile, line);
}
}
fortranfile.close();
return 0;
}
Running gives
$ g++ -o readinput readinput.cc
$ ./readinput
Line: '101112---100' -> 10 11 12 100
Line: '102113---101' -> 10 21 13 101
Line: '103114---102' -> 10 31 14 102
Here the format string we're using is %2d%2d%2d%3s%3d - 3 copies of %2d (decimal integer of width 2) followed by %3s (string of width 3, which we read into a variable we never use) followed by %3d (decimal integer of width 3).
Given that you wish to dynamically parse Fortran Format specifier flags, you should note that: you've immediately walked into the realm of parsers.
In addition to the other methods of parsing such input that others have noted here:
By using Fortran and CC/++ bindings to do the parsing for you.
Using pure C++ to parse it for you by writing a parser using a combination of:
sscanf
streams
My proposal is that if boost is available to you, you can use it to implement a simple parser for on-the-fly operations, using a combination of Regexes and STL containers.
From what you've described, and what is shown in different places, you can construct a naive implementation of the grammar you wish to support, using regex captures:
(\\d{0,8})([[:alpha:]])(\\d{0,8})
Where the first group is the number of that variable type.
The second is the type of the variable.
and the third is the length of variable type.
Using this reference for the Fortran Format Specifier Flags, you can implement a naive solution as shown below:
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <cstdlib>
#include <boost/regex.hpp>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
//A POD Data Structure used for storing Fortran Format Tokens into their relative forms
typedef struct FortranFormatSpecifier {
char type;//the type of the variable
size_t number;//the number of times the variable is repeated
size_t length;//the length of the variable type
} FFlag;
//This class implements a rudimentary parser to parse Fortran Format
//Specifier Flags using Boost regexes.
class FormatParser {
public:
//typedefs for further use with the class and class methods
typedef boost::tokenizer<boost::char_separator<char> > bst_tokenizer;
typedef std::vector<std::vector<std::string> > vvstr;
typedef std::vector<std::string> vstr;
typedef std::vector<std::vector<int> > vvint;
typedef std::vector<int> vint;
FormatParser();
FormatParser(const std::string& fmt, const std::string& fname);
void parse();
void printIntData();
void printCharData();
private:
bool validateFmtString();
size_t determineOccurence(const std::string& numStr);
FFlag setFortranFmtArgs(const boost::smatch& matches);
void parseAndStore(const std::string& line);
void storeData();
std::string mFmtStr; //this holds the format string
std::string mFilename; //the name of the file
FFlag mFmt; //a temporary FFlag variable
std::vector<FFlag> mFortranVars; //this holds all the flags and details of them
std::vector<std::string> mRawData; //this holds the raw tokens
//this is where you will hold all the types of data you wish to support
vvint mIntData; //this holds all the int data
vvstr mCharData; //this holds all the character data (stored as strings for convenience)
};
FormatParser::FormatParser() : mFmtStr(), mFilename(), mFmt(), mFortranVars(), mRawData(), mIntData(), mCharData() {}
FormatParser::FormatParser(const std::string& fmt, const std::string& fname) : mFmtStr(fmt), mFilename(fname), mFmt(), mFortranVars(), mRawData(), mIntData(), mCharData() {}
//this function determines the number of times that a variable occurs
//by parsing a numeric string and returning the associated output
//based on the grammar
size_t FormatParser::determineOccurence(const std::string& numStr) {
size_t num = 0;
//this case means that no number was supplied in front of the type
if (numStr.empty()) {
num = 1;//hence, the default is 1
}
else {
//attempt to parse the numeric string and find it's equivalent
//integer value (since all occurences are whole numbers)
size_t n = atoi(numStr.c_str());
//this case covers if the numeric string is expicitly 0
//hence, logically, it doesn't occur, set the value accordingly
if (n == 0) {
num = 0;
}
else {
//set the value to its converted representation
num = n;
}
}
return num;
}
//from the boost::smatches, determine the set flags, store them
//and return it
FFlag FormatParser::setFortranFmtArgs(const boost::smatch& matches) {
FFlag ffs = {0};
std::string fmt_number, fmt_type, fmt_length;
fmt_number = matches[1];
fmt_type = matches[2];
fmt_length = matches[3];
ffs.type = fmt_type.c_str()[0];
ffs.number = determineOccurence(fmt_number);
ffs.length = determineOccurence(fmt_length);
return ffs;
}
//since the format string is CSV, split the string into tokens
//and then, validate the tokens by attempting to match them
//to the grammar (implemented as a simple regex). If the number of
//validations match, everything went well: return true. Otherwise:
//return false.
bool FormatParser::validateFmtString() {
boost::char_separator<char> sep(",");
bst_tokenizer tokens(mFmtStr, sep);
mFmt = FFlag();
size_t n_tokens = 0;
std::string token;
for(bst_tokenizer::const_iterator it = tokens.begin(); it != tokens.end(); ++it) {
token = *it;
boost::trim(token);
//this "grammar" is based on the Fortran Format Flag Specification
std::string rgx = "(\\d{0,8})([[:alpha:]])(\\d{0,8})";
boost::regex re(rgx);
boost::smatch matches;
if (boost::regex_match(token, matches, re, boost::match_extra)) {
mFmt = setFortranFmtArgs(matches);
mFortranVars.push_back(mFmt);
}
++n_tokens;
}
return mFortranVars.size() != n_tokens ? false : true;
}
//Now, parse each input line from a file and try to parse and store
//those variables into their associated containers.
void FormatParser::parseAndStore(const std::string& line) {
int offset = 0;
int integer = 0;
std::string varData;
std::vector<int> intData;
std::vector<std::string> charData;
offset = 0;
for (std::vector<FFlag>::const_iterator begin = mFortranVars.begin(); begin != mFortranVars.end(); ++begin) {
mFmt = *begin;
for (size_t i = 0; i < mFmt.number; offset += mFmt.length, ++i) {
varData = line.substr(offset, mFmt.length);
//now store the data, based on type:
switch(mFmt.type) {
case 'X':
break;
case 'A':
charData.push_back(varData);
break;
case 'I':
integer = atoi(varData.c_str());
intData.push_back(integer);
break;
default:
std::cerr << "Invalid type!\n";
}
}
}
mIntData.push_back(intData);
mCharData.push_back(charData);
}
//Open the input file, and attempt to parse the input file line-by-line.
void FormatParser::storeData() {
mFmt = FFlag();
std::ifstream ifile(mFilename.c_str(), std::ios::in);
std::string line;
if (ifile.is_open()) {
while(std::getline(ifile, line)) {
parseAndStore(line);
}
}
else {
std::cerr << "Error opening input file!\n";
exit(3);
}
}
//If character flags are set, this function will print the character data
//found, line-by-line
void FormatParser::printCharData() {
vvstr::const_iterator it = mCharData.begin();
vstr::const_iterator jt;
size_t linenum = 1;
std::cout << "\nCHARACTER DATA:\n";
for (; it != mCharData.end(); ++it) {
std::cout << "LINE " << linenum << " : ";
for (jt = it->begin(); jt != it->end(); ++jt) {
std::cout << *jt << " ";
}
++linenum;
std::cout << "\n";
}
}
//If integer flags are set, this function will print all the integer data
//found, line-by-line
void FormatParser::printIntData() {
vvint::const_iterator it = mIntData.begin();
vint::const_iterator jt;
size_t linenum = 1;
std::cout << "\nINT DATA:\n";
for (; it != mIntData.end(); ++it) {
std::cout << "LINE " << linenum << " : ";
for (jt = it->begin(); jt != it->end(); ++jt) {
std::cout << *jt << " ";
}
++linenum;
std::cout << "\n";
}
}
//Attempt to parse the input file, by first validating the format string
//and then, storing the data accordingly
void FormatParser::parse() {
if (!validateFmtString()) {
std::cerr << "Error parsing the input format string!\n";
exit(2);
}
else {
storeData();
}
}
int main(int argc, char **argv) {
if (argc < 3 || argc > 3) {
std::cerr << "Usage: " << argv[0] << "\t<Fortran Format Specifier(s)>\t<Filename>\n";
exit(1);
}
else {
//parse and print stuff here
FormatParser parser(argv[1], argv[2]);
parser.parse();
//print the data parsed (if any)
parser.printIntData();
parser.printCharData();
}
return 0;
}
This is standard c++98 code and can be compiled as follows:
g++ -Wall -std=c++98 -pedantic fortran_format_parser.cpp -lboost_regex
BONUS
This rudimentary parser also works on Characters too (Fortran Format Flag 'A', for up to 8 characters). You can extend this to support whatever flags you may like by editing the regex and performing checks on the length of captured strings in tandem with the type.
POSSIBLE IMPROVEMENTS
If C++11 is available to you, you can use lambdas in some places and substitute auto for the iterators.
If this is running in a limited memory space, and you have to parse a large file, vectors will inevitably crash due to the way how vectors manages memory internally. It will be better to use deques instead. For more on that see this as discussed from here:
http://www.gotw.ca/gotw/054.htm
And, if the input file is large, and file I/O is a bottleneck, you can improved performance by modifying the size of the ifstream buffer:
How to get IOStream to perform better?
DISCUSSION
What you will notice is that: the types that you're parsing must be known at runtime, and any associated storage containers must be supported in the class declaration and definition.
As you would imagine, supporting all types in one main class isn't efficient. However, as this is a naive solution, an improved full solution can be specialized to support these cases.
Another suggestion is to use Boost::Spirit. But, as Spirit uses a lot of templates, debugging such an application is not for the faint of heart when errors can and do occur.
PERFORMANCE
Compared to #Jonathan Dursi's solution, this solution is slow:
For 10,000,000 lines of randomly generated output (a 124MiB file) using this same line format ("3I2, 3X, I3"):
#include <fstream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main(int argc, char **argv) {
srand(time(NULL));
if (argc < 2 || argc > 2) {
printf("Invalid usage! Use as follows:\t<Program>\t<Output Filename>\n");
exit(1);
}
ofstream ofile(argv[1], ios::out);
if (ofile.is_open()) {
for (int i = 0; i < 10000000; ++i) {
ofile << (rand() % (99-10+1) + 10) << (rand() % (99-10+1) + 10) << (rand() % (99-10+1)+10) << "---" << (rand() % (999-100+1) + 100) << endl;
}
}
ofile.close();
return 0;
}
My solution:
0m13.082s
0m13.107s
0m12.793s
0m12.851s
0m12.801s
0m12.968s
0m12.952s
0m12.886s
0m13.138s
0m12.882s
Clocks an average walltime of 12.946s
Jonathan Dursi's solution:
0m4.698s
0m4.650s
0m4.690s
0m4.675s
0m4.682s
0m4.681s
0m4.698s
0m4.675s
0m4.695s
0m4.696s
Blazes with average walltime of 4.684s
His is faster than mine by at least 270% with both on O2.
However, since you don't have to actually modify the source code every time you want to parse an additional format flag, then this solution is more optimal.
Note: you can implement a solution that involves sscanf / streams that only requires you to know what type of variable you wish to read (much like mine), but the additional checks such as verifying the type(s) bloats development time. (This is why I offer my solution in Boost, because of the convenience of tokenizers and regexes - which makes the development process easier).
REFERENCES
http://www.boost.org/doc/libs/1_34_1/libs/regex/doc/character_class_names.html
You could translate 3I2, 3X, I3 in a scanf format.
Given that Fortran is easily callable from C, you could write a little Fortran function to do this "natively." The Fortran READ function takes a format string as you describe, after all.
If you want this to work, you'll need to brush up on Fortran just a tiny bit (http://docs.oracle.com/cd/E19957-01/806-3593/2_io.html), plus learn how to link Fortran and C++ using your compiler. Here are a few tips:
The Fortran symbols may be implicitly suffixed with underscore, so MYFUNC may be called from C as myfunc_().
Multi-dimensional arrays have the opposite ordering of dimensions.
Declaring a Fortran (or C) function in a C++ header requires placing it in an extern "C" {} scope.
If your user is actually supposed to enter it in the Fortran format, or if you very quickly adapt or write Fortran code to do this, I would do as John Zwinck and M.S.B. suggest. Just write a short Fortran routine to read the data into an array, and use "bind(c)" and the ISO_C_BINDING types to set up the interface. And remember that the array indexing is going to change between Fortran and C++.
Otherwise, I would recommend using scanf, as mentioned above:
http://en.cppreference.com/w/cpp/io/c/fscanf
If you don't know the number of items per line you need to read, you might be able to use vscanf instead:
http://en.cppreference.com/w/cpp/io/c/vfscanf
However, although it looks convenient, I've never used this, so YMMV.
Thought about this some today but no time to write an example. #jrd1's example and analysis are on track but I'd try to make the parsing more modular and object oriented. The format string parser could build a list of item parsers that then worked more or less independently, allowing adding new ones like floating point without changing old code. I think a particularly nice interface would be an iomanip initialized with a format string so that the ui would be something like
cin >> f77format("3I2, 3X, I3") >> a >> b >> c >> d;
On implementation I'd have f77format parse the bits and build the parser by components, so it would create 3 fixed width int parsers, a devNull parser and another fixed width parser that would then consume the input.
Of course if you want to support all of the edit descriptors, it would be a big job! And in general it wouldn't just be passing the rest of the string on to the next parser since there are edit descriptors that require re-reading the line.
I have the following .txt file:
test.txt
1,2,5,6
Passing into a small C++ program I made through command line as follows:
./test test.txt
Source is as follows:
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char **argv)
{
int temp =0;
ifstream file;
file.open(argv[1]);
while(!file.eof())
{
temp=file.get();
file.ignore(1,',');
cout<<temp<<' ';
}
return 0;
}
For some reason my output is not 1 2 5 6 but 49 50 53 54. What gives?
UPDATE:
Also, I noticed there is another implementation of get(). If I define char temp then I can do file.get(temp) and that will also save me converting ASCII representation. However I like using while (file >> temp) so I will be going with that. Thanks.
temp is an int. So you see the encoded ascii values after casting the char to an int.
49 is the ascii code for digit 49-48 = 1.
get() gives you a character (character code).
by the way, eof() only becomes true after a failed read attempt, so the code you show,
while(!file.eof())
{
temp=file.get();
file.ignore(1,',');
cout<<temp<<' ';
}
will possibly display one extraneous character at the end.
the conventional loop is
while( file >> temp )
{
cout << temp << ' ';
}
where the expression file >> temp reads in one number and produces a reference to file, and where that file objected is converted to bool as if you had written
while( !(file >> temp).fail() )
This does not do what you think it does:
while(!file.eof())
This is covered in Why is iostream::eof inside a loop condition considered wrong?, so I won't cover it in this answer.
Try:
char c;
while (file >> c)
{
// [...]
}
...instead. Reading in a char rather than an int will also save you having to convert the ascii representation (ASCII value 49 is 1, etc...).
For the record, and despite this being the nth duplicate, here's how this code might look in idiomatic C++:
for (std::string line; std::getline(file, line); )
{
std::istringstream iss(line);
std::cout << "We read:";
for (std::string n; std::getline(iss, line, ','); )
{
std::cout << " " << n;
// now use e.g. std::stoi(n)
}
std::cout << "\n";
}
If you don't care about lines or just have one line, you can skip the outer loop.
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.).