Overload cin to take ifstream instead of char* fname? [closed] - c++

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I am trying to create a file manipulator so when I cin >> filename it will read the file and cout the value... of course I am going add more stuffs but I would like to proceed with the basic of getting the content of the file.
What I wanna achieve is by cout prompting user for a textfile name, then when he enter, I will do another cin >> readfile on next line of code then the compiler will run the operator overload and cout the content.
How do I achieve it. Below is my code.
Question is instead of taking in char* fname, can I send ifstream in instead
so I can do something like
ifstream infile(filename.c_str());
cin >> infile
Below is my current code
#include <iostream>
#include <fstream>
#include <string>
#include "point2d.cpp"
#include "line2d.cpp"
#include <iomanip>
using namespace std;
struct myself
{
string myname;
};
istream &operator>> (istream &stream, myself &myself )
{
cout << "Enter my name";
stream >> myself.myname;
return stream;
}
istream &operator>> (istream &in, char* fname)
{
ifstream inFile;
inFile.open(fname);
char ch1;
while (inFile.get(ch1))
{
cout << ch1;
}
}
int main()
{
string filename;
cout << "Enter file name: ";
cin >> filename;
// was thinking of
// ifstream infile(filename.c_str());
// then read infile with cin
//how do i cin filename and read its content.
return 0;
}

You are messing up some concepts here. Your question is so vague, I'm not convinced that you are clear on what you want to do.
As far as I understand, you want
cin >> istream;
To do the following:
Read a file name
Open the file for reading
replace cin with the input stream for the file
Is that correct?
IMHO this is a bad idea, because by conventions cin >> should not modify cin in any significant manner. Sure, it will advance the stream, and there are hacks to change the parsing mode.
If you replace the stream with a new stream, make this obvious, don't try to hide it in some >> construct.
Programming is not a sport of saving characters. Your program won't be faster if you make it more cryptic. Good programmers write code that is very clear on what it does, to avoid errors, and make it easier to find them.
istream* input = &cin; // Default input is stdin
*input >> filename; // read filename
input = new istream(filename, istream::in);
// Continue to use *input
// Clean up input, if you replaced it:
// You might want to use a boolean flag instead of this hack.
if (input != &cin) {
delete input;
}

I'm afraid you are quite confused here. cin does not read anything; you read from cin. So you might want to overload operator >> somehow to read the contents of cin into a file, but not the other way around.
What you can do is overload operator << so it outputs the contents of a file into an output stream, such as cout. The following, nevertheless, is invalid:
ostream& operator<<(ostream& out, char* filename);
You cannot define this operator because it is already defined. You need to write a class that represents a file name and overload the operator on that. A simple version:
struct Filename
{
Filename(const string& filename) : filename_(filename) {}
string filename_;
};
ostream& operator<<(ostream& out, const Filename& name)
{
// read the file whose name is name.filename_
}
And you can call it this way:
cout << Filename("filename.ext");

You are doing several bad practices all at once.
First, cin is an istream, so you are looking to overload an istream operator to take an istream. That will be incredibly confusing for anyone trying to read your code.
Second, a function should do 1 job (that is, you should not try to hide functionality by lumping several distinct tasks into 1 function). istream reads data from an input stream (in this case, the file name you want to open). You will then need to pass that file name to an ifstream instance to actually read the file. Presumably you want to output it, in which case you would use an ostream (e.g. cout).
The entire logic you are looking to implement can be done in roughly 5 lines of code or less:
string filename = "";
cin >> filename;
ifstream fin(filename.c_str(), ifstream::in);
copy(istreambuf_iterator<char>(fin), istreambuf_iterator<char>(), ostreambuf_iterator<char>(cout));
Which is much shorter and more readable than what you are trying to do.

Related

