Reading a line with integers and a string with spaces - c++

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;
}

Related

what can I get sum that string tokens converted int in C++?

I need to sum 100, 200, 300 in a.txt
a.txt
2323|A|5|0|2|100
2424|B|6|1|3|200
2525|C|7|2|4|300
so I opened this file, and read line by line using getline(), and tokenized.
main.cpp
for (std::string each; std::getline(split, each, split_char); tokens.push_back(each)) {
for (int i = 0; i < tokens.size(); i++) {
std::cout << tokens[i] << std::endl;
tokens.pop_back();
}
}
As expected, that code printed singly of all things.
so I thought using token index to sum values. but my code have error.
"vector subscript out of range" or no compile.
first try
for (std::string each; std::getline(split, each, split_char); tokens.push_back(each)) {
for (int i = 0; i < tokens.size(); i++) {
std::cout << tokens[i] << std::endl;
tokens.pop_back();
std::cout << tokens[5] << std::endl;
std::cout << tokens[11] << std::endl;
std::cout << tokens[17] << std::endl;
int a = 0;
int b = 0;
int c = 0;
int sum = 0;
a = stoi(tokens[5]);
b = stoi(tokens[11]);
c = stoi(tokens[17]);
sum = (a + b + c);
std::cout << sum << std::endl;
}
}
second try
for (std::string each; std::getline(split, each, split_char); tokens.push_back(each)) {
if(tokens.size() > 4) {
for (int k = 0; k < ((tokens.size() - 5) / 6) + 1; k++) {
int sum = 0;
int change = 0;
int j = 0;
j = 6 * k + 5;
change = stoi(tokens[j]);
sum += change;
std::cout << sum << std::endl;
tokens.pop_back();
}
}
}
what should I do sum value? and I'm wondering that tokens.size()`s meaning except meaning "size" because second for statement always get an error if not exactly correcting i < tokens.size()
You are modifying the tokens vector while you are looping through it. Don't do that. You are affecting its size(), which accounts for why you are able to go out of bounds.
You say that you need to sum only the last token of each line. But that is not what your code is trying to do. There is no need for an inner for loop at all. Simply split each line into a local tokens vector and then use tokens.back() to get the last token, eg:
std::string line;
int sum = 0;
while (std::getline(inFile, line))
{
std::istringstream iss(line);
std::vector<std::string> tokens;
std::string token;
while (std::getline(iss, token, '|')) {
tokens.push_back(token);
}
// use tokens as needed...
token = tokens.back();
sum += std::stoi(token);
}
std::cout << sum << std::endl;
Live Demo
I would like to structure my code slightly differently.
Rather than try and do everything in the main function split your code up so that you read each line and validate it is correct:
#include <iostream>
#include <string>
// A structure representing the data we want to parse.
struct DataLine
{
int v1;
char c;
int v2;
int v3;
int v4;
int v5;
// An input operator that will read one line of data.
// If the data is valid will update the variable we are reading into.
friend std::istream& operator>>(std::istream& str, DataLine& data)
{
DataLine tmp;
char s[5];
std::string extra;
if ( str >> tmp.v1 >> s[0] && s[0] == '|'
&& str >> tmp.c >> s[1] && s[1] == '|'
&& str >> tmp.v2 >> s[2] && s[2] == '|'
&& str >> tmp.v3 >> s[3] && s[3] == '|'
&& str >> tmp.v4 >> s[4] && s[4] == '|'
&& str >> tmp.v5
&& std::getline(str, extra) && extra.empty())
{
// all the data was read and the line was valid.
// update the correct variable.
swap(tmp, data);
}
else {
// there was an issue.
// set the stream to bad so that reading will stop.
str.setstate(std::ios::badbit);
}
return str;
}
// Standard swap method.
friend void swap(DataLine& lhs, DataLine& rhs) noexcept
{
using std::swap;
swap(lhs.v1, rhs.v1);
swap(lhs.c , rhs.c );
swap(lhs.v2, rhs.v2);
swap(lhs.v3, rhs.v3);
swap(lhs.v4, rhs.v4);
swap(lhs.v5, rhs.v5);
}
};
Then the loop you use to read the data becomes really trivial to implement.
int main()
{
DataLine data;
int sum = 0;
// Now we can read the data in a simple loop.
while(std::cin >> data) {
sum += data.v5;
}
std::cout << "Sum: " << sum << "\n";
}

Assigning splitted string into substrings

