I have done a lot of reading on this topic online, and cannot figure out if my code is working. i am working on my phone with the c4droid app, and the debugger is nearly useless as far as i can tell.
as the title says, i need to separate 2 words out of one input. depending on what the first word is, the second may or may not be used. if i do not need the second word everything is fine. if i need and have the second word it works, or seems to. but if i need a second word but only have the first it compiles, but crashes with an out of range exception.
ActionCommand is a vector of strings with 2 elements.
void splitstring(std::string original)
{
std::string
std::istringstream OrigStream(original);
OrigStream >> x;
ActionCommand.at(0) = x;
OrigStream >> x;
ActionCommand.at(1) = x;
return;
}
this code will separate the words right?
any help would be appreciated.
more of the code:
called from main-
void DoAction(Character & Player, room & RoomPlayerIn)
{
ParseAction(Player, GetAction(), RoomPlayerIn);
return;
}
std::string GetAction()
{
std::string action;
std::cout<< ">";
std::cin>>action;
action = Lowercase(action);
return action;
}
maybe Lowercase is the problem.
std::string Lowercase(std::string sourceString)
{
std::string destinationString;
destinationString.resize(sourceString.size());
std::transform(sourceString.begin(), sourceString.end(), destinationString.begin(), ::tolower);
return destinationString;
)
void ParseAction(Character & Player, std::string CommandIn, room & RoomPlayerIn)
(
std::vector<std::string> ActionCommand;
splitstring(CommandIn, ActionCommand);
std::string action = ActionCommand.at(0);
if (ActionCommand.size() >1)
std::string action2 = ActionCommand.at(1);
skipping some ifs
if (action =="wield")
{
if(ActionCommand.size() >1)
DoWield(action2);
else std::cout<<"wield what??"<<std::endl;
return;
}
and splitstring now looks like this
void splitstring(std::string const &original, std::vector<std::string> &ActionCommand)
{
std::string x;
std::istringstream OrigStream(original);
if (OrigStream >>x)
ActionCommand.push_back(x);
else return;
if (OrigStream>>x)
ActionCommand.push_back(x);
return;
}
#include <sstream>
#include <vector>
#include <string>
std::vector<std::string> ActionCommand;
void splitstring(std::string const &original)
{
std::string x;
std::istringstream OrigStream{ original };
if(OrigStream >> x)
ActionCommand.push_back(x);
else return;
if(OrigStream >> x)
ActionCommand.push_back(x);
}
Another idea would be to use the standard library. You can split a string into tokens (using spaces as dividers) with the following function:
#include <string>
#include <vector>
#include <sstream>
#include <iterator>
inline auto tokenize(const std::string &String)
{
auto Stream = std::stringstream(String);
return std::vector<std::string>{std::istream_iterator<std::string>{Stream}, std::istream_iterator<std::string>{}};
}
Here, the result is created in place by using an std::istream_iterator, which basically stands in for the >> operation in your example.
Warning:
This code needs at least c++11 to compile.
Related
Here is the question:
Create a function that takes an array of names and returns an array where only the first letter of each name is capitalized.
example
capMe(["mavis", "senaida", "letty"]) ➞ ["Mavis", "Senaida", "Letty"]
And the code I wrote to answer this question:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void capme(vector<string> name)
{
char ch;
for(int i = 0; i < name[i].size(); i++)
{
putchar(toupper(name[i][0]));
cout << name[i] << endl;
}
}
int main()
{
vector <string> name = {"mavis", "senaida", "letty"};
capme(name);
return 0;
}
As you can see, it prints "Mmavis", "Ssenaida", "Lletty", which is wrong. Can you guys help me in answering this question as I don't know how?
To change the input argument, we have two choice: make the argument mutable reference, or add a return type, here I choose the first one.
putchar can be used to print only one character, it recommended to use cout to print a string, possible solutions:
with traditional loop: capme
with range for-loop since c++11 : capme2
with stl algorithm transform: capme3
Don't forget to check if the string element is empty, or you may crash while accessing the first character.
To obey the single-responsibility principle (SRP), it's better to print the string vector out of the capme function.
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void capme(vector<string>& name) {
for (int i = 0; i < name[i].size(); i++) {
if (name[i].empty()) continue;
name[i][0] = toupper(name[i][0]);
}
}
void capme2(vector<string>& names) {
for (auto& name : names) {
if (name.empty()) continue;
name[0] = toupper(name[0]);
}
}
void capme3(vector<string>& names) {
std::transform(names.begin(), names.end(), names.begin(), [](auto& s) {
return s.empty() ? s : (s[0] = toupper(s[0]), s);
});
}
Online demo
You have used the wrong function. What you need is a replacement and not a prepend. Try using std::string::operator[] to access the first element of the words in the vector. This is how I would write this code:
std::vector<string> capitaliseInitLetter(std::vector<string> vec) {
for (auto& word : vec) {
word[0] -= 32; //add a check to see if the letter is already capital
}
return vec;
}
The above code is just an example which assumes that the input is valid. You'll have to add checks and exception handling for invalid inputs on your own. (Hint: Take a look at #prehistoricpenguin's answer)
You are calling putchar() which writes a character to standard output, and in this case is printing the first letter of each string in name as uppercase, then you are writing the entire string to standard output immediately after.
Additionally, your function does not meet the requirements you stated above saying it should return an array where the strings have the first letter capitalized.
What you could do is change the signature of capme() to return a std::vector<std::string>, and perhaps utilize the for_each() function to handle changing the first letter of each string in your vector then return it.
For reference:
#include <vector>
#include <string>
#include <algorithm>
#include <cctype>
std::vector<std::string> capme(std::vector<std::string> name)
{
std::for_each(name.begin(), name.end(), [](std::string &s) {
s[0] = toupper(s[0]);
});
return name;
}
Or as kesarling suggested, a simple for each loop:
std::vector<std::string> capme(std::vector<std::string> name)
{
for (auto& s : name) {
s[0] = toupper(s[0]);
}
return name;
}
I am implementing the history command in my own shell, in C++. I am writing it in NonCanonicalMode. I have implemented the up arrow key and down arrow key as well as backspace. I do not know how to start history. Should I use a built in function from one of the C++ libraries?
----EDit
char *buf;
rl_bind_key('\t',rl_abort);//disable auto-complete
while((buf = readline("\n >> "))!=NULL)
{
if (strcmp(buf,"quit")==0)
break;
printf("[%s]\n",buf);
if (buf[0]!=0)
add_history(buf);
}
I have not used NonCanonicalMode but here is how I implemented readline's history in one of my projects.
Maybe it will be of some use to you:
#include <string>
#include <memory>
#include <iostream>
#include <algorithm>
#include <readline/readline.h>
#include <readline/history.h>
// clean up user input by deleting spaces from each end
inline std::string& trim(std::string& s, const char* t = " \t")
{
s.erase(s.find_last_not_of(t) + 1);
s.erase(0, s.find_first_not_of(t));
return s;
}
// smart pointer to clean up memory
// allocated by readline
struct malloc_deleter
{
template <class T>
void operator()(T* p) { std::free(p); }
};
typedef std::unique_ptr<char, malloc_deleter> cstring_uptr;
int main()
{
// this directory needs to exist beforehand
const std::string config_dir = "/home/wibble/.prog";
using_history();
read_history((config_dir + "/.history").c_str());
std::string shell_prompt = "> ";
cstring_uptr input;
std::string line, prev;
input.reset(readline(shell_prompt.c_str()));
// copy input into a std::string
while(input && trim(line = input.get()) != "exit")
{
if(!line.empty())
{
// only add line to history if it is different
// from previous line
if(line != prev)
{
add_history(line.c_str());
write_history((config_dir + "/.history").c_str());
prev = line;
}
// process the input
std::reverse(line.begin(), line.end());
// give relevant output
std::cout << "reply: " << line << '\n';
}
input.reset(readline(shell_prompt.c_str()));
}
}
I don't like that I need to call readline() in two places but I wasn't able to figure how to re-write the loop to avoid it. Maybe I'm missing something simple?
It uses a smart pointer std::unique_ptr with a custom deleter to clean up the buffers that readline allocates using malloc().
I am currently trying to write code for finding unique lines as well as unique words. I have my attempted code below for unique lines but it is not calculating correctly when it comes to unique lines. I believe it is ignoring enters and still counting lines that contain letters, words, sentences, etc. So I need help in figuring out what to add that it will count enters (blank lines) as lines and not count any extra lines that are the same. I know this is happening because I have tested a few different lines. As for the unique words, I don't even know how to get started :/
unsigned long countULines(const string& s)
{
set<string> wl;
string ulines;
for(int ul = 0; ul < s.size(); ul++){
wl.insert(ulines);
}
return wl.size();
}
This will work for you:
#include <iostream>
#include <sstream>
#include <string>
#include <set>
size_t countUniqueLines(const std::string& s)
{
std::set<std::string> uniqueLines;
std::istringstream is(s);
std::string line;
while (std::getline(is, line)) {
if (!line.empty() && line[line.size() - 1] == '\r') {
line.erase(line.size() -1);
}
uniqueLines.insert(line);
}
return uniqueLines.size();
}
int main()
{
const std::string myLines = "four\none\n\ntwo\nthree\nthree\n\n";
std::cout << countUniqueLines(myLines) << std::endl;
}
I have a comma separated integers and I want to store them in std::vector<int>. Currently I am manually doing it. Is there any built-in function which did the above functionality?
Edit:
I was in hurry and forget to put full details
Actually i have string (to be exact Unicode string) containing CSvs e.g. "1,2,3,4,5"
Now i want to store them in std::vector<int> so in above case my vector would have five elements pushed into it. Currently i am doing this by manual but its slow as well as there is lot of mess with that code
It's probably not be the most efficient way, but here's a way to do it using the TR1 regex functionality (I also use C++0x lambda syntax in this sample, but obviously it could also be done without that):
#include <iostream>
#include <algorithm>
#include <vector>
#include <regex>
#include <iterator>
#include <cstdlib>
std::vector<int> GetList(const std::wstring &input)
{
std::vector<int> result;
std::wsregex_iterator::regex_type rex(L"(\\d+)(,|$)");
std::wsregex_iterator it(input.begin(), input.end(), rex);
std::transform(it, std::wsregex_iterator(), std::back_inserter(result),
[] (const std::wsregex_iterator::value_type &m)
{ return std::wcstol(m[1].str().c_str(), nullptr, 10); });
return result;
}
You can do this using purely in STL for simplicity (easy to reading, no complex libs needed), which will be fast for coding, but not the fastest in terms of execution speed (though you can probably tweak it a little, like pre-reserving space in the vector:
std::vector<int> GetValues(std::wstring s, wchar_t delim)
{
std::vector<int> v;
std::wstring i;
std::wstringstream ss(s);
while(std::getline(ss,i,delim))
{
std::wstringstream c(i);
int x;
c >> x;
v.push_back(x);
}
return v;
}
(no forwarding(&&) or atoi to keep the code portable).
Sadly, the STL doesn't allow you to split a string on a separator. You can use boost to do it though: (requires a recent C++ compiler such as MSVC 2010 or GCC 4.5)
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
using namespace std;
int main(int argc, char** argv)
{
string input = "1,2,3,4";
vector<string> strs;
boost::split(strs, input, boost::is_any_of(","));
vector<int> result;
transform(
strs.begin(), strs.end(), back_inserter(result),
[](const string& s) -> int { return boost::lexical_cast<int>(s); }
);
for (auto i = result.begin(); i != result.end(); ++i)
cout << *i << endl;
}
The quick and dirty option is to use the C string library strtok() function, and atoi():
void Split(char * string, std::vector<int>& intVec)
{
char * pNext = strtok(string, ",");
while (pNext != NULL)
{
intVec.push_back(atoi(pNext));
pNext = strtok(NULL, ",");
}
}
Insert your own input data validation as required.
See:
http://www.cplusplus.com/reference/clibrary/cstring/strtok/
http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/
As well as the wide string versions:
http://msdn.microsoft.com/en-us/library/2c8d19sb%28v=vs.71%29.aspx
http://msdn.microsoft.com/en-us/library/aa273408%28v=vs.60%29.aspx
EDIT:
Note that strtok() will modify your original string, so pass a copy if need be.
Try this:
It will read any type (that can be read with >>) separated by any char (that you choose).
Note: After the object is read there should can only be space between the object and the separator. Thus for things like ObjectSepReader<std::string, ','> it will read a word list separated by ','.
This makes it simple to use our standard algorithms:
#include <vector>
#include <sstream>
#include <iostream>
#include <iterator>
#include <algorithm>
int main()
{
std::stringstream data("1,2,3,4,5,6,7,8,9");
std::vector<int> vdata;
// Read the data from a stream
std::copy(std::istream_iterator<ObjectSepReader<int, ','> >(data),
std::istream_iterator<ObjectSepReader<int, ','> >(),
std::back_inserter(vdata)
);
// Copy data to output for testing
std::copy(vdata.begin(), vdata.end(), std::ostream_iterator<int>(std::cout," "));
}
The secret class to make it work.
template<typename T,char S>
struct ObjectSepReader
{
T value;
operator T const&() const {return value;}
};
template<typename T,char S>
std::istream& operator>>(std::istream& stream, ObjectSepReader<T,S>& data)
{
char terminator;
std::string line;
std::getline(stream, line, S);
std::stringstream linestream(line + ':');
if (!(linestream >> data.value >> terminator) || (linestream.tellg() != line.size()+1) || (terminator != ':'))
{ stream.setstate(std::ios::badbit);
}
return stream;
}
Personally I'd make a structure and have the vector contain instances of the struct.
Like so:
struct ExampleStruct
{
int a;
int b;
int c;
};
vector<ExampleStruct> structVec;
How about this?
#include <string>
#include <vector>
#include <functional>
#include <algorithm>
#include <iostream>
struct PickIntFunc
{
PickIntFunc(std::vector<int>& vecInt): _vecInt(vecInt),_pBegin(0){}
char operator () (const char& aChar)
{
if(aChar == ',' || aChar == 0)
{
_vecInt.push_back(atoi(std::string(_pBegin,&aChar).c_str()));
_pBegin = 0;
}
else
{
if(_pBegin == 0)
{
_pBegin = &aChar;
}
}
return aChar;
}
const char* _pBegin;
std::vector<int>& _vecInt;
};
int _tmain(int argc, _TCHAR* argv[])
{
std::vector<int> vecInt;
char intStr[] = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20";
std::for_each(intStr,intStr+sizeof(intStr),PickIntFunc(vecInt));
// Now test it
std::for_each(vecInt.begin(),vecInt.end(), [] (int i) { std::cout << i << std::endl;});
return 0;
}
I love how in python I can do something like:
points = []
for line in open("data.txt"):
a,b,c = map(float, line.split(','))
points += [(a,b,c)]
Basically it's reading a list of lines where each one represents a point in 3D space, the point is represented as three numbers separated by commas
How can this be done in C++ without too much headache?
Performance is not very important, this parsing only happens one time, so simplicity is more important.
P.S. I know it sounds like a newbie question, but believe me I've written a lexer in D (pretty much like C++) which involves reading some text char by char and recognizing tokens,
it's just that, coming back to C++ after a long period of python, just makes me not wanna waste my time on such things.
I`d do something like this:
ifstream f("data.txt");
string str;
while (getline(f, str)) {
Point p;
sscanf(str.c_str(), "%f, %f, %f\n", &p.x, &p.y, &p.z);
points.push_back(p);
}
x,y,z must be floats.
And include:
#include <iostream>
#include <fstream>
All these good examples aside, in C++ you would normally override the operator >> for your point type to achieve something like this:
point p;
while (file >> p)
points.push_back(p);
or even:
copy(
istream_iterator<point>(file),
istream_iterator<point>(),
back_inserter(points)
);
The relevant implementation of the operator could look very much like the code by j_random_hacker.
The C++ String Toolkit Library (StrTk) has the following solution to your problem:
#include <string>
#include <deque>
#include "strtk.hpp"
struct point { double x,y,z; }
int main()
{
std::deque<point> points;
point p;
strtk::for_each_line("data.txt",
[&points,&p](const std::string& str)
{
strtk::parse(str,",",p.x,p.y,p.z);
points.push_back(p);
});
return 0;
}
More examples can be found Here
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm> // For replace()
using namespace std;
struct Point {
double a, b, c;
};
int main(int argc, char **argv) {
vector<Point> points;
ifstream f("data.txt");
string str;
while (getline(f, str)) {
replace(str.begin(), str.end(), ',', ' ');
istringstream iss(str);
Point p;
iss >> p.a >> p.b >> p.c;
points.push_back(p);
}
// Do something with points...
return 0;
}
This answer is based on the previous answer by j_random_hacker and makes use of Boost Spirit.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <boost/spirit.hpp>
using namespace std;
using namespace boost;
using namespace boost::spirit;
struct Point {
double a, b, c;
};
int main(int argc, char **argv)
{
vector<Point> points;
ifstream f("data.txt");
string str;
Point p;
rule<> point_p =
double_p[assign_a(p.a)] >> ','
>> double_p[assign_a(p.b)] >> ','
>> double_p[assign_a(p.c)] ;
while (getline(f, str))
{
parse( str, point_p, space_p );
points.push_back(p);
}
// Do something with points...
return 0;
}
Fun with Boost.Tuples:
#include <boost/tuple/tuple_io.hpp>
#include <vector>
#include <fstream>
#include <iostream>
#include <algorithm>
int main() {
using namespace boost::tuples;
typedef boost::tuple<float,float,float> PointT;
std::ifstream f("input.txt");
f >> set_open(' ') >> set_close(' ') >> set_delimiter(',');
std::vector<PointT> v;
std::copy(std::istream_iterator<PointT>(f), std::istream_iterator<PointT>(),
std::back_inserter(v)
);
std::copy(v.begin(), v.end(),
std::ostream_iterator<PointT>(std::cout)
);
return 0;
}
Note that this is not strictly equivalent to the Python code in your question because the tuples don't have to be on separate lines. For example, this:
1,2,3 4,5,6
will give the same output than:
1,2,3
4,5,6
It's up to you to decide if that's a bug or a feature :)
You could read the file from a std::iostream line by line, put each line into a std::string and then use boost::tokenizer to split it. It won't be quite as elegant/short as the python one but a lot easier than reading things in a character at a time...
Its nowhere near as terse, and of course I didn't compile this.
float atof_s( std::string & s ) { return atoi( s.c_str() ); }
{
ifstream f("data.txt")
string str;
vector<vector<float>> data;
while( getline( f, str ) ) {
vector<float> v;
boost::algorithm::split_iterator<string::iterator> e;
std::transform(
boost::algorithm::make_split_iterator( str, token_finder( is_any_of( "," ) ) ),
e, v.begin(), atof_s );
v.resize(3); // only grab the first 3
data.push_back(v);
}
One of Sony Picture Imagework's open-source projects is Pystring, which should make for a mostly direct translation of the string-splitting parts:
Pystring is a collection of C++ functions which match the interface and behavior of python’s string class methods using std::string. Implemented in C++, it does not require or make use of a python interpreter. It provides convenience and familiarity for common string operations not included in the standard C++ library
There are a few examples, and some documentation
all these are good examples. yet they dont answer the following:
a CSV file with different column numbers (some rows with more columns than others)
or when some of the values have white space (ya yb,x1 x2,,x2,)
so for those who are still looking, this class:
http://www.codeguru.com/cpp/tic/tic0226.shtml
is pretty cool... some changes might be needed