I am having some problem when trying to do classes for C++. This is my header file.h:
#include <iostream>
#include <string>
#ifndef MESSAGES__H__
#define MESSAGES__H__
class Message
{
public:
Message(std::string recipient, std::string sender);
void append(std::string text);
std::string to_string() const;
void print() const;
private:
std::string recipient;
std::string sender;
std::string message;
std::string text_input;
char* timestamp;
};
#endif
And when I run the main method, the getline(cin,) is giving me some error message:
int main()
{
vector <Message*> message_list;
Message* message1 = new Message("Student1", "Gabriel");
cout << "Enter message text line, enter . on new line to finish: " << endl;
while(getline(cin, text_input))
{
}
}
The getline method is giving me no instance of overloaded function. Also, from the same line, the text_input is showing identifier is undefined. I thought I declared in .h class already?
Thanks in advance.
Updated portion
Now all the error has been fixed:
vector <Message*> message_list;
Message* message1 = new Message("Saiful", "Gabriel");
cout << "Enter message text line, enter . on new line to finish: " << endl;
while(getline(cin, message1->get_text_input()))
{
if(message1->get_text_input() == ("."))
{
break;
}
else
{
message1->append(message1->get_text_input());
}
}
Inside the while loop, once "." is detected at the beginning of the new line, it supposingly will stop. However, no matter how many times I entered "." at the new line, it just keep prompting. Anybody know why?
To fix "text_input is showing identifier is undefined"
You need to change
while(getline(cin, text_input))
to
while(getline(cin, message1->text_input))
Possibly this will fix the first error.
Try changing your loop like this:
while(getline(cin,&message1->text_input))
{
}
If I remember correctly, getline function looks like this:
getline(isstream& stream, string& string)
It feels as if you are over complicating things. Just use a temporary variable in getline. If the input is a "." then break, otherwise append the line to the Message object. As a consequence, you should be able to remove the text_input member variable from your Message header file.
std::vector<Message*> message_list;
Message* message1 = new Message("Saiful", "Gabriel");
std::cout << "Enter message text line, enter . on new line to finish: " << std::endl;
std::string input = "";
while(getline(std::cin, input))
{
if(input == ".")
{
break;
}
else
{
message1->append(input);
}
}
Related
{
const char* fname = "myfile"; //or string fname ("str.txt") in C++11
string input; //our first look at the C++ string object
fstream myfile; //construct fstream object for file i/o
openfile(myfile, fname, ios_base::out); //open file for output (current contents lost)
std::transform(myfile.open(), myfile.end(), myfile.open(), ::toupper);
while (cout << "Enter a line ('q' to quit): " && getline(cin, input))
{
if (input == "q")
break;
else //exit while loop
myfile << input << endl; //pipe string we read file add new line character
}
myfile.close(); //close our file
std::transform(myfile.open(), myfile.end(), myfile.open(), ::toupper);
openfile(myfile, fname, ios_base::in); //reopen it for input
cout << "\nHere's whats in " << fname << ":\n";
while (getline(myfile, input)) //get and print all the lines
cout << input << endl;
system("pause");
}
Severity Code Description Project File Line Suppression State
Error (active) E0304 no instance of overloaded function "std::basic_fstream<_Elem, _Traits>::open [with _Elem=char, _Traits=std::char_traits<char>]" matches the argument list fileio2 C:\Users\burnsk\source\repos\Hello\fileio2\fileio2.cpp 14
Severity Code Description Project File Line Suppression State
Error (active) E0109 expression preceding parentheses of apparent call must have (pointer-to-) function type fileio2 C:\Users\burnsk\source\repos\Hello\fileio2\fileio2.cpp 14
std::transform(myfile.open()
That's not going to work. std::transform expects an input iterator range, typically some begin/end range. std::fstream::open doesn't return an iterator.
You probably want to look at std::istream_iterator<char>.
We'll do a quick code review of what you've posted. I've removed most of your comments, and we'll actually use that as our first critique. The comments don't help readability at all. You're just restating the line of code, which isn't a great comment. Comments that explain 'why' or fill in the information gaps that the code alone cannot explain are much better.
// Missing includes and main()
// This does not qualify as a Minimal, Reproducible Example
// Specifically, it's not reproducible, as it cannot be copy/pasted
// and ran by the people you want to get help from.
// https://stackoverflow.com/help/minimal-reproducible-example
{
const char* fname = "myfile"; // Hard-coded file name is meh, C-string also
string input; // Prefer this EVERYWHERE
fstream myfile;
openfile(myfile, fname, ios_base::out); // No explanation of this function
// It's not standard, you should
// have posted its code.
// Bad syntax, as explained. Refer to the documentation
// https://en.cppreference.com/w/cpp/algorithm/transform
std::transform(myfile.open(), myfile.end(), myfile.open(), ::toupper);
// Clever, but the user likely doesn't need to be prompted before
// every line. Instead, you could have checked for the end condition.
while (cout << "Enter a line ('q' to quit): " && getline(cin, input))
{
if (input == "q")
break; // This is the line that exits the loop
else //exit while loop
myfile << input << endl; // Premature write
}
myfile.close();
// See above about transform syntax being bad. But why write data
// you don't want, close the file, re-open the file, try to
// re-write the data, and close the file again?
std::transform(myfile.open(), myfile.end(), myfile.open(), ::toupper);
openfile(myfile, fname, ios_base::in); //reopen it for input
cout << "\nHere's whats in " << fname << ":\n";
while (getline(myfile, input)) //get and print all the lines
cout << input << endl;
system("pause");
}
Since you are reading the strings in, and not caring about the original contents, why not manipulate the string to look the way you want, and then write it to the file. It's a lot simpler.
My code below does that, and uses a few other tricks to avoid copy/pasta.
The biggest change is that the user is only told what to do once, and the while() loop Boolean expression grabs the line and ensures it's not the quit condition.
The string is then run through std::transform() and fully capitalized. And then it is written to the file. This writes the data we want one time, instead of writing a bunch of bad data and then doubling back and trying to fix it.
For printing the contents of the file to the screen, we create a new class that holds a string and changes how it can be read in, essentially reading an entire line at a time instead of a word. This is not necessary, but like I said, I put some stuff in to avoid a straight copy/paste/submit situation.
#include <algorithm>
#include <cctype>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
namespace detail {
class Line {
std::string m_line;
public:
friend std::istream& operator>>(std::istream& sin, Line& line) {
std::getline(sin, line.m_line);
return sin;
}
operator std::string() const { return m_line; }
};
} // namespace detail
int main(int argc, char* argv[]) {
if (argc != 2) return 1;
std::ofstream fout(argv[1]);
if (!fout) {
std::cerr << "Error opening file for writing.\n";
return 2;
}
std::cout << "Type lines. Type \"q\" on its own line to quit.\n";
std::string line;
while (std::getline(std::cin, line) && line != "q") {
std::transform(line.begin(), line.end(), line.begin(),
[](const auto& c) { return std::toupper(c); });
fout << line << '\n';
}
fout.close();
std::ifstream fin(argv[1]);
if (!fin) {
std::cerr << "Error opening file for reading.\n";
return 3;
}
std::copy(std::istream_iterator<detail::Line>(fin),
std::istream_iterator<detail::Line>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
fin.close();
}
Output:
❯ ./a.out text.txt
Type lines. Type "q" on its own line to quit.
You'll be back, time will tell
You'll remember that I served you well
Oceans rise, empires fall
We have seen each other through it all
And when push comes to shove
I will send a fully armed battalion to remind you of my love!
q
YOU'LL BE BACK, TIME WILL TELL
YOU'LL REMEMBER THAT I SERVED YOU WELL
OCEANS RISE, EMPIRES FALL
WE HAVE SEEN EACH OTHER THROUGH IT ALL
AND WHEN PUSH COMES TO SHOVE
I WILL SEND A FULLY ARMED BATTALION TO REMIND YOU OF MY LOVE!
Firstly, Happy new year to those who come across my question.
I'm currently learning C++ and I have a class project to complete.
In a nutshell, my code (so far) is supposed to instantiate student objects with details read from a txt file (name, reg-number, and a map of ) and add the student to a list.
I then read a second txt file (consisting of reg-number, course code and mark) and check if there is a match of student reg numbers between whats read and the list.
if there is a match, I should insert the marks read from the txt file into the map (part of the student object), such that afterwards, each student has a map containing the courses taken and marks achieved
I seem to be creating the list fine, then I use a stringstream to read the second file and loop through the list to compare reg-numbers.
if there is a match I then call the add mark method to add marks to the map.
Here's the thing.. If after I completed the map inserting, I loop and print a map of a student, the map is empty. for all students. To confirm this I used map.size().
I have tried many ways to understand and rectify the issue but it seems i'm missing the point of something. Instinct tells me that the add mark method is copying a reference to the variable passed, which is then destroyed by the stringstream in the main method, thus showing no data in the map. unfortunately, I can't change any code within the header files, only implement what's declared.
after reading the std library docs for strings, maps etc, and attempting numerous ways of correcting the behaviour, I'm at a loss.
Any advice would be greatly appreciated so I can continue with the project and better understand what is happening. I have added the files below. there is also a Person base class but I havnt changed this as it consists of only a setter and getter.
Many thanks in advance.
student Header:
#ifndef _STUDENT_H_
#define _STUDENT_H_
#include <string>
#include <map>
#include <stdexcept>
#include "Person.h"
using namespace std;
class NoMarkException: public exception
{
};
class Student: public Person
{ public:
// constructor should initialise name and registration number using arguments
// and initialise marks map to be empty
Student(const string &name, int regNo);
// method to return registration number
int getRegNo() const;
// method to add the mark to the map
// if a mark for the module is already present it should be overwritten
void addMark(const string& module, float mark);
// method to retrieve the mark for a module
// should throw NoMarkException if student has no mark for that module
float getMark(const string &module) const throw (NoMarkException);
private:
int regNo;
map<string, float> marks; // keys are modules, values are marks in range 0.0 to 100.0
// friend function to output details of student to stream
// should output name, regno, and minimum, maximum and average marks on a single line
// if the student has no marks "has no marks" should be output instead of the marks
friend ostream& operator<<(ostream &str, const Student &s);
};
#endif
Student.cpp file:
#include <iostream>
#include "Student.h"
#include "Person.h"
using namespace std;
//constructor makes a student object and initialises the map; marks.
Student::Student(const string &name, int regNo) : Person(name) {
this->name = name;
this->regNo = regNo;
map<string, float> marks;
}
//implemented from the header file. Returns the registration number.
int Student::getRegNo() const {
return regNo;
}
// implemented as per header file request. adds marks to the map. if a mark exists, then it is overwritten.
void Student::addMark(const string &module, float mark) {
marks[module] = mark;
cout << "added: " << marks[module]<< endl;
}
//used to find marks in a map.
float Student::getMark(const string &module) const throw (NoMarkException) {
auto search = marks.find(module);
//line to test the map size after using addMark.
cout << "size after inputted values: "<< marks.size();
return marks[module];
}
main.cpp file
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <list>
#include "Student.h"
using namespace std;
//main method to obtain a file name from a user and read the file.
//method passes each .. to student.cpp
int main() {
//setting up variables required
string fileName;
const string fileEnd = ".txt";
string line;
string line2;
ifstream file;
int reg;
int reg2;
string studName;
string lastName;
float mark;
string module;
list<Student> listOfStudents;
cout << "Please enter a file name to access..." << std::endl;
cin >> fileName;
fileName += fileEnd;
// opening file an reading its contents. values are prepared and sent to the Student constructor. Fills a list
// with student objects created with variables read from the studs.txt file.
//checks file is found and exits the program if not
file.open(fileName);
if (!file) {
cerr << "Unable to open file " << fileName << endl;
exit(1);
}
while (getline (file, line)) {
stringstream stream (line);
stream >> reg >> studName >> lastName;
studName += (' ' + lastName);
cout << "Student: " << studName << " has been created." << endl;
listOfStudents.push_front(Student(studName, reg));
}
file.close();
cout << "The list of students has been created :)" << endl << endl;
cout << "Please enter the name of the next file to open"<< endl;
cout << listOfStudents.size()<<endl;
// opening second file. If file not found, exit with an error code.
// otherwise read each line, separate the three words into variables, then loop through the previously created list
//and look for a match of regNo. if true then call the addMark method to add the module and mark to the student's map.
cin >> fileName;
fileName += fileEnd;
file.open(fileName);
if (!file) {
cerr << "Unable to open file " << fileName << endl;
exit(1);
}
while(getline(file, line))
{
istringstream line_stream(line);
line_stream >> reg2 >> module >> mark;
for(Student stud : listOfStudents){
if(stud.getRegNo() == reg2){//out << "Match reg2: " << reg2 << " with stud: " << stud.getName() <<stud.getRegNo()<< endl;
stud.addMark(module,mark); }
}
}
//testing the get mark method of student class. with a module that is in the map. expecting a result
for(Student s :listOfStudents){
cout << s.getMark("CE151") << endl;
}
return 0;
}
You create a temporary copy of Student each time you're going to addMark, and discard it afterwards. You need not to copy it, but use reference, like this:
for (auto& stud: listOfStudents) /* stud.addMark */
It would also be a good idea to avoid copying Student when you print the results:
for (const auto& stud: listOfStudents) /* stud.getMark */
Aaaand one more thing:
float getMark(const string &module) const throw (NoMarkException);
Dynamic exception specification is deprecated in C++11 and removed in later standards. It is considered to be a bad practice and should be avoided.
The answer by user: grungegurunge is pretty much the one you are looking for. However, after looking at your class. I noticed that in the private section for the member variables you have declared map<string, float> marks which is okay. Yet when I look at your class's constructor after you set the name and regNo it appears that you are declaring another map<string, float> named marks where this one is local to the constructor only and does nothing. You create local memory on the stack with automatic storage, never use it, then it gets destroyed after the object goes out of scope; it is unnecessary to have it declared here.
//constructor makes a student object and initialises the map; marks.
Student::Student(const string &name, int regNo) : Person(name) {
this->name = name;
this->regNo = regNo;
// map<string, float> marks; // this line is not needed.
}
I have an assignment that to create a C++ Tokenizer class with one public method
vector *GetTokens ( void );
The function takes a string in via stdin, tokenizes the string, and returns a vector pointer of the tokens either of size ()=1 or 2. The function needs to throw errors when: there are 0 tokens, more than 2 tokens, when the first token is not a String, or when the first token is not a String and the second is not an Integer.
Code that calls my function from professor:
//////////////////////
For (int i=0; i <5; i++) {
Tokenizer tok;
Vector<string> *test = tok.GetTokens ();
If (test->size ()==1) {
cout << "Yay" << endl;
} else {
cout << "Boo" << endl;
}
///////////////
I have been able to successfully complete this program for proper tokenization. I am also to print out the errors via if...then statements. However, during my errors, the cout << "Yay" or "Boo" still prints out. I need to instead not have this text print out while still allowing the for loop/calling function continue execution.
Is there a way, to use exceptions or errors with asserts in my GetTokens () method to essentially stop execution, print my error text, pass control back to the calling function without printing out any additional text and progress to the next loop cycle?
::::EDIT::::
My Tokenizer.cpp
///////////////////
'//Constructor
Tokenizer::Tokenizer( void ) { }
//This includes my tokenization with my error/exception handling
vector<string> * Tokenizer::GetTokens() {
string strIN;
cout << "> ";
getline(cin, strIN);
vector<string> *tokens = atokenizer(strIN);
//So with one of my constraints, I need to print an error for 0 tokens
try{
if(tokens->size()!=0) {
return tokens;
} else {
throw tokens;
}
}
catch(vector<string> *error) {
cout << "ERROR!" << endl;
return error;
}
}
//This is my tokenization function which parses by whitespace
vector<string> *Tokenizer::atokenizer(string strIN) {
vector<string> *tokens = new vector<string>();
string token="";
int c=0;
bool whiteSpace=true;
//Iterates thru all chars and tokenizes by " "
for(int i=0; i<strIN.size(); i++) {
//Checks if char==1-0,a-z,etc. or is space=" "
if((strIN[i]>=33) && (strIN[i]<=126)) {
//If char, appends to token
token+=strIN[i];
whiteSpace=false;
}
else if((strIN[i]==32) && (whiteSpace==false)) {
//If space, adds token to tokens
tokens->push_back(token);
token="";
c++;
whiteSpace=true;
}
}
//Adds final token
if(token!="") {
tokens->push_back(token);
}
return tokens;
}'
//////////////////
So I figured a different way to solve my question. I couldn't get any of the error handling or exceptions to work. Instead, I just went back to using If...then...else statements to do error checking with cout and then I created functions to control the printout.
void coutOff() {
cout.setstate(std::ios_base::failbit)
}
void coutOn() {
cout.clear();
}
I am making a statistics collector that reads the log of a music player and lets the user show top ten most played etc. As a noob project.
A line from the log looks like: "20:42:03 start E:\ROTATION\A\Håkan Lidbo - Dammlunga.mp3"
I have put this in a string using ifstream and getline.
Then making an array of chars of the string using
const char *charveqtur = newline.c_str();
Then I tried to sort i out with sscanf:
sscanf (charveqtur, "%d:%d:%d\tstart\t%s", &this->hour, &this->minute, &this->second, &this->filename);
The problem is that the filename is cut at the first space. I have also tried using istringstream instead but no breakthrough so far.
Which is the most convinient way of doing this? Thanks.
You can use some input stream to read the first integers and colons, and because the filename is the last entity, you can then use std::getline. However, even if your filename is not the last part, note that std::getline is quite a versatile function that accepts any delimiter.
A more advanced method would be to define your own type for filenames and overload operator>>(std::istream &, T const &) on it.
Here is a complete example using std::getline and stringstream with basic diagnostics and some reformatting:
#include <sstream> // for istringstream
#include <iostream> // for cout and cerr
#include <iomanip> // for setprecision
#include <cmath>
bool read (std::string const &line) {
char c = 0;
double length;
double rating;
std::string title;
std::istringstream ss;
ss.str (line);
ss >> length;
if (!ss.good()) { std::cerr << "invalid length\n"; return false; }
if (ss.get()!=':') { std::cerr << "expected colon\n"; return false; }
ss >> rating;
if (!ss.good()) { std::cerr << "invalid rating\n"; return false; }
if (ss.get()!=':') { std::cerr << "expected colon\n"; return false; }
std::getline (ss, title);
double sink;
std::cout << title << " ("
<< int(length) << ':' << 60*std::modf (length,&sink)
<< " min), your rating: " << rating << '\n';
return true;
}
int main () {
read ("30.25:5:Vivaldi - The four seasons.ogg");
read ("3.5:5:Cannibal Corpse - Evisceration Plague.ogg");
read ("meh");
return 0;
}
Output:
Vivaldi - The four seasons.ogg (30:15 min), your rating: 5
Cannibal Corpse - Evisceration Plague.ogg (3:30 min), your rating: 5
invalid length
Important: When parsing, you are sailing close to the security risks. Always be conscious and sensible and try to use tested and proven libraries where possible. This also implies that you do not use sscanf, which is not typesafe, error-prone and sometimes hard to get right.
Don't use C if you have C++, and used correctly, iostreams are even more convenient than printf/scanf+co.
You could perhaps do something like
int lastpos = 0;
if sscanf (charveqtur, "%d:%d:%d\tstart\t%n", &this->hour,
&this->minute, &this->second,
&lastpos) > 3 && lastpos >0) {
std::string filename = newline.substr(lastpos);
/* do something with filename */
}
void EDataset::PrintErr(const NDataString& ErrMsg){
system("echo " + $ErrMsg + " >> err.txt");
.... code ....
}
It prints blank line as the value of ErrMsg. How come?
As already #gf mentioned in the comment, $ErrMsg is not proper. Also, NDataString definition is not clear.
Assuming there is a way to get string out of NDataString :
void PrintErr(const NDataString& ErrMsg)
{
std::stringstream tempString;
tempString <<"echo ";
//Get the string out of NDataString...
//if ErrMsg was std::string then c_str() will give you const char*
tempString<< ErrMsg.c_str();
tempString<<" >> err.txt";
system(tempString.c_str());
}