I'm looking for a way to convert a string with specified dividers (such as slashes or spaces) into an array of the integers those dividers separate.
For example, if the user inputs 12/3/875/256, I need to retrieve the array {12, 3, 875, 256}. Ideally, it would be able to handle an arbitrary length.
I tried sweeping through the string character-by-character and storing everything that's not a divider in a temporary variable, which is added to the array the next time I encounter a divider character. Unfortunately, the type conversions are being a pain in the butt. Is there an easier way to do this?
You can set '/' to a delimiter and read using getline? then you'd have to put each one into a variable, and you'd need to know the size--maybe you can pass over the array and count the slashes? then you'd know that and can set up the array first. You might need to parse each string segment into an int, which may or may not be difficult. (haven't used c++ for a while, I don't remember a convenient way.)
See here for a small example of how this is done (3 posts down).
Try using the boost::tokenizer and boost::lexical_cast
strtok and strtol? (this is somewhat tongue in cheek. Strtok is usually not a good idea)
The splitting is covered in this Parsing String to Array of Integers
COnverting strings to int in C++ has quite a number of relevant questions https://stackoverflow.com/search?q=convert+string+to+int+c%2B%2B
What is the issue with the type conversions? It doesn't seem to be a block as far as I can see.
Can you show your code?
Take a look at this other answer. It even has an example of a tokenizer code using boost::tokenizer.
EDIT:
I copied the code there with the neccessary modifications:
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
#include <vector>
#include <boost/lexical_cast.hpp>
#include <iterator>
#include <algorithm>
using namespace std;
using namespace boost;
int main(int argc, char** argv)
{
string text = "125/55/66";
vector<int> vi;
char_separator<char> sep("/");
tokenizer<char_separator<char> > tokens(text, sep);
BOOST_FOREACH(string t, tokens)
{
vi.push_back(lexical_cast<int>(t));
}
copy(vi.begin(), vi.end(), ostream_iterator<int>(cout, "\n"));
}
Will print:
125
55
66
You could use a combination of Boost.split and Boost.lexical_cast to break up the string by whatever delimiters you want, and then you could lexical cast it all.
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <vector>
#include <string>
int main()
{
std::string s = "11/23/2010";
std::vector<std::string> svec;
std::vector<int> ivec;
// split the string 's' on '/' delimiter, and the resulting tokens
// will be in svec.
boost::split(svec, s, boost::is_any_of("/"));
// Simple conversion - iterate through the token vector svec
// and attempt a lexical cast on each string to int
BOOST_FOREACH(std::string item, svec)
{
try
{
int i = boost::lexical_cast<int>(item);
ivec.push_back(i);
}
catch (boost::bad_lexical_cast &ex)
{
std::cout << ex.what();
}
}
return 0;
}
Untested...don't have boost on this machine.
Other ways you could use to convert std::string/char * to int types involve stringstream use directly, or C constructs like atoi.
Related
In C++, how do I get ALL of the text after a space. I am trying to make my own coding language, so I want the user to be able to enter (/print (text here)) and print the text the user has entered. I want this to be all in one line; without having the user to input the command, then input the thing they want to output. Thank you to anyone who replies in advance.
Try this way. It will give you all the characters after the first space in the string.
std::string x = "ABC CDEFG HIJKL";
x.substr(x.find(" ") + 1);
Leveraging <algorithm>
The following will work with C++11:
#include <string>
#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
bool is_blank(char ch)
{
return std::isblank(static_cast<unsigned char>(ch));
}
int main() {
std::string inp = "print foo";
auto it = std::find_if(inp.begin(), inp.end(), is_blank);
it = std::find_if_not(it, inp.end(), is_blank);
std::copy(it, inp.end(), std::ostream_iterator<char>(std::cout));
}
Run this code in Compiler Explorer.
Note that we're only iterating over the input string once. Also note that this solution leverages the algorithms which come with the C++ standard library - no raw loops required :-)
Using std::string's find functions
std::string has a ton of built-in functions. I'm pretty sure if C++ could be developed from scratch most of them wouldn't be there. But since we have them we put them to some use:
#include <string>
#include <algorithm>
#include <iostream>
#include <iterator>
int main() {
std::string inp = "print foo";
const std::string whitespace = " \t";
auto i = inp.find_first_of(whitespace);
i = inp.find_first_not_of(whitespace, i);
std::cout << inp.substr(i, inp.size() - i) << std::endl;
}
Run this code in Compiler Explorer.
I prefer the first solution since I find the last line a little more readable. std::copy might also be slightly more efficient. Here std::string::substr() returns a temporary string which gets destroyed once std::cout has printed it. Not ideal in terms of performance which might or might not matter here.
this is what I have done till now: I want to read words from file in C++ and I am allowed to use only cstring library. this is my piece of code
#include <cstring>
#include <fstream>
#include <stdio.h>
using namespace std;
int main(){
ifstream file;
char word[1];
file.open("p.txt");
while (!file.eof()){
file >> word;
cout << word << endl;
}
system("pause");
return 0;
}
It is working fine and reading one word at a time. But I don't understand how this is working fine.
How can char array of any size be it char word[1] or char word[50] read only one word at a time ignoring spaces.
And further I want to store these words in dynamic array. How can I achieve this? Any guidance would be appreciated?
Your code has undefined behaviour. operator >> simply overwrites memory beyond the array.
Take into account that included by you header <stdio.h> is not used in the program. On the other hand you need to include header <cstdlib> that declares function system.
As for your second question then you should use for example standard container std::vector<std::string>
For example
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstdlib>
int main()
{
std::ifstream file("p.txt");
std::string s;
std::vector<std::string> v;
v.reserve( 100 );
while ( file >> s ) v.push_back( s );
std::system( "pause" );
return 0;
}
Or you can simply define the vector as
std::vector<std::string> v( ( std::istream_iterator<std::string>( file ) ),
std::istream_iterator<std::string>() );
provided that you will include header <iterator>
For example
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <cstdlib>
int main()
{
std::ifstream file("p.txt");
std::vector<std::string> v( ( std::istream_iterator<std::string>( file ) ),
std::istream_iterator<std::string>() );
for ( const std::string &s : v ) std::cout << s << std::endl;
std::system( "pause" );
return 0;
}
Your code is invoking undefined behavior. That it doesn't crash is a roll of the dice, but its execution is not deterministic precisely because that is the nature of being undefined.
The easiest way (I've found) to load a file of words with whitespace separation is by:
std::ifstream inp("p.txt");
std::istream_iterator<std::string> inp_it(inp), inp_eof;
std::vector<std::string> strs(inp_it, inp_eof);
strs will contain every whitespace delimited char sequence as a linear vector of std::string. Use std::string for dynamic string content and don't feel the least bit guilty about exploiting the hell out of the hard work those that came before you gave us all: The Standard Library.
Your code is failing due to the overload of char * for operator>>.
An array of char, regardless the size, will decompose to the type char * where the value is the address of the start of the array.
For compatibility with the C language, the overloaded operator>>(char *) has been implemented to read one or more characters until a terminating whitespace character is reached, or there is an error with the stream.
If you declare an array of 1 character and read from a file containing "California", the function will put 'C' into the first location of the array and keep writing the remaining characters to the next locations in memory (regardless of what data type they are). This is known as a buffer overflow.
A much safer method is to read into a std::string or if you only want one character, use a char variable. Look in your favorite C++ reference for the getline methods. There is an overload for reading until a given delimiter is reached.
You only need a couple changes:
#include <cstring>
#include <fstream>
#include <stdio.h>
#include <string>
int main(){
ifstream file;
string word;
file.open("p.txt");
while (file >> word){
cout << word << endl;
}
system("pause");
return 0;
}
It works because you are lucky and you don't overwrite some critical memory. You need to allocate enough bytes for char word array, say char word[64]. And use while(file>>word) as your test for EOF. In the loop you can push_back the word into a std::vector<string> if you are allowed to use C++ STL.
If you want a simple C++11 STL-like solution, use this
#include <algorithm>
#include <iterator>
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
ifstream fin("./in.txt"); // input file
vector<string> words; // store the words in a vector
copy(istream_iterator<string>(fin),{}, back_inserter(words)); // insert the words
for(auto &elem: words)
cout << elem << endl; // display them
}
Or, more compactly, construct the container directly from the stream iterator like
vector<string> words(istream_iterator<string>(fin),{});
and remove the copy statement.
If instead a vector<string> you use a multiset<string> (#include <set>) and change
copy(istream_iterator<string>(fin),{}, back_inserter(words)); // insert the words
to
copy(istream_iterator<string>(fin),{}, inserter(words, words.begin())); // insert the words
you get the words ordered. So using STL is the cleanest approach in my opinion.
You're using C++, so you can avoid all that C stuff.
std::string word;
std::vector<std::string> words;
std::fstream stream("wordlist");
// this assumes one word (or phrase, with spaces, etc) per line...
while (std::getline(stream, word))
words.push_back(word);
or for multiple words (or phrases, with spaces, etc) per line separated by commas:
while (std::getline(stream, word, ','))
words.push_back(word);
or for multiple words per line separated by spaces:
while(stream >> word)
words.push_back(word);
No need to worry about buffer sizes or memory allocation or anything like that.
file>>char *
Will work with any char * and you are using
file >> word;
and it simply sees work variable as a char * but you are getting a segemntation fault somewhere and if your code grows you will see something is not working without any logical reason. GDB debugger will show you the seg fault
Basicly I have a text file which i need t read-in the values so the program can manipulate them.
Im using C++ and i have written working code to tell if the file exists or not.
The text file is formatted like this:
1 7
8 10
20 6
3 14
...
The values on the left are X values and the values on the right are Y values. (The space in the middle is a tab)
How do I extract this data? say to pass them into a class like this...
myVector(X,Y);
Also, I guess before I can use it in a class I have to TryParse to change it from a string to int right? can C++ do this?
Thank you!
I would be writing something like this if I were you. Note, this is just prototype code, and it was not even tested.
The fundamental idea is to read twice in a line, but with different delimiters. You would read with the tab delimiter first, and then just the default line end.
You need to make sure to gracefully quit the loop when you do not have anything more to read, hence the breaks, albeit the second could be enough if your file is "correct".
You will also need to make sure to convert to the proper type that your vector class expects. I assumed here that is int, but if it is string, you do not need the conversion I have put in place.
#include <string>
#include <fstream>
using namespace std;
void yourFunction()
{
..
ifstream myfile("myfile.txt");
string xword, yword;
while (1) {
if (!getline(myfile, xword, '\t'))
break;
if (!getline(myfile, yword))
break;
myVector.push_back(stoi(xword), stoi(yword));
}
...
}
This sort of parsing could be done in one line with boost.spirit:
qi::phrase_parse(begin, end, *(qi::int_ > qi::int_ > qi::eol), qi::ascii::blank, v);
The grammar could be read as: "read one int, then one int, then one EOL (end of line) (\n or \r\n, depends on locale), as many time as possible". Between ints and EOL can be found blank characters (e.g. spaces or tabs).
Advantages: rather than std::getline loops, code is more clear/concise. spirit.qi get you more powerful control and you don't need stoi calls.
Drawbacks: build-depends (no depends) to spirit.qi, compilation time.
#include <iostream>
#include <fstream>
#include <vector>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>
namespace spirit = boost::spirit;
namespace qi = spirit::qi;
int main(int argc, char **argv)
{
std::ifstream in(argv[1], std::ios_base::in);
std::string storage;
in.unsetf(std::ios::skipws);
spirit::istream_iterator begin(in), end;
std::vector<std::pair<int, int> > v;
qi::phrase_parse(begin, end, *(qi::int_ > qi::int_ > qi::eol), qi::ascii::blank, v);
for(const auto& p : v)
std::cout << p.first << "," << p.second << std::endl;
return 0;
}
I have a vector of strings, like this:
{"abc"}{"def"}{"ghi"}
I want to concatenate them into a single string, with a separator like "-".
Is there a concise (pretty) way of doing this without using a typical for loop? I have c++03 and boost available to me.
Sure, boost provides a convenient algorithm for achieving what you are trying to do. In higher level languages you may have spotted a join function. Boost provides an equivalent algorithm in the join function.
#include <boost/algorithm/string/join.hpp>
using namespace std;
string data[] = {"abc","def","ghi"};
const size_t data_size = sizeof(data) / sizeof(data[0]);
vector<string> stringVector(data, data + data_size);
string joinedString = boost::algorithm::join(stringVector, "-");
Just for reference, there is currently a proposal for std::join, which you can check out here.
But since you have boost available, you can use boost::algorithm::join, which takes a sequence of strings and a separator, like so:
#include <iostream>
#include <string>
#include <vector>
#include <boost/algorithm/string/join.hpp>
int main() {
std::vector<std::string> words;
words.push_back("abc");
words.push_back("def");
words.push_back("ghi");
std::string result = boost::algorithm::join(words, "-");
std::cout << result << std::endl;
}
Prints:
abc-def-ghi
Another option using only the STL is:
std::ostringstream result;
if (my_vector.size()) {
std::copy(my_vector.begin(), my_vector.end()-1,
std::ostream_iterator<string>(result, "-"));
result << my_vector.back();
}
return result.str()
I am building a command line tool and at the beginning whole line is a string. How could I convert:
string text = "-f input.gmn -output.jpg";
into
const char *argv[] = { "ProgramNameHere", "-f", "input.gmn", "-output.jpg" };
If I had to use getopt, and I knew I was starting with a white-space separated std::string, I'd do this:
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <cassert>
#include <cstring>
int main() {
https://stackoverflow.com/questions/236129/how-to-split-a-string-in-c
// My input
std::string sentence = "-f input.gmn -output.jpg";
// My input as a stream
std::istringstream iss(sentence);
// Create first entry
std::vector<std::string> tokens;
tokens.push_back("ProgramNameHere");
// Split my input and put the result in the rest of the vector
std::copy(std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>(),
std::back_inserter(tokens));
// Now we have vector<string>, but we need array of char*. Convert to char*
std::vector<char *> ptokens;
for(auto& s : tokens)
ptokens.push_back(&s[0]);
// Now we have vector<char*>, but we need array of char*. Grab array
char **argv = &ptokens[0];
int argc = ptokens.size();
// Use argc and argv as desired. Note that they will become invalid when
// *either* of the previous vectors goes out of scope.
assert(strcmp(argv[2], "input.gmn") == 0);
assert(argc == 4);
}
See also: Split a string in C++?
Postscript: In the solution I provided, I used two language features introduced in C++2011: range-based for loops and type inference.
This code fragment will only compile if your compiler supports thew new features:
for(auto& s : tokens)
ptokens.push_back(&s[0]);
If you have an older C++ compiler, you might need to rewrite it using C++2003 features:
for(std::vector<string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
ptokens.push_back(it->c_str());
or
for(std::vector<string>::size_type i = 0; i < tokens.size(); ++i)
ptokens.push_back(tokens[i].c_str());
I would recommend using boost::program_options to parse your program's arguments.
Otherwise if you are using MSVC, you might want to use the built-in __argc and __argv.
There is no portable way to get your program's image name, so you cannot get that information out of nowhere if you dropped it in the first place by discarding your original argv argument.
You could use the C strtok function to split your arguments ... actually scratch that, just use boost::algorithm::split with any_of(' ').