read a char with space in C++ [duplicate] - c++

This question already has answers here:
std::cin input with spaces?
(8 answers)
Why does std::getline() skip input after a formatted extraction?
(5 answers)
Closed 8 months ago.
I'm doing a crud in C++, but in the data entry, I can't add space in the name, it bugs, I tried to use ignore and getline, it worked but the first letter disappears when reading the variable, using only getline, it doesn't work
class banca
{
private:
char name[100];
char email[100];
char course_aluno[100];
int semestre;
int id;
public:
friend istream& operator>>(istream& in, banca& p);
friend ostream& operator<<(ostream& out, banca& p);
}
istream& operator>>(istream& in, banca& p)
{
cout << "--------ALUNO--------\n";
cout << "Name: \n" << endl;
cin.ignore();
cin.getline(p.name, 100);
cout << "Email: \n";
cin.ignore();
cin.getline(p.email, 100);
cout << "Course : \n";
cin.ignore();
cin.getline(p.course_aluno, 100);
cout << "Semestre: \n";
in >> p.semestre;
cout << "ID: \n";
in >> p.id;
}

You are basically running into an old problem, when working with input functions.
You need to learn about the difference between formatted and unformatted input.
The extraction and inserter operators >> and << are formatted input functions. They read or write a sequence of characters and format them. Example:
int i = 42;
std::cout << i;
This is an example of formatted IO. Although 42 has the binary representation of 101010 you will see the character sequence "42" on the screen.
Same is valid vice versa. If you want to read a number from the user, you typically use something like
int i;
std::cin >> i;
And, although you enter the characters '4' and '2' and ENTER ('\n') in the terminal, you will get the value 42 in your variable "i". The ENTER ('\n') is still in the input stream and has not yet been consumed. This is an additional problem that I will explain in a minute.
If you want to read a string, like "Peter Parker", and use
std::string name{};
std::cin >> name;
You will only get "Peter", because the formatted input functions (per default) stop converting after they hit a white space. So, after "Peter" has been read. The whitespace will not be consumed and will be still in the input stream.
For this reason you will use std::getline to read a string. This will not stop at a whitespace, but at a delimiter, per default at '\n'. So it will read a complete line.
From reading above, you may now see the problem. If there is a transition from formatted to unformatted input there is often the problem that there is still a whitespace (often the ENTER '\n', in your case the space) in the input stream. And you need to eliminate this whitespace.
You tried it with std::ignore, but there is a better possibility: std::ws. You may read here about it. This will often be the better approach. You may use it after the transition from formatted to unformatted input the eat away the non wanted white spaces. You can also use it in general if you want to eat up leading white spaces using unformatted input. (Beware: the formatted input functions like >> will skip the white spaces (per default)).
ignore is a unformatted input function and will actively wait for characters. It is an input function. It will not return until it read something. So, be careful. It will often be just used in error cases.
With the above knowledge, we could rewrite your code like the below:
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
class banca
{
private:
std::string name;
std::string email;
std::string course_aluno;
int semestre;
int id;
public:
friend std::istream& operator >>(std::istream& in, banca& p);
friend std::ostream& operator <<(std::ostream& out, const banca& p);
};
std::istream& operator>>(std::istream& in, banca& p)
{
std::cout << "\n\n--------ALUNO--------\n";
std::cout << "Name:\n";
std::getline(in >> std::ws, p.name);
std::cout << "Email:\n";
std::getline(in, p.email);
std::cout << "Course:\n";
std::getline(in, p.course_aluno);
std::cout << "Semestre:\n";
in >> p.semestre;
std::cout << "ID:\n";
in >> p.id;
return in;
}
std::ostream& operator <<(std::ostream& out, const banca& p) {
return out << "\nName:\t\t " << p.name << "\nEMail:\t\t " << p.email << "\nCourse:\t\t "
<< p.course_aluno << "\nSemestre:\t " << p.semestre << "\nID:\t\t " << p.id << '\n';
}
int main() {
banca b1,b2;
std::cin >> b1;
std::cout << b1;
std::cin >> b2;
std::cout << b2;
}
But beware. Input functions may fail. Check the return code of each input function. And if you have an error, then use ignore.
Example. If you expect a number and enter a string, then the formatted input function will fail, and all invalid characters will still be in the stream. Enter "abc" for the semester and you will see it.
Solution. Check result of IO function:
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <limits>
class banca
{
private:
std::string name{};
std::string email{};
std::string course_aluno{};
int semestre{};
int id{};
public:
friend std::istream& operator >>(std::istream& in, banca& p);
friend std::ostream& operator <<(std::ostream& out, const banca& p);
};
std::istream& operator>>(std::istream& in, banca& p)
{
std::cout << "\n\n--------ALUNO--------\n";
std::cout << "Name:\n";
std::getline(in >> std::ws, p.name);
std::cout << "Email:\n";
std::getline(in, p.email);
std::cout << "Course:\n";
std::getline(in, p.course_aluno);
std::cout << "Semestre:\n";
in >> p.semestre;
std::cout << "ID:\n";
in >> p.id;
return in;
}
std::ostream& operator <<(std::ostream& out, const banca& p) {
return out << "\nName:\t\t " << p.name << "\nEMail:\t\t " << p.email << "\nCourse:\t\t "
<< p.course_aluno << "\nSemestre:\t " << p.semestre << "\nID:\t\t " << p.id << '\n';
}
int main() {
banca b1,b2;
if (std::cin >> b1)
std::cout << b1;
else {
std::cerr << "\nError: Problem with input\n\n";
std::cin.clear(); // unset failbit
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // skip bad input
}
if (std::cin >> b2)
std::cout << b2;
else {
std::cerr << "\nError: Problem with input\n\n";
std::cin.clear(); // unset failbit
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // skip bad input
}
}

