I'm trying to read a string until a ',' character is reached and store what's been read in a new string.
e.g. "5,6"
// Initialise variables.
string coorPair, xCoor, yCoor
// Ask to enter coordinates.
cout << "Input coordinates: ";
// Store coordinates.
cin >> coorPair
// Break coordinates into x and y variables and convert to integers.
// ?
I also need to store the y value in a separate variable.
What is the best way to do this in C++?
Also, is the best way to validate the input to convert to integers and test the values range?
If you have only one comma seperator in string, you can just find where comma first occurs in input and substring input with found position.
Try following:
std::size_t pos = coorPair.find_first_of(","); //Find position of ','
xCoor = coorPair.substr(0, pos); //Substring x position string
yCoor = coorPair.substr(pos + 1); //Substring y position string
int xCoorInt = std::stoi(xCoor); //Convert x pos string to int
int yCoorInt = std::stoi(yCoor); //Convert y pos string to int
The easiest way to do this is to just let operator>> do all the work for you:
int xCoor, yCoor;
char ch;
cout << "Input coordinates: ";
if (cin >> xCoor >> ch >> yCoor)
{
// use coordinates as needed ...
}
else
{
// bad input...
}
You can do this by specifying you delimiter and parsing the string
std::string delimiter = ",";
size_t pos = 0;
std::string token;
while ((pos = coorPair.find(delimiter)) != std::string::npos) {
token = coorPair.substr(0, pos);
std::cout << token << std::endl;
coorPair.erase(0, pos + delimiter.length());
}
std::cout << coorPair << endl;
The last token example in {5,6} the {6} would be in the coorPair.
Another way would be to use std::getline as pointed in the comments:
std::string token;
while (std::getline(coorPair, token, ','))
{
std::cout << token << std::endl;
}
http://www.cplusplus.com/reference/string/string/getline/
I would recommend using getline().
Below is a small example of how I use it. It takes the input from a stream, so you can either use an ifstream as an input, or do what I did below and convert the string to a stream.
// input data
std::string data("4,5,6,7,8,9");
// convert string "data" to a stream
std::istringstream d(data);
// output string of getline()
std::string val;
std::string x;
std::string y;
bool isX = true;
char delim = ',';
// this will read everything up to delim
while (getline(d, val, delim)) {
if (isX) {
x = val;
}
else {
y = val;
}
// alternate between assigning to X and assigning to Y
isX = !isX;
}
Related
How to split the string to two-parts after I assign the operation to math operator? For example 4567*6789 I want to split string into three part
First:4567 Operation:* Second:6789
Input is from textfile
char operation;
while (getline(ifs, line)){
stringstream ss(line.c_str());
char str;
//get string from stringstream
//delimiter here + - * / to split string to two part
while (ss >> str) {
if (ispunct(str)) {
operation = str;
}
}
}
Maybe, just maybe, by thinking this out, we can come up with a solution.
We know that operator>> will stop processing when encounter a character that is not a digit. So we can use this fact.
int multiplier = 0;
ss >> multiplier;
The next characters are not digits, so they could be an operator character.
What happens if we read in a character:
char operation = '?';
ss >> operation;
Oh, I forgot to mention that the operator>> will skip spaces by default.
Lastly, we can input the second number:
int multiplicand = 0;
ss >> multiplicand;
To confirm, let's print out what we have read in:
std::cout << "First Number: " << multiplier << "\n";
std::cout << "Operation : " << operation << "\n";
std::cout << "Second Number: " << multiplicand << "\n";
Using a debugger here will help show what is happening, as each statement is executed, one at at time.
Edit 1: More complicated
You can always get more complicated and use a parser, lexer or write your own. A good method of implementation is to use a state machine.
For example, you would read a single character, then decide what to do with it depending on the state. For example, if the character is a digit, you may want to build a number. For a character (other than white space), convert it to a token and store it somewhere.
There are parse trees and other data structures which can ease the operation of parsing. There are parsing libraries out there too, such as boost::spirit, yacc, bison, flex and lex.
One way is:
char opr;
int firstNumber, SecondNumber;
ss>>firstNumber>>opr>>SecondNumber;
instead of:
while (ss >> str) {
if (ispunct(str)) {
operation = str;
}
}
Or using regex for complex expersions. Here is an example of using regex in math expersions.
If you have a string at hand, you could simply split the string into left and right at the operator position as follows:
char* linePtr = strdup("4567*6789"); // strdup to preserve original value
char* op = strpbrk(linePtr, "+-*");
if (op) {
string opStr(op,1);
*op = 0x0;
string lhs(linePtr);
string rhs(op+1);
cout << lhs << " " << opStr << " " << rhs;
}
A simple solution would be to use sscanf:
int left, right;
char o;
if (sscanf("4567*6789", "%d%c%d", &left, &o, &right) == 3) {
// scan valid...
cout << left << " " << o << " " << right;
}
My proposual is to create to functions:
std::size_t delimiter_pos(const std::string line)
{
std::size_t found = std::string::npos;
(found = line.find('+')) != std::string::npos ||
(found = line.find('-')) != std::string::npos ||
(found = line.find('*')) != std::string::npos ||
(found = line.find('/')) != std::string::npos;
return found;
}
And second function that calculate operands:
void parse(const std::string line)
{
std::string line;
std::size_t pos = delimiter_pos(line);
if (pos != std::string::npos)
{
std::string first = line.substr(0, pos);
char operation = line[pos];
std::string second = line.substr(pos + 1, line.size() - (pos + 1));
}
}
I hope my examples helped you
I have the following code to convert an encrypted ciphertext to a readable hexadecimal format:
std::string convertToReadable(std::string ciphertext)
{
std::stringstream outText;
for(unsigned int i = 0; i < ciphertext.size(); i++ )
outText << std::hex << std::setw(2) << std::setfill('0') << (0xFF & static_cast<byte>(ciphertext[i])) << ":";
return outText.str();
}
The readable result of this function is something as:
56:5e:8b:a8:04:93:e2:f1:5c:20:8b:fd:f5:b7:22:0b:82:42:46:58:9b:d4:c1:8e:ac:62:85:04:ff:7f:c6:d3:
Now I need to do the way back, converting the readable format to the original ciphertext in order to decrypt it:
std::string convertFromReadable(std::string text)
{
std::istringstream cipherStream;
for(unsigned int i = 0; i < text.size(); i++ )
{
if (text.substr(i, 1) == ":")
continue;
std::string str = text.substr(i, 2);
std::istringstream buffer(str);
int value;
buffer >> std::hex >> value;
cipherStream << value;
}
return cipherStream.str();
}
This is not absolutely working, as I´m getting the wrong string back.
How can I fix the convertFromReadable() so that I can have the original ciphertext back ?
Thanks for helping
Here are problems that you should fix before debugging this any further:
cipherStream should be ostringstream, not istringstream
The for loop should stop two characters before the end. Otherwise your substr is going to fail. Make the loop condition i+2 < text.size()
When you read two characters from the input, you need to advance i by two, i.e. add i++ after the std::string str = text.substr(i, 2); line.
Since you want character output, add a cast to char when writing the data to cipherStream, i.e. cipherStream << (char)value
Good you got your code working. Just thought I'd illustrate a slightly simpler, more direct approach using streams without the fiddly index tracking and substr extraction:
std::string convertFromReadable(const std::string& text)
{
std::istringstream iss(text);
std::ostringstream cipherStream;
int n;
while (iss >> std::hex >> n)
{
cipherStream << (char)n;
// if there's another character it better be ':'
char c;
if (iss >> c && c != ':')
throw std::runtime_error("invalid character in cipher");
}
return cipherStream.str();
}
Note that after the last hex value, if there's no colon the if (iss >> c... test will evaluate false as will the while (iss >> ... test, fallingt through to return.
Hi I'm trying to take a c-string from a user, input it into a queue, parse the data with a single space depending on its contents, and output the kind of data it is (int, float, word NOT string).
E.g. Bobby Joe is 12 in 3.5 months \n
Word: Bobby
Word: Joe
Word: is
Integer: 12
Word: in
Float: 3.5
Word: months
Here's my code so far:
int main()
{
const int maxSize = 100;
char cstring[maxSize];
std::cout << "\nPlease enter a string: ";
std::cin.getline(cstring, maxSize, '\n');
//Keyboard Buffer Function
buffer::keyboard_parser(cstring);
return EXIT_SUCCESS;
}
Function:
#include <queue>
#include <string>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <vector>
namespace buffer
{
std::string keyboard_parser(char* input)
{
//Declare Queue
std::queue<std::string> myQueue;
//Declare String
std::string str;
//Declare iStringStream
std::istringstream isstr(input);
//While Loop to Read iStringStream to Queue
while(isstr >> str)
{
//Push onto Queue
myQueue.push(str);
std::string foundDataType = " ";
//Determine if Int, Float, or Word
for(int index = 0; index < str.length(); index++)
{
if(str[index] >= '0' && str[index] <= '9')
{
foundDataType = "Integer";
}
else if(str[index] >= '0' && str[index] <= '9' || str[index] == '.')
{
foundDataType = "Float";
break;
}
else if(!(str[index] >= '0' && str[index] <= '9'))
{
foundDataType = "Word";
}
}
std::cout << "\n" << foundDataType << ": " << myQueue.front();
std::cout << "\n";
//Pop Off of Queue
myQueue.pop();
}
}
}
Right now with this code, it doesn't hit the cout statement, it dumps the core.
I've read about using the find member function and the substr member function, but I'm unsure of how exactly I need to implement it.
Note: This is homework.
Thanks in advance!
UPDATE: Okay everything seems to work! Fixed the float and integer issue with a break statement. Thanks to everyone for all the help!
Your queue is sensible: it contains std::strings. Unfortunately, each of those is initialised by you passing cstring in without any length information and, since you certainly aren't null-terminating the C-strings (in fact, you're going one-off-the-end of each one), that's seriously asking for trouble.
Read directly into a std::string.
std::istreams are very useful for parsing text in C++... often with an initial read of a line from a string, then further parsing from a std::istringstream constructed with the line content.
const char* token_type(const std::string& token)
{
// if I was really doing this, I'd use templates to avoid near-identical code
// but this is an easier-to-understand starting point...
{
std::istringstream iss(token);
int i;
char c;
if (iss >> i && !(iss >> c)) return "Integer";
}
{
std::istringstream iss(token);
float f;
char c; // used to check there's no trailing characters that aren't part
// of the float value... e.g. "1Q" is not a float (rather, "word").
if (iss >> f && !(iss >> c)) return "Float";
}
return "Word";
}
const int maxSize = 100; // Standard C++ won't let you create an array unless const
char cstring[maxSize];
std::cout << "\nPlease enter a string: ";
if (std::cin.getline(cstring, maxSize, '\n'))
{
std::istringstream iss(cstring);
std::string token;
while (iss >> token) // by default, streaming into std::string takes a space-...
token_queue.push(token); // ...separated word at a time
for (token_queue::const_iterator i = token_queue.begin();
i != token_queue.end(); ++i)
std::cout << token_type(*i) << ": " << *i << '\n';
}
I've an input formatted like this:
integer multi-word-string integer
I know the maximum lenght of multi-word-string, however I don't know how many words it contains. How can I read it ?
I'd read the line first and convert the first and last word to integers. Loosely:
std::string line;
std::getline(infile, line);
size_t ofs_front = line.find(' ');
size_t ofs_back = line.rfind(' ');
int front = std::strtol(line.substr(0, ofs_front).c_str(), NULL, 0);
int back = std::strtol(line.substr(ofs_back).c_str(), NULL, 0);
std::string text = line.substr(ofs_front, ofs_back - ofs_front);
You'll have to do some modifications to get rid of spaces (e.g. increment the offsets to gobble up all spaces), and you should add lots of error checking.
If you want to normalize all the interior spaces inside the text, then there's another solution using string streams:
std::vector<std::string> tokens;
{
std::istringstream iss(line);
std::string token;
while (iss >> token) tokens.push_back(token);
}
// process tokens.front() and tokens.back() for the integers, as above
std::string text = tokens[1];
for (std::size_t i = 2; i + 1 < tokens.size(); ++i) text += " " + tokens[i];
Read the first integer. Jump to back of the string and skip digits. Then read an int from this point. The part in the middle is the string. May not be 100% correct but:
char buf[256], *t = buf, *p, str[256];
fread(buf, 1, 256, file);
int s,e;
t += sscanf(buf, "%d", &s);
*p = buf + strlen(buf);
while (isdigit(*p)) p--;
sscanf(p, "%d", &e);
strncpy(str, p, p - t);
This is not as efficient as #KerrekSB's solution, but another way to do this is to extract the first integer, then loop through the rest of the string until you find the second integer.
#include <iostream>
#include <sstream>
int main()
{
std::istringstream ss( "100 4 words to discard 200" );
int first, second;
char buf[1000] = {0};
if( !( ss >> first ) ) {
std::cout << "failed to read first int\n";
return 1;
}
while( !( ss >> second ) || !ss.eof() ) {
if( ss.eof() ) {
std::cout << "failed to read second int\n";
return 1;
}
ss.clear();
if( ss.getline( buf, 1000, ' ' ).bad() ) {
std::cout << "error extracting word\n";
return 1;
}
}
std::cout << "first = " << first << "\n";
std::cout << "second = " << second << "\n";
return 0;
}
I'm reading C++ Primer and working on its exercise. It want me to get user's input and separate by space ' '. So I come up with 2 solution.
First solution:
vector<string> vector1;
string input;
string temp = ""; // temperary hold each word value in input.
string x1 = "";
char x2 = 'b';
x1 += x2;
cout << x1 << endl;
getline(cin, input);
input += " ";
for (string::size_type index = 0; index != input.size(); index++)
{
if (!isspace(input[index]))
{
temp += input[index];
}
else
{
if (temp.size() > 0)
{
vector1.push_back(temp);
temp = "";
}
}
}
Second solution
vector<string> vector1;
string input;
string temp = ""; // temperary hold each word value in input.
string x1 = "";
char x2 = 'b';
x1 += x2;
cout << x1 << endl;
getline(cin, input);
//input += " ";
for (string::size_type index = 0; index != input.size(); index++)
{
if (!isspace(input[index]))
{
temp += input[index];
}
else
{
if (temp.size() > 0)
{
vector1.push_back(temp);
temp = "";
}
}
}
if (!temp.empty())
{
vector1.push_back(temp);
}
The difference between them is first solution is add space to user input while second solution check that I don't add last word or not. I want to know which one is better solution for this problem?
If there're better solutions, please tell me.
I would write this:
std::vector<std::string> data;
std::copy(std::istream_iterator< std::string>(std::cin),
std::istream_iterator< std::string>(),
std::back_inserter(data));
It is almost same as #K-ballo's answer, except that I let std::copy read directly from input stream (i.e std::cin) rather than from std::stringstream.
Demo: http://www.ideone.com/f0Gtc
--
Or you could make use of vector's constructor, avoiding std::copy altogether:
std::vector<std::string> data(std::istream_iterator<std::string>(std::cin),
std::istream_iterator<std::string>());
And you're done! Demo : http://www.ideone.com/Szfes
If you find it difficult to read, then use this instead:
std::istream_iterator<std::string> begin(std::cin), end;
std::vector<std::string> data(begin, end);
Demo : http://www.ideone.com/PDcud
In C++, reading space-separated values is quite easy, and is built into the language. I may be wrong, but it looks like you are over-complicating things.
std::string line;
if (std::getline(std::cin, line)) {
std::stringstream ss(line);
std::vector<std::string> inputs_on_this_line(
std::istream_iterator<std::string>(ss)
, std::istream_iterator<std::string>() );
//do stuff with the strings on this line.
}
You can easily split the input from a stream into string separated values, and insert them into a vector with:
string input;
... get the input, perhaps with getline(cin, input); ...
stringstream input_stream( input );
vector<string> vector1;
std::copy(
(std::istream_iterator< std::string >( input_stream )), std::istream_iterator< std::string >()
, std::back_inserter( vector1 )
);
The std::istream_iterator< std::string > pair will iterate over the input_stream by extracting an std::string at a time (a string is read until a whitespace character is found). std::back_inserter will call push_back into your vector for each of those strings.