I am new to c++ and I am a bit lost here!
I am trying to read the following line
* - 6 + x -6 - - 9 6 * 0 c
and I am iterating through the characters using
for (std::string line; std::getline(std::cin, line);) {
for(auto c : line){
if(c != ' ')
}
}
now I am expecting to get "-6" at the sixth iteration but a I am getting "-" and then at the next iteration I am getting 6, I need to get "-6"! any help!
In the outer for loop, you are infinitely looping until getline() evaluates to false. In the inner for loop, you are looping through each character in line.
I don't know of any elegant syntax to parse a string into tokens like it seems you are trying to do. One way is to utilise std::stringstream and put things into an array or vector. Below is a sample main I wrote demonstrating how versatile this is:
#include <iostream>
#include <sstream>
#include <vector>
int main() {
std::string line;
while ( true ) {
std::cout << "Enter parse string: ";
std::getline( std::cin, line );
if ( line == "Stop" ) break; // Enter stop to quit program
std::stringstream ss( line ); // constructor sets 'line' as contents of the stream
std::vector <std::string> tokens; // where our tokenised input is stored
std::string temp; // where current token is temporarily loaded before it's put into the vector
while( getline( ss, temp, ' ' ) ) // extract characters from ss until a space is found and stores them in temp
tokens.push_back( temp ); // put the token formed by getline in our vector, from back so order is kept
/*
Now you can use the vector as you would like. Below the contents are printed.
*/
for ( auto& x : tokens ) // for each token in 'tokens' vector,
std::cout << x << ' '; // print out token with a space at the end
std::cout << "\n\n";
for ( int i = 0; i < tokens.size(); i++ ) // for each token in 'tokens' vector,
std::cout << i << ": " << tokens[i] << '\n'; // print out index number and token
std::cout << std::endl; // newline & flush
}
return 0;
}
If you want to abstract the details away, you could always wrap the std::stringstream stuff into a function that takes in a string and returns a std::vector<std::string>. Below I demonstrate what that would look like:
#include <iostream>
#include <sstream>
#include <vector>
std::vector <std::string> toParsedVector( std::string input ) {
std::stringstream ss( input ); // constructor sets 'line' as contents of the stream
std::vector <std::string> tokens; // where our tokenised input is stored
std::string temp; // where current token is temporarily loaded before it's put into the vector
while( getline( ss, temp, ' ' ) ) // extract characters from ss until a space is found and stores them in temp
tokens.push_back( temp ); // put the token formed by getline in our vector, from back so order is kept
return tokens;
}
int main() {
std::string line;
while ( true ) {
std::cout << "Enter parse string: ";
std::getline( std::cin, line );
if ( line == "Stop" ) break; // Enter stop to quit program
std::vector <std::string> tokens = toParsedVector( line ); // create vector
// Now you can use the vector (tokens) as you would like. Below the contents are printed.
for ( auto& x : tokens ) // for each token in 'tokens' vector,
std::cout << x << ' '; // print out token with a space at the end
std::cout << "\n\n";
for ( int i = 0; i < tokens.size(); i++ ) // for each token in 'tokens' vector,
std::cout << i << ": " << tokens[i] << '\n'; // print out index number and token
std::cout << std::endl; // newline & flush
}
return 0;
}
I hope this helps; might not be the most elegant solution, but it is very versatile.
That's the first if, which I don't want my code to enter second time, so I added a gate. I have another if, which has as a condition the counter to be equal to 3, so you can imagine that if I don't have a gate for the first if, my code will keep entering the first if, as 2 comes before 3 always. Now what's my problem... Whenever I add the condition with the gate and the line (gate=0) inside the if my code goes on an infinity loop. Also, if I set the gate in a different value than 1, I get the same infinity loop. Please help me.
EDIT: Below you can find a better example of my code in order to help me. Thank you :)
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream file;
file.open ("file.txt");
ofstream file1;
file1.open ("file1.txt");
char counter = 0;
char ch;
char x;
string word;
string word2;
string word3;
int gate = 1;
word.clear();
while (file >> std::noskipws >> ch) {
if (ch == ' ') {
++counter;
}
else if (ch != ' ') {
counter = 0;
}
if ( (counter == 2) && (gate == 1) ) {
gate = 0;
x = file.get();
while ( x != ' ' ) {
word = word + x;
x = file.get();
}
counter = 1;
word2 = word;
word.clear();
file2 << word1 << " ";
}
if (counter == 3) {
x = file.get();
while ( x != ' ' ) {
word = word + x;
x = file.get();
}
counter = 1;
word3 = word;
word.clear();
file2 << word3 << endl;
word3.clear();
gate = 1;
}
if (file.eof()) {
break;
}
}
file.close();
file1.close();
return 0;
}
Your endless loop is probably here:
x = file.get();
while ( x != ' ' ){
word = word + x;
x = file.get();
}
If you reach the end of the input stream, x will become EOF, which is different from space and thus the while-loop will never be left. Do check for EOF or check the eofbit from the istream (file).
I am trying to parse a command line string, at every white space putting into consideration the string has words between quotation marks. I want to store whatever is between 2 quotation marks as 1 index in a vector.
vector<string> words;
stringstream ss(userInput);
string currentWord;
vector<string> startWith;
stringstream sw(userInput);
while (getline(sw, currentWord, ' '))
words.push_back(currentWord);
while (getline(ss, currentWord, '"'))
startWith.push_back(currentWord); //if(currentWord.compare("")){ continue;}
for (int i = 0; i < startWith.size(); i++)
curr
if(currentWord.compare("")){ continue;}
cout << " Index "<< i << ": " << startWith[i] << "\n";
It is not clear what you're trying to do. Here's a starting point (run it):
#include <iostream>
#include <sstream>
#include <string>
std::istream& get_word_or_quote( std::istream& is, std::string& s )
{
char c;
// skip ws and get the first character
if ( !std::ws( is ) || !is.get( c ) )
return is;
// if it is a word
if ( c != '"' )
{
is.putback( c );
return is >> s;
}
// if it is a quote (no escape sequence)
std::string q;
while ( is.get( c ) && c != '"' )
q += c;
if ( c != '"' )
throw "closing quote expected";
//
s = std::move( q );
return is;
}
int main()
{
std::istringstream is {"not-quoted \"quoted\" \"quoted with spaces\" \"no closing quote!" };
try
{
std::string word;
while ( get_word_or_quote( is, word ) )
std::cout << word << std::endl;
}
catch ( const char* e )
{
std::cout << "ERROR: " << e;
}
return 0;
}
The expected output is:
not-quoted
quoted
quoted with spaces
ERROR: closing quote expected
I have been trying to read following data table and create an object for the HUBs(rows) and another object for continent (columns). Since I am not a C++ experienced user I have been facing some difficulties. The data is in following. The number after HUB and the dash shows the order from the hub. The other numbers under each continent are the corresponding cost and tariffs between a HUB and continent. I would like to be able to cout for instance following and get the result which would be 73.
cout << hub(1)->cont(USA)->transport() << endl;
,USA,EUROPE,ASIA
HUB1-12000,,,
Transportation Cost,73,129,141
Tariffs,5,5,1
ShippingType,a,b,c
OtherFees,0.6,0.3,0.8
HUB2-11000,,,
Transportation Cost,57,101,57
Tariffs,7,7,5
ShippingType,b,b,d
OtherFees,0.7,0.3,0.6
Really appreciate your help. Here is what I have tried so far:
void Hub()
{
string file = "/hubs.csv";
// 1-First read the first line and save the continent name
string str, field;
getline( fin, str );
vector<string> contList;
stringstream linestr( str );
while ( linestr.good() )
{
getline( linestr, field, ',' );
string contname;
contList.push_back(contname);
}
// 2-Then read the rest
getline( fin, str );
while ( !fin.eof() ) // Read the whole file
{
stringstream linestr( str );
string contname, order;
if ( qstr[0] == 'HUB1' || qstr[0] == 'HUB2')
{
// Read the name of the hub
getline( linestr, hubname, ',' ); // Read the hub name
getline( linestr, order, ',' ); // Read the order quantityity
int quantity;
istringstream orderstream( order);
orderstream >> quantity;
// Find the hub and add the order to the hub
Hub* hub = glob->FindHubName( hubname ); // this returns a pointer
if ( glob->FindHubName( hubname ) == nullptr )
{
hubNotFound.push_back( hubname );
getline( fin, qstr );
continue;
}
hub->TotalOrder( quantity );
}
else if ( qstr[0] != 'HUB1' || qstr[0] != 'HUB2')
{
// Read costs and tariffs
cout << hub(1)->cont(ASIA)->transport()
}
getline( fin, qstr );
}
fin.close();
}
Something like this:
#include <iostream>
#include <fstream>
#include <boost/tokenizer.hpp>
#include <string>
int main() {
using namespace std;
using namespace boost;
string line, file_contents;
fstream file("test.csv");
if (!file.is_open()) {
cerr << "Unable to open file" << endl;
return 1;
}
getline(file, line);
tokenizer<> tok_head(line);
int n_columns = 0;
for (tokenizer<>::iterator beg=tok_head.begin(); beg!=tok_head.end(); ++beg) {
cout << *beg << '\t';
n_columns++;
}
cout << endl;
while (getline(file, line)) {
file_contents += line;
}
file.close();
tokenizer<> tok(file_contents);
int i = 0;
for (tokenizer<>::iterator beg=tok.begin(); beg!=tok.end(); ++beg, ++i) {
cout << *beg;
if (i % n_columns) {
cout << '\t';
} else {
cout << endl;
}
}
return 0;
}
Makefile
all: t
t: csv.cpp
g++ -I /usr/include/boost csv.cpp -o t
It looks like you must parse each line using different logic, so you should check first column first and using it apply appropriate logic, below is some pseudocode for that:
std::fstream fs("test.txt");
std::string line;
//
// Read line by line
while (std::getline(fs, line)) {
std::istringstream str(line);
std::string rec_type;
// Read record type (your first two lines looks like are of no type?)
if ( !std::getline(str, rec_type, ',') )
continue;
// Decide type of record, and parse it accordingly
if ( rec_type == "Transportation Cost") {
std::string val;
// Read comma delimited values
if ( !std::getline(str, val, ',') )
continue;
int ival1 = std::stoi(val);
if ( !std::getline(str, val, ',') )
continue;
int ival2 = std::stoi(val);
// ...
}
if ( rec_type == "Tariffs") {
std::string val;
if ( !std::getline(str, val, ',') )
continue;
int ival = std::stoi(val);
// ...
}
}
One method is to consider each line as a separate record and object.
Let the objects read their data.
For example:
class Tariff
{
int values[3];
public:
friend std::istream& operator>>(std::istream& input, Tariff& t);
};
std::istream& operator>>(std::istream& input, Tariff& t)
{
// Read and ignore the label "Tariff"
std::string name;
std::getline(input, name, ','); // Read until ',' delimiter.
input >> t.value[0];
// Note: the ',' is not a digit, so it causes an error state,
// which must be cleared.
input.clear();
input >> t.value[1];
input.clear();
input >> t.value[2];
input.clear();
}
Another method is to read the label first, then delegate to a function that reads in the row.
std::string row_text;
std::getline(text_file, row_text); // Read in first line and ignore.
while (std::getline(text_file, row_text))
{
std::istringstream text_stream(row_text);
std::string label;
std::getline(text_stream, label, ','); // Parse the label.
// Delegate based on label.
// Note: can't use switch for strings.
if (label == "Tariffs")
{
Input_Tariff_Data(text_stream);
}
else if (label == "ShippingType")
{
Input_Shipping_Type_Data(text_stream);
}
//...
} // End-while
The if-else ladder can be replaced by a lookup table that uses function pointers. Sometimes the table is easier to read.
typedef void (*P_Input_Processor)(std::istringstream& text_stream);
struct Table_Entry
{
char const * label;
*P_Input_Processor input_processor;
};
//...
const Table_Entry delegation_table[] =
{
{"Tariffs", Input_Tariff_Data},
{"ShippingType", Input_Shipping_Type_Data},
};
const unsigned int entry_quantity =
sizeof(delegation_table) / sizeof(delegation_table[0]);
// ...
std::string row_text;
std::getline(input_file, row_text); // Read and ignore first line.
while (std::getline(input_file, row_text))
{
// Create a stream for parsing.
std::istringstream text_stream(row_text);
// Extract label text
std::string label;
std::getline(text_stream, label, ',');
// Lookup label in table and execute associated function.
for (unsigned int index = 0; index < entry_quantity; ++index)
{
if (label == delegation_table[index].name)
{
// Execute the associated input function
// by derferencing the function pointer.
delegation_table[index](text_stream);
break;
}
}
}
An alternative to the lookup table is to use:
std::map<std::string, P_Input_Processor>
or
std::map<std::string, void (*P_Input_Processor)(std::istringstream&)>
I need to print some data from stream - istringstream ( in main () ).
example:
void Add ( istream & is )
{
string name;
string surname;
int data;
while ( //something )
{
// Here I need parse stream
cout << name;
cout << surname;
cout << data;
cout << endl;
}
}
int main ( void )
{
is . clear ();
is . str ( "John;Malkovich,10\nAnastacia;Volivach,30\nJohn;Brown,60\nJames;Bond,30\n" );
a . Add ( is );
return 0;
}
How to do parsing this line
is.str ("John;Malkovich,10\nAnastacia;Volivach,30\nJohn;Brown,60\nJames;Bond,30\n");"
to name;surname,data?
This is somewhat fragile, but if you know your format is exactly what you posted, there's nothing wrong with it:
while(getline(is, name, ';') && getline(is, surname, ',') && is >> data)
{
is.ignore(); // ignore the new line
/* ... */
}
If you know the delimiters will always be ; and ,, it should be fairly easy:
string record;
getline(is, record); // read one line from is
// find ; for first name
size_t semi = record.find(';');
if (semi == string::npos) {
// not found - handle error somehow
}
name = record.substr(0, semi);
// find , for last name
size_t comma = record.find(',', semi);
if (comma == string::npos) {
// not found - handle error somehow
}
surname = record.substr(semi + 1, comma - (semi + 1));
// convert number to int
istringstream convertor(record.substr(comma + 1));
convertor >> data;