For my project I have to overwrite the operator>> method to read in an array of numbers from a text file. This is my first time doing any of this and I am pretty lost. My code so far looks like this.
std::istream& operator>>(std::istream& in, bigint array){
bool semi = false ;
while(!semi){
if(get() = ';')
semi = true ;
in <<get();
}
return in ;
}
And the file looks like this.
10000000000000000000000000000000000345;
299793000000
00000000000000000000067;
4208574289572473098273498723475;
28375039287459832728745982734509872340985729384750928734590827098752938723;
99999999; 99999999;
Each new array stops when it hits a ";'. The white spaces and endlines are confusing me too. Any help would be appreciated thank you.
You will want to use
bigint& array
to take the value by reference (or you couldn't possibly insert the digits read into it).
Also, you will want to use
char ch;
in >> ch;
instead of in << get() (which doesn't compile). Better yet, add error handling:
if (!(in >> ch))
{
// we're in trouble (in.rdstate(), in.eof(), in.fail() or in.bad() to know more)
}
If you wanted to use in.get(), you should be prepared to skip your own whitespace (including newlines). I'd prefer std::istream_iterator here, because it will automatically do so (if the std::ios::skipws flag is in effect, which it is, by default).
So here's a simplist approach (that mostly assumes input data is valid and whitespace ignorable):
#include <vector>
#include <istream>
#include <iterator>
struct bigint
{
std::vector<char> v; // or whatever representation you use (binary? bcd?)
};
std::istream& operator>>(std::istream& in, bigint& array)
{
for (std::istream_iterator<char> f(in), l; f != l; ++f) {
if (*f>='0' && *f<='9')
array.v.push_back(*f - '0');
else if (*f==';')
break;
else
throw "invalid input";
}
return in;
}
#include <iostream>
#include <sstream>
int main()
{
std::istringstream iss(
"10000000000000000000000000000000000345;\n"
"299793000000\n"
"00000000000000000000067;\n"
"4208574289572473098273498723475;\n"
"28375039287459832728745982734509872340985729384750928734590827098752938723;\n"
"99999999; 99999999;\n");
bigint value;
while (value.v.clear(), iss >> value)
std::cout << "read " << value.v.size() << " digits\n";
}
See it Live on Coliru
There's quite a lot of confusions here. I'll just list some points, but you have a way to go even if you fix these things.
What exactly are you reading? You say you are reading an array of numbers, but your code says this
std::istream& operator>>(std::istream& in, bigint array){
I might be wrong but bigint sounds like a single number to me. I would expect something like this
std::istream& operator>>(std::istream& in, std::vector<bigint>& array){
Which brings me to the second point, operator>> is expected to modify it's second argument, which means it cannot be passed by value, you must use a reference. In other words this is wrong
std::istream& operator>>(std::istream& in, X x){
but this is OK
std::istream& operator>>(std::istream& in, X& x){
You are trying to read an array of bigints, so you need a loop (you have that) and each time round the loop you will read one bigint. So you need a way to read one bigint, do you have that? There nothing in your question or your code that indicates you have the ability to read a bigint, but it's obviously crucial that you do. So if you do not have any code yet to read a bigint, you can forget about this whole exercise until you have that code, so work on that first. When you can read one bigint, only then should you come back to the problem of reading an array of bigints.
The other tricky part is the stopping condition, you stop when you read a semi-colon (possibly preceded by whitespace). So you need a way to read the next non-space character, and crucially you need a way to unread it if it turns out not to be a semicolon. So you need something like this
if (!(in >> ch) || ch == ';')
{
// quit, either no more input, or the next char is a semicolon
break;
}
in.putback(ch); // unread the last char read
// read the next bigint
Hope this helps.
Related
I am currently trying to create a candlestick chart representing high and low temperatures for my C++ class. In this assignment, we are provided a txt file with two data columns in the following format:
Average Monthly Low High Temperatures(F)
X Y
X Y
X Y
I have successfully been able to read the txt file, but am confused as how to select specific data. I would like to essentially skip the first sentence and store the remaining variables to create the graph. This is what I am having trouble with.
In the end, I need to display the first line of the text file, as well as display the graph. I would very much like your help.
If there is a more efficient way of doing this, I would love to learn more!
You read a line in C++ with std::getline(). Note that it is good practice to check the return value of all input functions in order to detect faulty or missing data or media problems etc. getline() as well as the input operator>>() return the input stream. Streams have a handy conversion to bool: When an error has been encountered (read error, end of file), the conversion yields false; if the stream is OK, it is true. This conversion with these semantics has been designed exactly so that it is convenient to check the success of an input operation:
std::string header;
if(!std::getline(infile, header)) {
std::cerr << "Couldn't read first line\n";
exit(1);
}
// keep line for later output.
// Now read the number pairs. The numbers must be pairs but they actually don't
// have to be on a single line (but it doesn't hurt either).
int a, b;
// The read could fail for other reasons as well;
// a more robust program would look at the status bits of the stream
// and, if an error occurred, print an error message.
while (infile >> a >> b) { // will be false when EOF is reached
{
// print your tree or whatever
}
You should split the big problem into smaller problems and then solve the smallest problem first. Additionally, you should make use of buildinC++ functionality. An,in C++ you should use an object oriented approach.
That means, store data and their related functions in one object. So, the data and the methods, operating on this data.
I would propose the build 2 classes (or structs). On holds just one pair of low and high temperature.
The second class, holds the header line and a list (implemented as std::vector) of low-high-temperature pairs.
The methods that we needin our example are extraction from and insertion into a stream.
For this we usethe well known operators << and >> and add them to our class. Here we will do all necessary IO-operations.
So, if we split the big problem into smaller parts according to the above approach, then we will get easier to understand and better readable code.
Last but not least, we will use existing functions from the standard library, like std::copy.
The istream_iterator and std::ostream_iterator will simply call the underlying extratcor operator >> and inserter operator <<.
An example for a complete program (one of many possible soultions) can be seen below:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
// A data struct that can hold a low and a high temperatur and knows, how reaad and write its data
struct LowHighTemperature {
// The data
double low{};
double high{};
// The extractor operator for reading values from a stream
friend std::istream& operator >> (std::istream& is, LowHighTemperature& lht) {
return is >> lht.low >> lht.high;
}
// The inserter operator for writing values to a stream
friend std::ostream& operator << (std::ostream& os, const LowHighTemperature& lht) {
return os << lht.low << '\t' << lht.high;
}
};
// A list with high and low temperatures and a header line
struct TemperatureList {
// The data
std::string header{};
std::vector<LowHighTemperature> lowHighTemperature{};
// The extractor operator for reading values from a stream
friend std::istream& operator >> (std::istream& is, TemperatureList& tl) {
// Delete potentioally pre existing data
tl.lowHighTemperature.clear();
// Read the header line
if (std::getline(is, tl.header))
// Now read all temperatures
std::copy(std::istream_iterator<LowHighTemperature>(is), {}, std::back_inserter(tl.lowHighTemperature));
return is;
}
// The inserter operator for writing values to a stream
friend std::ostream& operator << (std::ostream& os, const TemperatureList& tl) {
// Show header
os << tl.header << '\n';
// Show temperatures
std::copy(tl.lowHighTemperature.begin(), tl.lowHighTemperature.end(), std::ostream_iterator<LowHighTemperature>(os, "\n"));
return os;
}
};
// Please store the path to your temperatures source file here
const std::string temperatureFileName{ "r:\\temperatures.txt" };
int main() {
// Open source file and check, if it is open
if (std::ifstream temperaturFileStream{ temperatureFileName }; temperaturFileStream) {
// Here we will store the list with all temperatures and the header
TemperatureList temperatureList{};
// Read all data from file stream
temperaturFileStream >> temperatureList;
// For debug purposes, you show the result on the screen
std::cout << temperatureList;
}
else {
std::cerr << "\n\nError: Could not open '" << temperatureFileName << "'\n";
}
return 0;
}
You can add whatever other methods you need for you own calculations.
You can access the first temperature pair via temperatureList.lowHighTemperature[0]
I hope you got the idea.
I'd like to have a std::getline function which is able to stop if it encounters any of the characters listed in a string, so I came up with the following:
std::istream& read_until(std::istream& is, std::string& s, const std::string& list) {
s.clear();
while (is.peek() && is && list.find(is.peek()) == list.npos) {
s += is.get();
}
return is;
}
The fact that it leaves the terminating character on the stream is the desired behavior. This works, but it's ugly and doesn't feel the right way to go. I'd like to ask if you see any clear mistake or if you have a better way of handling this.
I have a custom String class that contains a char array, and I need to overload the >> operator for this class. For the life of me I can't figure out how to do two things.
1 - Read the user input until a ; is reached
2 - Include whitespace from user input
I cannot use namespace or c++'s built-in string. Unfortunately that rules out the use of getline and any of the convenient find functions (I think it does, anyway?). Some things I have tried:
std::istream& operator >> (std::istream& output, String& input) {
output >> input.str;
return output;}
This works but only up until the first whitespace, after which point it stops reading the user input.
std::istream& operator >> (std::istream& output, String& input) {
while (output != ';') {
output >> input.str;
}
return output;}
An istream I guess isn't equivalent to the user input so you cannot compare it to a char like I tried to in my while loop.
So, my questions are, how does one read input until a specified character is encountered, and how does one include all whitespace when using >> ?
The global operator>> for string/character input stops reading when it encounters whitespace, so it is not worthwhile to implement your custom operator>> in terms of the global operator>>.
You ruled out use of std::getline(), but you can use std::istream::getline() instead. Like std::getline(), it also has an optional delim parameter (the default is '\n'), and will read characters - including whitespace - until the delimiter or EOF is reached.
std::istream& operator >> (std::istream& input, String& output)
{
return input.getline(output.str, yourmaxstrsize, ';');
}
When using stdio.h, I can easily read certain kinds of formatted input like this:
FILE* fin = fopen(...);
fscanf(fin, "x = %d, y = %d", &x, &y);
The great thing about this is that I don't really have to worry about how many spaces there are between the character 'x' and the following '=', and other minor details.
In C++ it appears to me as though,
ifstream fin(...);
string s;
fin >> s;
may result in s being "x" or "x=", or even "x=12" depending on the spacing of the input.
Is there a convenient way to get behavior similar to scanf/fscanf using iostream/fstream?
This is actually surprisingly easy, given a prerequisite. I have these three functions that I stick in a header somewhere. These allow you to stream in character literals, and string literals. I've never quite understood why these aren't standard.
#include <iostream>
//These are handy bits that go in a header somewhere
template<class e, class t, int N>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e(&sliteral)[N]) {
e buffer[N-1] = {}; //get buffer
in >> buffer[0]; //skips whitespace
if (N>2)
in.read(buffer+1, N-2); //read the rest
if (strncmp(buffer, sliteral, N-1)) //if it failed
in.setstate(std::ios::failbit); //set the state
return in;
}
template<class e, class t>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e& cliteral) {
e buffer(0); //get buffer
in >> buffer; //read data
if (buffer != cliteral) //if it failed
in.setstate(std::ios::failbit); //set the state
return in;
}
//redirect mutable char arrays to their normal function
template<class e, class t, int N>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, e(&carray)[N]) {
return std::operator>>(in, carray);
}
Given those, the rest is easy:
in>>'x'>>'='>>data.first>>','>>'y'>>'='>>data.second;
Proof here
For more complex situations, you probably want to use std::regex or boost::regex, or maybe a real lexer/parser.
like you specify format in fscanf/scanf using %xx you can specify format using stream manipulators as detailed in this tutorial -
http://www.tenouk.com/Module18.html
very comprehensive. stream manipulator is near the bottom of the page.
The short answer is "no".
A slightly longer answer is "You can probably build something that does that". For example, you could read the line of text, and then use a suitable "replace spaces with empty string" type function. Or perhaps something like this:
int x, y;
string s;
getline(cin, s, '=');
cin.get(); // Get rid of =
cin >> x;
getline(cin, s, '=');
cin >> y;
Alternatively, using cin.ignore to skip things (since the string reading is not really useful uness you want to know that 'x' and 'y' are actually 'x' and 'y'=:
int x, y;
cin.ignore(1000000, '='); // Skip up to a '='
cin >> x;
cin.ignore(1000000, '='); // Skip up to a '='
cin >> y;
This will "break" if someone enteres over 100k characters without an = sign, and there is need for error checking to see that "garbage" isn't coming in - just like fscanf does. if (cin >> x) would take care of the "detect that something went wrong, but you need to then do something sensible with the fact that it's gone wrong, which I'm not sure of right now...
Of course, since C++ supports (nearly) all of C, you can of course always use whatever members of the <cstdio> functions that you would like to use, as well. [And in at least some cases, they are actually a bit better].
this is part of a homework assignment. I don't want an answer just help. I have to make a class called MyInt that can store any sized positive integer. I can only use cstring cctype iomanip and iostream libraries. I really don't understand even where to begin on this.
6) Create an overload of the extraction operator >> for reading integers from an input stream. This operator should ignore any leading white space before the number, then read consecutive digits until a non-digit is encountered (this is the same way that >> for a normal int works, so we want to make ours work the same way). This operator should only extract and store the digits in the object. The "first non-digit" encountered after the number may be part of the next input, so should not be extracted. You may assume that the first non-whitespace character in the input will be a digit. i.e. you do not have to error check for entry of an inappropriate type (like a letter) when you have asked for a number.
Example: Suppose the following code is executed, and the input typed is " 12345 7894H".
MyInt x, y;
char ch;
cin >> x >> y >> ch;
The value of x should now be 12345, the value of y should be 7894 and the value of ch should be 'H'.
The last state of my code is as follows:
istream& operator>>(istream& s, MyInt& N){
N.Resize(5);
N.currentSize=1;
char c;
int i = 0;
s >> c;
N.DigitArray[i++] = C2I(c);
N.currentSize++;
c = s.peek();
while(C2I(c) != -1){
s >> c;
if(N.currentSize >= N.maxSize)
N.Resize(N.maxSize + 5);
N.DigitArray[i] = C2I(c);
i++;
N.currentSize++;
}
}
It almost works! Now it grabs the right number but it doesn't end when I hit enter, I have to enter a letter for it to end.
You can create an operator>> overload for your class this way (as a free function, not inside the class):
std::istream& operator>>(std::istream& lhs, MyInt& rhs) {
// read from lhs into rhs
// then return lhs to allow chaining
return lhs;
}
You can use the members peek and read of istream to read in characters, and isspace to test if a character is a space, and isdigit to check if a character is a number (isspace and isdigit are in the <cctype> header).
First of all, your operator>> should be concerned only with extracting the sequence of chars from the stream and knowing when to stop based on your rules for that. Then, it should defer to a constructor of myInt to actually ingest that string. After all, that class will probably want to expose constructors like:
myInt bigone ("123456123451234123121");
for more general-purpose use, right? And, functions should have a single responsibility.
So your general form will be:
istream& operator>> (istream& is, myInt x)
{
string s = extract_digits_from_stream(is);
x = myInt(s);
return is; // chaining
}
Now how do you extract just digits from a stream and stop at a non-digit? Well, the peek function comes to mind, as does unget. I'd look at source code for the extraction operator for regular integers and see what it does.