Reading from text file and then store the item into a list [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Im trying to read from a file called stock.txt, which contains the following values:
ID, Item, Colour, Size, Quantity, Price
11,T-shirt,blue,XL,2,10.500000
12,Supreme,red,M,10,20.500000
13,BANG,red,M,10,20.500000
I wanted to store each item in a list, how can I do that?
int main() {
ifstream infile;
infile.open("Stock.txt");
string id; string title; string colour; string size; string quantity; string cost;
//If file open is successful
while(infile.good()){
getline(infile,id,',');
getline(infile,title,',');
getline(infile,colour,',');
getline(infile,size,',');
getline(infile,quantity,',');
getline(infile,cost,'\n');
}
infile.close();
}
You should use a more moden C++ approach.
I would be happy, if you could study this solution and try to use some features in the future.
In the object orient world, we use classes (or structs) and put data and functions, operating on this data, in one (encapsulated) object.
Only the class should know, how to read and write its data. Not some outside global functions. Therefor I added 2 member functions to your struct. I have overwritten the inserter and the extractor operator.
And in the extractor, we will use modern C++ algorithms, to split a string into tokens. For this purpose, we have the std::sregex_token_iterator. And because there is a specialized function for this purpose, we should use it. And besides, it is ultra simple.
With the below one-liner, we split the complete string into tokens and put the resulting tokens in a std::vector
std::vector token(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {});
Then we copy the resulting data in our member variables.
For demo output I have also overwritten the inserter operator. Now you can use the exteractor and inserter operators (">>" and "<<") for variables of type Stock, as for any other C++ integral variable.
In main, we use also an ultrasimple approach. First, we open the file and check, if this was OK.
Then we define a variable "stocks" (A std::vector of Stock) and use its range constructor and the std::istream_operator to read the complete file. And, since the App has an overwritten extractor operator, it knows, how to read and will parse the complete CSV file for us.
Again, the very simple and short one-liner
std::vector stocks(std::istream_iterator<Stock>(inFile), {});
will read the complete source file, all lines, parse the lines and store the member variables in the single stock elements of the resulting std::vector.
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <regex>
#include <iterator>
#include <algorithm>
std::regex delimiter{ "," };
struct Stock {
// The data. Member variables
std::string id{};
std::string title{};
std::string colour{};
std::string size{};
std::string quantity{};
std::string cost{};
// Overwrite extractor operator
friend std::istream& operator >> (std::istream& is, Stock& s) {
// Read a complete line
if (std::string line{}; std::getline(is, line)) {
// Tokenize it
std::vector token(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {});
// If we read at least 6 tokens then assign the values to our struct
if (6U <= token.size()) {
// Now copy the data from the vector to our members
s.id = token[0];
s.title = token[1];
s.colour = token[2];
s.size = token[3];
s.quantity = token[4];
s.cost = token[5];
}
}
return is;
}
// Overwrite inserter operator
friend std::ostream& operator << (std::ostream& os, const Stock& s) {
return os << "ID: " << s.id << "\nTitle: " << s.colour
<< "\nSize: " << s.size << "\nQuantity: " << s.quantity << "\nCost: " << s.cost;
}
};
int main() {
// Open file and check, if it could be opened
if (std::ifstream inFile("stock.txt"); inFile) {
// Define the variable and use range constructor to read and parse the complete file
std::vector stocks(std::istream_iterator<Stock>(inFile), {});
// Show result to the user
std::copy(stocks.begin(), stocks.end(), std::ostream_iterator<Stock>(std::cout, "\n"));
}
return 0;
}
Please note: I am using C++17 and can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction").
Additionally, you can see that I do not use the "end()"-iterator explicitely.
This iterator will be constructed from the empty brace-enclosed initializer list with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that.
You should model each row with a class or struct:
struct Record
{
int id;
std::string title;
std::string colour;
std::string size;
int quantity;
double price;
friend std::istream& operator>>(std::istream& input, Record & r);
};
std::istream& operator>>(std::istream& input, Record & r)
{
char comma;
input >> r.id;
input >> comma;
std::getline(input, r.title, ',');
std::getline(input, r.colour, ',');
std::getline(input, r.size, ',');
input >> r.quantity;
input >> comma;
input >> r.price;
input.ignore(100000, '\n');
return input;
}
Now you can read into a list:
std::list<Record> database;
Record r;
while (infile >> r)
{
database.push_back(r);
}
The overloading of operator>> makes the code simpler and easier to read.

How to use Input String Stream with symbols seperating strings instead of spaces?

note: I just learned about Getline and Streams.
Instead of a space separating first name, last name, and age, how could I separate them with ^ or --?
Is there a function for this? Is this a stupid question, and if so, why?
-The reason for this question is because I was going to make a function for solving polynomial derivatives and failed miserably.
int main() {
istringstream inSS; // Input string stream
string lineString; // Holds line of text
string firstName;
string lastName;
int userAge;
getline(cin, lineString);
inSS.str(lineString);
inSS >> firstName;
inSS >> lastName;
inSS >> userAge;
return 0;
}
The free function getline also offers to have a custom delimiter like user4581301 said. However, it will only extract strings and you also have int.
A similar solution can be found in this answer and I have modified the code to fit your needs: changing the delimiter for cin (c++)
You can use imbue to have some custom delimiter. A simple example is below:
#include <locale>
#include <iostream>
template<char Delim>
struct alternativeDelimiter : std::ctype<char> {
alternativeDelimiter() : std::ctype<char>(get_table()) {}
static mask const* get_table()
{
static mask rc[table_size];
rc[Delim] = std::ctype_base::space;
return &rc[0];
}
};
int main() {
using std::string;
using std::cin;
using std::locale;
cin.imbue(locale(cin.getloc(), new alternativeDelimiter<'^'>));
string word;
while(cin >> word) {
std::cout << word << "\n";
}
}
imbue does take ownership of the ctype, so no worries about calling delete yourself.
If you input some^text, the output will be
some
text
You can also use it with your example, of course.
If you extend the table by writing lines similar to line 11 (rc[Delim] = std::ctype_base::space) only changing Delim, you can have multiple characters that will be interpreted as space.
I am not sure in how far this solves your original problem of writing a math parser, though. The general terminology involves the concepts "parser" and "lexer" and you might research these concepts to build a reliable math solver. Hope it helps, too.

Reading File with Strings using overloaded input stream operator>>

I'm trying to get inputs from a file by overloading the istream operator. For that, I declared it as friend of a class. Then, I take as input that same class like this:
file >> *(class pointer);
When I'm trying to debug the part of my code that need this to work, it goes as expected into this:
istream& operator>> (istream& in, MYCLASS& n)
{
string buffer;
while (!in.eof()) { // input is a file
in >> buffer;
// do stuff
}
return in;
}
The problem is that the buffer stays empty ("") and does not take what it's suppose to be taking from the file. Normally, the format of the file should not be a problem since I'm using a similar method elsewhere in my code without a problem, but here it is in case:
* Name Age
* Name Age
* Name Age
...
What should I put inside my istream overload function so i get inputs as intended?
This...
while (!in.eof()) {
...is broken. You should attempt to read and parse data into your variables, then check for failure/eof. Do not assume that you'll necessarily be at the end of file after reading the last MYCLASS. Instead:
string name;
int age;
while (in >> name >> age)
...do stuff...
If you've really got some kind of leading dot on each line, add a char and read into it too:
char dot;
string name;
int age;
while (in >> dot && dot == '.' && in >> name >> age)
...do stuff...
More generally, it's not a very scalable model to assume the rest of the input stream will contain one MYCLASS object. You could instead have a delimiter (e.g. when the first word on a line is not a name, but <END>), that terminates the loop.

Using getline in an overloaded input operator

Book.h:
#ifndef BOOKDATE
#define BOOKDATE
#include <iostream>
#include <string>
class Book{
friend std::istream& operator>>(std::istream&, Book&);
private:
std::string title, author;
int number;
};
std::istream& operator>>(std::istream&, Book&);
#endif // BOOKDATE
Book.cpp:
#include "BookDate.h"
using namespace std;
istream& operator>>(istream& is, Book& rhs){
getline(is, rhs.title);
getline(is, rhs.author);
is >> rhs.number;
if(!is)
rhs = Book();
return is;
}
I was wondering how exactly I should approach creating the input operator for the Book class. The title and author will be more than one word, so it fits that I need to use getline to receive that data. The issue then with getline is that it may pick up any '\n' left in the stream since cin was last used. For instance;
int x;
cin >> x; //newline is not extracted and left behind
Book a;
cin >> a; //"title" is automatically made empty!
I could instead use cin.ignore(256, '\n') but whose responsibility, the user's or class author's, is it to use this? Does the user use .ignore before he inputs a Book object or does the class author put .ignore at the beginning of the input operation?
It seems that in the former case the user would have to understand an .ignore method is needed but in doing so has to understand the implementation of the Book's input operator, which is not desirable. In the latter case, putting .ignore in the operator means my operator may not adapt to certain circumstances, since it always expects to encounter a newline before processing. For instance reading from an input file with data such as:
book1
author1
1
book2
author2
2
Means book1 gets ignored by cin.ignore(256,'\n').
To make your operator>> behave more like the operators for the built in types, you can use the ws manipulator to skip whitespace before you read your input.
Just use
is >> ws;
at the beginning of your input operator, and the stream will be positioned at the first non-whitespace character after the current position.
To overload the extraction operator properly you can change your input format to be a sequence of three variables that you want to populate, namely:
(title, author, number)
and modify your operator>> to:
istream& operator>>(istream& is, Book& rhs){
// just a suggestion: it is better if there is no input to do nothing
if(!is) return is;
string title, author;
int number;
char par1, comma, par2;
cin >> skipws >> par1 >> title >> comma >> author>> comma >> number >> par2;
if (par1 != '(' || comma != ',' || par1 != ')'){
// set failbit to indicate invalid input format
is.clear(ios_base::failbit);
}
rhs(title, author, number);
return is;
}
put is.ignore(); before getline(is, rhs.title);

C++ Piping ifstream into stringstream

I am overloading my istream operator, so I can replace std::cin with my object. I know I will have to feed it an empty stringstream for the final output to work.
I would like to feed an std::ifstream into a std::stringstream as so:
while(ifs >> ss) {}
Is this possible? Here is an example prototype code:
friend istream & operator >> (istream & is, Database & db)
{
ifstream ifs;
ifs.open(db.inputFilename_, ios::in | ios::binary);
if (!ifs.is_open())
{
cout << "Couldn't read " << db.inputFilename_ << endl;
return is;
}
while (ifs >> db.iss)
{}
ifs.close()
return db.iss;
}
I am not interested in any answers that start with "use Boost" :) This is a purely standard C++ project. Thank you for any help or pointers.
Right now I am getting:
error: invalid operands to binary expression ('ifstream' (aka 'basic_ifstream<char>') and 'istringstream' (aka 'basic_istringstream<char>'))
Simply do this:
if(ifs){
db.iss << ifs.rdbuf();
ifs.close();
}
You can use std::copy with an std::istream_iterator for std::cin and std::ostream_iterator for the std::stringstream.
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <sstream>
void redirect(std::ifstream &is, std::stringstream &os) {
is >> std::noskipws;
std::istream_iterator<char> begin(is);
std::istream_iterator<char> end;
std::ostream_iterator<char> out(os);
std::copy(begin, end, out);
}
Note that this copies the entire file into the std::stringstream, and thus for really large files that can't fit in memory, this will fail. The rdbuf solution that NaCl gave will similarly have an issue with very large files.
You can solve the large file problem by not reading all of the input at once. However, this will most likely require you to restructure your code inside your parsers. Without more detail on their implementations, I can't point you in the right direction.