My problem is rather simple yet I can't get my head around it.
I was searching through the internet of course, but all solutions I found were using std::vectors and I'm not allowed to use them.
I have the following string:
std::string str "Tom and Jerry";
I want to split this string using space as a delimiter, and then assign the three words into three different strings.
//this is what I am trying to achieve
std::string substr1 = "Tom";
std::string substr1 = "and";
std::string substr1 = "Jerry";
This is how I am splitting the string by the space as a delimiter:
std::string buf;
std::string background;
std::stringstream ss(str);
while (ss >> buf) {
if (buf == " ")
background = buf; // don't really understand that part
std::cout << "splitted strings: " << buf << std::endl;
}
But I have no idea when and how should I assign the splitted strings into the substr1, substr2, substr3. Would anyone explain how should I throw in the strings assignment part into this?
I have tried some weird stuff like:
std::string substr1, substr2, substr3;
int counter = 1;
while (ss >> buf) {
if (buf == " ")
background = buf; // don't really understand that part
counter = 1;
if (counter == 1) {
substr1 = buf;
std::cout << "substr1 (Tom): " << substr1 << std::endl;
counter++;
}
else if (counter == 2) {
substr2 = buf;
std::cout << "substr2 (and): " << substr2 << std::endl;
counter++;
}
else if (counter == 3) {
substr3 = buf;
std::cout << "substr3 (Jerry): " << substr3 << std::endl;
counter++;
}
Thanks.
You can simply do ss >> substr1; ss >> substr2; ss >> substr3;. The >> operator works exactly with spaces as separator.
Code
in "while" ,when coming a space ,make it a substring before the space and the "tom and jerry" has 2 space so it was splitted to two words. ss>>buf means input "ss"'s string to buf. so if there comes a spce it can store the word before space.

How to read such inputs ? A,1,BC,2

I've started working on hackerrank/hackerearth like sites. There i've found one problem fetching input. Its easy in Java.
Consider a very simple problem of reading input and displaying it :
Flow is like :
read A
repeat A times
read X1,N1,X2,N2 ; where Xi is any string, Ni is any integer.
display X1+X2, N1+N2
i don't know how to read X1,N1,X2,N2 where X1 and X2 are strings, i've also tried but problem is that when i read first string it reads entire line for eg when i read string it supposed to be X1 but it is X1,N1,X2,N2. code that i used is
scanf("%s,%d,%s,%d", x1, &n1, x2, &n2)
thanks in advance and sorry for my bad english.
Update#1:
example lines :
3
some,123,thing,456
something,579
a,1,b,2
ab,3
hello,100,world,100
helloworld,200
I think you are looking for something like this:
int number_of_inputs;
std::cin >> number_of_inputs;
for (int iteration = 0; iteration < number_of_inputs; ++iteration){
int integer1, integer2;
string string1, string2, stupid_comma;
std::cin >> string1 >> stupid_comma >> integer1 >> stupid_comma >> string2 >> stupid_comma >> integer2;
std::cout << string1 << " + " << string2 << " = " << integer1+integer2 << std::endl;
}
edit2: After op provides input, my code is not correct. Check this answer: Parsing a comma-delimited std::string
edit3: alternative split method op requires:
std::vector<std::string> split(const std::string &text, char sep, int num)
{
std::vector<std::string> tokens;
std::size_t start = 0, end = 0;
int elements = 0;
while ((end = text.find(sep, start)) != std::string::npos) {
if ( elements == num) break;
tokens.push_back(text.substr(start, end - start));
start = end + 1;
elements++;
}
tokens.push_back(text.substr(start));
return tokens;
}
edit4: new code using split function:
int number_of_inputs;
std::cin >> number_of_inputs;
for (int iteration = 0; iteration < number_of_inputs; ++iteration){
std:string line;
cin >> line;
int integer1, integer2;
string string1, string2, stupid_comma;
std::vector<std::string> my_line = split(line, ',', 4);
string1 = my_line[0];
string2 = my_line[2];
integer1 = stoll(my_line[1], nullptr, 10);
integer2 = stoll(my_line[3], nullptr, 10);
std::cout << string1 << " + " << string2 << " = " << integer1+integer2 << std::endl;
}
Here is a solution using std::regex, even though it is longer than the accepted answer, I find it much clearer and more flexible.
#include <iostream>
#include <string>
#include <regex>
using namespace std;
struct MyPair {
string x; int n;
MyPair() {}
MyPair( const string& x, int n )
: x(x), n(n) {}
};
void parse_line( const string& str, MyPair& p1, MyPair& p2 ) {
typedef regex_iterator<string::const_iterator> re_iterator;
typedef re_iterator::value_type re_iterated;
regex re("(\\w+),(\\d+),(\\w+),(\\d+)");
re_iterator rit( str.begin(), str.end(), re );
re_iterator rend;
if ( rit != rend ) {
p1 = MyPair( (*rit)[1], stoi((*rit)[2]) );
p2 = MyPair( (*rit)[3], stoi((*rit)[4]) );
}
}
int main() {
int A = 0;
while ( A <= 0 ) {
cin >> A;
}
string line;
MyPair p1, p2;
for ( int i = 0; i < A; i++ ) {
cin >> line;
parse_line( line, p1, p2 );
cout << (p1.x + p2.x) << " " << (p1.n + p2.n) << endl;
}
}
Note that it uses features from C++11, so in order to compile it using clang++ (for instance), you should do:
clang++ -std=c++11 file.cpp -o file

Split String with math expression

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

c++ convert hexadecimal string with ":" to original "binary" string

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.