Related

Delete n precedent line from file

I have a file, that contain student name, id, age and number of this student. I want to read the file and for example if the number of student is 2 delete all 3 precedent lines corresponding to this student.
My txt look like this
Marc 45646564 18 1 Jean 4563213 21 2
Paul 45654 22 4 Alice 45948 20 5
while (getline(student, line))
{
if (counter % 4 == 2) { // Here I want to delete all prcedent 3 lines}
}
Unfortunately your approach, or your design idea is not that good. It should be improved a little bit.
You need to start thinking in an object-oriented way. The object "student" has properties or data elements, like "name", "id", "age" and "number". All this data belong together and are related to a specific "student". And next, you need to define methods that should operate on this data, like "reading" and "writing" the complete data for some student.
If the "number" property that you have, after reading a complete "student" record, is ok, then you will store it in some kind of array or std::vector
So, lets start with the data part:
struct Student {
std::string name{};
unsigned long id{};
unsigned int age{};
unsigned int number{};
};
This will describe the properties of one student.
Next, we do want to operate on this data. And the first operation is that we do want to read this data from any kind of stream, for example from the console (std::cin) or from a file (std::ifstream). This basically doesn't matter, stream is stream :-)
The iostream library has the so called "extractor" operator for reading from streams. That is the >> operator. And this we can simply define for our "Student" struct. Like so:
friend std::istream& operator >> (std::istream& is, Student& s) {
return std::getline(is >> std::ws, s.name) >> s.id >> s.age >> s.number;
}
The signature of the function is given. This is a formal requirement. Then, now to what are we doing in that function. I would like to remind everybody that the istream extraction operations always return a reference to the given istream.. So, first we call std::getline. This will read the name of the student, and then return again is. Then the line looks like return is >> s.id >> s.age >> s.number. Next we will read the id. This will also return is. Then the statement would look like return is >> s.age >> s.number. And so on and so on. At the end we will have read everything and simply return is;
In the std::getline you will see >>std::ws. This will ignore all leading white spaces like new lines or whatever.
So, now, if we have an open file stream, let' s say "ifs" and a Student variable called "student", then we can write ifs >> student and it will read a complete student record from the file. So, all 4 lines.
By the way, we can do the same mechanism for the inserter function <<.
If we run this in a loop now and store the read student data into a std::vector we can copy all data from the file.
Or, if a student has a certain property, then we can also decide to not copy the data.
This could then look like:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
struct Student {
std::string name{};
unsigned long id{};
unsigned int age{};
unsigned int number{};
friend std::istream& operator >> (std::istream& is, Student& s) {
return std::getline(is >> std::ws, s.name) >> s.id >> s.age >> s.number;
}
friend std::ostream& operator << (std::ostream& os, const Student& s) {
return os << s.name << '\n' << s.id << '\n' << s.age << '\n' << s.number << '\n';
}
};
int main() {
// Here we will store all our studends
std::vector<Student> students{};
// Open the file with the data
std::ifstream ifs("r:\\data.txt");
// Check, if the file could be opened.
if (ifs) {
Student tempStudent{};
// Read all students from a file, until we hit eof or some other problem
while (ifs >> tempStudent) {
// if the student does fullfill the requirements, then we take it, else not
if (tempStudent.number != 2)
students.emplace_back(std::move(tempStudent));
}
// OK, for debug purposes we show all data ion the screen
for (const Student& s : students) std::cout << s << '\n';
}
else std::cerr << "\n\nError: source file could not be opened\n\n";
return 0;
}
And if you want to see a more advance C++ solution, please look at the below:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <iterator>
#include <algorithm>
struct Student {
std::string name{};
unsigned long id{};
unsigned int age{};
unsigned int number{};
friend std::istream& operator >> (std::istream& is, Student& s) {
return std::getline(is >> std::ws, s.name) >> s.id >> s.age >> s.number;
}
friend std::ostream& operator << (std::ostream& os, const Student& s) {
return os << s.name << '\n' << s.id << '\n' << s.age << '\n' << s.number << '\n';
}
};
int main() {
// Here we will store all our studends
std::vector<Student> students{};
// Open the file with the data and check, if it is open
if (std::ifstream ifs("r:\\data.txt");ifs) {
// Read all data
std::copy_if(std::istream_iterator<Student>(ifs), {}, std::back_inserter(students), [](const Student& s) {return s.number != 2;});
// For debug purposes we show all data ion the screen
std::copy(students.begin(), students.end(), std::ostream_iterator<Student>(std::cout, "\n"));
}
else std::cerr << "\n\nError: source file could not be opened\n\n";
return 0;
}

How do I write an array of contents into a text file? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
The code is supposed to open an existing text file, transfer the contents into the array, then create a new text file and then write the array contents into the new text file. The problem I'm having is that the code only outputs the last line of the content from the new text file.
file.open("Patient.txt", ios::in);
while (!file.eof()) {
file >> arr[i].name >> arr[i].DOB >> arr[i].address >> arr[i].Dr_name >> arr[i].V_date;
/*cout << arr[i].name << arr[i].DOB << arr[i].address << arr[i].Dr_name << arr[i].V_date << endl;*/
}
file.close();
files.open("Patients_2.txt");
if (files.is_open()) {
for (i; i < 30; i++) {
files << arr[i].name << arr[i].DOB << arr[i].address << arr[i].Dr_name << arr[i].V_date;
}
}
files.close();
patientss.open("Patients_2.txt");
cout << "Patient 2: " << endl;
while (!patientss.eof()) {
getline(patientss, line);
cout << line << endl;
}
patientss.close();
system("pause");
return 0;
}
IMHO, you should overload the formatted insertion and extraction operators in your patient class:
struct Patient
{
std::string name;
std::string dob;
std::string address;
std::string dr_name;
std::string v_date;
friend std::istream& operator>>(std::istream& input, Patient& p);
friend std::ostream& operator<<(std::ostream& output, const Patient& p);
};
std::istream& operator>>(std::istream& input, Patient& p)
{
std::getline(input, p.name);
std::getline(input, p.dob);
std::getline(input, p.address);
std::getline(input, p.dr_name);
std::getline(input, p.v_date);
return input;
}
std::ostream& operator<<(std::ostream& output, const Patient& p)
{
output << p.name << "\n";
output << p.dob << "\n";
output << p.address << "\n";
output << p.dr_name << "\n";
output << p.v_date << "\n";
return output;
}
The above makes input and output easier:
std::vector<Patient> database;
Patient p;
while (input_file >> p)
{
database.push_back(p);
}
const unsigned int quantity = database.size();
for (unsigned int i = 0; i < quantity; ++quantity)
{
output_file << database[i];
}
The above code also supports the concepts of encapsulation and data hiding. The Patient struct is in charge or reading its members because it knows the data types of the members. The code external to the Patient is only concerned with the input and output of a Patient instance (doesn't care about the internals).
This loop has a few problems:
while (!file.eof()) {
file >> arr[i].name >> arr[i].DOB ....
You never increase i so the same arr[i] will be overwritten time and time again.
You use !file.eof() as a condition to stop reading. eof() does not get set until after you've tried to read beyond the end of the file, which means that if you had increased i as you should, the last arr would be empty / broken. Instead check if the extraction from the stream succeeded. Since the stream is returned when you do stream >> var and has an overload for explicit operator bool() const which returns !fail() you can do this check directly in your loop:
while(stream >> var) { extraction success }
Using formatted input (>>) for string fields that are likely to contain spaces is however not a good idea. Your name, nung khual, would be split so nung would go into name and khual would go into DOB. It's better to use a field separator that is very unlikely to be included in anyone's name. \n is usually good and works well with std::getline.
std::getline returns the stream that you gave as an argument which means that you can chain getlines similarly to stream >> var1 >> var2, except it's a little more verbose.
getline(getline(stream, var1), var2) will put the first line in var1 and the second line in var2.
To make input and output a little simpler you can add stream operators for your data type and make the input stream operator use getline for your fields.
Example:
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
struct data_t {
std::string name;
std::string DOB;
std::string address;
std::string Dr_name;
std::string V_date;
};
// input stream operator using chained getlines
std::istream& operator>>(std::istream& is, data_t& d) {
using std::getline;
return getline(getline(getline(getline(getline(is,
d.name), d.DOB), d.address), d.Dr_name), d.V_date);
}
// output stream operator
std::ostream& operator<<(std::ostream& os, const data_t& d) {
return os << d.name << '\n'
<< d.DOB << '\n'
<< d.address << '\n'
<< d.Dr_name << '\n'
<< d.V_date << '\n';
}
int main() {
std::vector<data_t> arr;
if(std::ifstream file("Patient.txt"); file) {
data_t tmp;
while(file >> tmp) { // remember, no eof() needed
arr.push_back(tmp);
}
}
if(std::ofstream file("Patients_2.txt"); file) {
for(const data_t& d : arr) {
file << d;
}
}
if(std::ifstream patientss("Patients_2.txt"); patientss) {
data_t tmp;
while(patientss >> tmp) {
std::cout << tmp;
}
}
}

C++ "noskipws" is not working as expected, How to properly allow whitespace in string?

I want to be able to type in a full string, including white spaces, and then print that string.
Why is this code not behaving as I would expect?
Code
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "Enter your name:\n";
string name;
cin >> noskipws >> name;
cout << "Hello, " << name << "!";
return 0;
}
Output
Enter your name:
>tes test test
Hello, tes!
noskipws stops the stream from skipping leading whitespace before it reads a value. operator>> will still stop reading when it reaches whitespace after a word.
If you want to read a whole line from the console, use std::getline() instead of operator>>:
#include <iostream>
#include <string>
int main()
{
std::cout << "Enter your name:\n";
std::string name;
std::getline(std::cin, name);
std::cout << "Hello, " << name << "!";
return 0;
}
You can define a helper class to give yourself something that looks like an manipulator, if that is the syntax you want to use.
struct Getline {
std::string &s_;
Getline(std::string &s) : s_(s) {}
friend auto & operator >> (std::istream &&is, Getline &&g) {
return std::getline(is, g.s_);
}
};
And then you can use it like this:
int main()
{
std::cout << "Enter your name:\n";
std::string name;
std::cin >> Getline(name);
std::cout << "Hello, " << name << "!";
return 0;
}

how to fill an array from a text file in c++

Hi I'm trying to fill in an array of a class object I created. Input is from a text file. The text file has strings and numbers. I got the first set of info in but the rest of the file won't read in, any thoughts would be appreciated!
class mess
{
private:
string name;
float age, weight, height;
public:
void setname(string a) {name=a;}
void setage(float b){age=b;}
//etc.
string getname(){return name;}
float getage(){return age;}
//etc.
}
ifstream input;
input.open("test.txt");
mess people[2]
string str;
float num;
for(inti=0; i<2; i++)
{
getline(input,str,'\n');
people[i].setname(str);
input >> num;
people[i].setage(num);
input >> num;
people[i].setweight(num);
input >> num;
people[i].setheight(num);
}
for(inti=0; i<2; i++)
{
cout << people[i].getname() << endl;
cout << people[i].getage() << endl;
cout << people[i].getweight() << endl;
cout << people[i].getheight() << endl;
}
test.txt
jack
17 150.3 5.10
Amy
18 110.4 5.11
Output:
Jack
17
150.3
5.10
(blank)
0
0
0
The problem here is that when you use the input operator >> it will leave the newline after the last number for the first record. This means that the next getline call will read that newline as an empty line, and then the numbers will fail to read.
There are a couple of ways to solve this. The first is to discard all text in the input until newline after reading the last number in the record. For this you can do e.g.
// All other input in loop
input.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Read about the ignore function.
Another way is to read the second line, complete, and put it into an std::istringstream and then read out the numbers from it:
// Reading the name...
std::string numbers;
std::getline(input, numbers);
std::istringstream istr(numbers);
istr >> num;
people[i].setage(num);
// ...
Also note that the third argument to std::getline already defaults to a newline, so if you're using it to read lines, then you don't need to provide it.
I suggest you overload operators << and >> in your class:
class mess
{
private:
string name;
float age, weight, height;
public:
void setname(string a) {name=a;}
void setage(float b){age=b;}
//etc.
string getname(){return name;}
float getage(){return age;}
//etc.
friend std::istream& operator>>(std::istream& inp, mess& m);
friend std::ostream& operator<<(std::ostream& out, const mess& m);
}
std::istream& operator>>(std::istream& inp, mess& m)
{
std::getline(inp, m.name);
inp >> m.age;
inp >> m.weight;
inp >> m.height;
return inp;
}
std::ostream& operator<<(std::ostream& out, const mess& m)
{
out << m.name << endl;
out << m.age << endl;
out << m.weight << endl;
out << m.height << endl;
return out;
}
This simplifies your input to:
std::vector<mess> people;
mess p;
while (input_file >> p)
{
people.push_back(p);
}
Your output looks like:
for (unsigned int i = 0; i < people.size(); ++i)
{
cout << people[i];
cout << "\n";
}
I would define the operator<< to write out an object of your class. Then defined the operator>> to read an object of your class. Then you can use std::istream_iterator to read the values directly into the container:
class M
{
private:
string name;
float age;
float weight;
float height;
public:
<STUFF>
friend std::ostream& operator<<(std::ostream& s, M const& data)
{
return s << data.age << " "
<< data.weight << " "
<< data.height << " "
// Put name on the edn because it may contain space.
// So we want to read it off using std::getline
<< data.name << "\n";
}
friend std::istream& operator>>(std::istream& s, M& data)
{
s >> data.age >> data.wight >> data.height;
std::getline(s, data.name);
// Strip leading space (see output operator)
data.name = data.name.substring(1);
return s;
}
};
Then easy to use in most containers.
int main()
{
std::ifstream f("data");
// OK.
// A vector is not actually an array.
// But you can use the same principle with a manual loop.
// as std::istream_iterator is just a normal iterator.
std::vector<M> data(std::istream_iterator<M>(f), std::istream_iterator<M>());
}

Problem with file loop and reading into map

The while loop I have while reading in from a file doesn't break. I'm not sure what the problem is. If you need any more information just ask.
Code:
#include <string>
#include <map>
#include <fstream>
#include <iostream>
#include <iterator>
using namespace std;
class Customer {
public:
string name;
string address;
Customer() {}
};
class Purchase {
public:
string product_name;
double unit_price;
int count;
Purchase() {}
Purchase(string pn, double up, int c) :product_name(pn), unit_price(up), count(c) {}
};
// Function Object for comparison in map container
struct Cmp_name {
bool operator()(const Customer& first, const Customer& second)
{ return first.name < second.name; }
};
// ostream overloads
ostream& operator<<(ostream& out, const Customer& c)
{
out << c.name << '\n'
<< c.address << '\n';
return out;
}
ostream& operator<<(ostream& out, const Purchase& p)
{
out << p.product_name << '\n'
<< p.unit_price << '\n'
<< p.count << '\n';
return out;
}
istream& operator>>(istream& in, Customer& c)
{
getline(in, c.name);
getline(in, c.address);
return in;
}
istream& operator>>(istream& in, Purchase& p)
{
getline(in, p.product_name);
in >> p.unit_price >> p.count;
return in;
}
int main()
{
cout << "Enter file to read orders from: \n";
string file;
cin >> file;
ifstream is(file.c_str());
if (!is) cerr << "File doesn't exist.\n";
multimap<Customer, Purchase, Cmp_name> orders;
while (!is.eof()) {
Customer c;
Purchase p;
is >> c;
is >> p;
orders.insert(make_pair(c,p));
}
for (multimap<Customer, Purchase, Cmp_name>::iterator it = orders.begin(); it!=orders.end(); ++it)
cout << it->first << it->second << "\n\n";
}
As for your Customer/Purchase ostream inserters, declare the second argument const& instead of non-const &. For example:
ostream& operator<<(ostream& out, Customer const& c)
That's necessary because the key in a map is immutable even if you're using a non-const iterator (modifying the key would invalidate whatever tree-sorting or hashing the map implementation uses.
It's best to check every istream extraction operation for success, and break out of the loop the first time one doesn't succeed. Your "is.eof()" isn't going to read any extra (e.g. whitespace) characters, so it may claim "!eof()" at the semantic end of file.
Something like:
for(;;) {
Customer c;
Purchase p;
if (!getline(is, c.name)) break;
if (!getline(is, c.address) break;
if (!getline(is, p.product_name) break;
if (!(is >> p.unit_price >> p.count)) break;
orders.insert(make_pair(c,p));
}
Since those all return the original istream, it's the same as having a "if (!is) break;" after every attempted input.
You can also simplify things somewhat by defining extractors for Customer and Purchase, e.g.
istream& operator>>(istream &i,Customer &c)
A failure to read a Customer would let you break out (the istream will evaluate as false if an eof stops the read from succeeding).
Obviously you can make some of the failed-input points "ok to eof" and give a specific error in all the other cases.