Recursion cin.getline() doesn't want to ask for input - c++

Here is the code:
void Reader::read(short& in) {
char* str = new char[6];
char* strbeg = str;
cin.getline(str, 6);
in = 0;
int value = 0;
short sign = 1;
if (*str == '+' || *str == '-') {
if (*str == '-' ) sign = -1;
str++;
}
while (isdigit(*str)) {
value *= 10;
value += (int) (*str - '0');
str++;
if (value > 32767) {
cout.write("Error, value can't fit short. Try again.\n", 41);
delete[] strbeg;
read(in);
return;
}
}
if (sign == -1) { value *= -1; }
in = (short) value;
delete[] strbeg;
return;
}
What happens is that if I type 999999999, it calls itself but on fourth line it's not gonna ask for input again. Debugger couldn't give much info as it is more language-specific question. Thank you in advance. Have a nice day!
Yes, the goal is to parse input as short. I know about losing 1 from min negative, wip :)
=== edit ===
I've tried goto... No, same thing. So it's not about visible variables or addresses, I guess.
=== edit ===
I can't use operator >> as it is forbidden by the task.

999999999 will cause an overflow, thus failbit is set for cin. Then your program reach read(in), then the cin.getline(). Here, beacause of failbit, cin will not ask any input again.
If you tried to figure out why in my code cin do ask for more input, you might find out all this by yourself.
I write you an example.
#include <iostream>
#include <climits>
using namespace std;
int main() {
char str[6];
short x = 0;
bool flag = false;
while (flag == false) {
cin.getline(str, 6);
flag = cin.good();
if (flag) { // if read successfully
char *p = str;
if (*p=='-') // special case for the first character
++p;
while (*p && *p>='0' && *p<='9')
++p;
if (*p) // there is a non digit non '\0' character
flag = false;
}
if (flag == false) {
cout << "An error occurred, try try again." << endl;
if (!cin.eof()) {
cin.unget(); // put back the possibly read '\n'
cin.ignore(INT_MAX, '\n');
}
cin.clear();
} else {
// str is now ready for parsing
// TODO: do your parsing work here
// for exemple x = atoi(str);
}
}
std::cout << x << std::endl;
return 0;
}
As we have discussed, you don't need new.
Check whether the string read is clean before parsing. If you mix checking and parsing, things will be complicated.
And you don't need recursion.
Read characters from stream by istream::getline seems to be the only option we have here. And when an error occurred, this function really doesn't tell us much, we have to deal with overflow and other problem separately.

Related

Please help me write this Function on how to see if a given string is an float [duplicate]

Does anybody know of a convenient means of determining if a string value "qualifies" as a floating-point number?
bool IsFloat( string MyString )
{
... etc ...
return ... // true if float; false otherwise
}
If you can't use a Boost library function, you can write your own isFloat function like this.
#include <string>
#include <sstream>
bool isFloat( string myString ) {
std::istringstream iss(myString);
float f;
iss >> noskipws >> f; // noskipws considers leading whitespace invalid
// Check the entire string was consumed and if either failbit or badbit is set
return iss.eof() && !iss.fail();
}
You may like Boost's lexical_cast (see http://www.boost.org/doc/libs/1_37_0/libs/conversion/lexical_cast.htm).
bool isFloat(const std::string &someString)
{
using boost::lexical_cast;
using boost::bad_lexical_cast;
try
{
boost::lexical_cast<float>(someString);
}
catch (bad_lexical_cast &)
{
return false;
}
return true;
}
You can use istream to avoid needing Boost, but frankly, Boost is just too good to leave out.
Inspired by this answer I modified the function to check if a string is a floating point number. It won't require boost & doesn't relies on stringstreams failbit - it's just plain parsing.
static bool isFloatNumber(const std::string& string){
std::string::const_iterator it = string.begin();
bool decimalPoint = false;
int minSize = 0;
if(string.size()>0 && (string[0] == '-' || string[0] == '+')){
it++;
minSize++;
}
while(it != string.end()){
if(*it == '.'){
if(!decimalPoint) decimalPoint = true;
else break;
}else if(!std::isdigit(*it) && ((*it!='f') || it+1 != string.end() || !decimalPoint)){
break;
}
++it;
}
return string.size()>minSize && it == string.end();
}
I.e.
1
2.
3.10000
4.2f
-5.3f
+6.2f
is recognized by this function correctly as float.
1.0.0
2f
2.0f1
Are examples for not-valid floats. If you don't want to recognize floating point numbers in the format X.XXf, just remove the condition:
&& ((*it!='f') || it+1 != string.end() || !decimalPoint)
from line 9.
And if you don't want to recognize numbers without '.' as float (i.e. not '1', only '1.', '1.0', '1.0f'...) then you can change the last line to:
return string.size()>minSize && it == string.end() && decimalPoint;
However: There are plenty good reasons to use either boost's lexical_cast or the solution using stringstreams rather than this 'ugly function'. But It gives me more control over what kind of formats exactly I want to recognize as floating point numbers (i.e. maximum digits after decimal point...).
I recently wrote a function to check whether a string is a number or not. This number can be an Integer or Float.
You can twist my code and add some unit tests.
bool isNumber(string s)
{
std::size_t char_pos(0);
// skip the whilespaces
char_pos = s.find_first_not_of(' ');
if (char_pos == s.size()) return false;
// check the significand
if (s[char_pos] == '+' || s[char_pos] == '-') ++char_pos; // skip the sign if exist
int n_nm, n_pt;
for (n_nm = 0, n_pt = 0; std::isdigit(s[char_pos]) || s[char_pos] == '.'; ++char_pos) {
s[char_pos] == '.' ? ++n_pt : ++n_nm;
}
if (n_pt>1 || n_nm<1) // no more than one point, at least one digit
return false;
// skip the trailing whitespaces
while (s[char_pos] == ' ') {
++ char_pos;
}
return char_pos == s.size(); // must reach the ending 0 of the string
}
void UnitTest() {
double num = std::stod("825FB7FC8CAF4342");
string num_str = std::to_string(num);
// Not number
assert(!isNumber("1a23"));
assert(!isNumber("3.7.1"));
assert(!isNumber("825FB7FC8CAF4342"));
assert(!isNumber(" + 23.24"));
assert(!isNumber(" - 23.24"));
// Is number
assert(isNumber("123"));
assert(isNumber("3.7"));
assert(isNumber("+23.7"));
assert(isNumber(" -423.789"));
assert(isNumber(" -423.789 "));
}
Quick and dirty solution using std::stof:
bool isFloat(const std::string& s) {
try {
std::stof(s);
return true;
} catch(...) {
return false;
}
}
I'd imagine you'd want to run a regex match on the input string. I'd think it may be fairly complicated to test all the edge cases.
This site has some good info on it. If you just want to skip to the end it says:
^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$
Which basically makes sense if you understand regex syntax.
[EDIT: Fixed to forbid initial whitespace and trailing nonsense.]
#include <sstream>
bool isFloat(string s) {
istringstream iss(s);
float dummy;
iss >> noskipws >> dummy;
return iss && iss.eof(); // Result converted to bool
}
You could easily turn this into a function templated on a type T instead of float. This is essentially what Boost's lexical_cast does.
I always liked strtof since it lets you specify an end pointer.
bool isFloat(const std::string& str)
{
char* ptr;
strtof(str.c_str(), &ptr);
return (*ptr) == '\0';
}
This works because the end pointer points to the character where the parse started to fail, therefore if it points to a nul-terminator, then the whole string was parsed as a float.
I'm surprised no one mentioned this method in the 10 years this question has been around, I suppose because it is more of a C-Style way of doing it. However, it is still perfectly valid in C++, and more elegant than any stream solutions. Also, it works with "+inf" "-inf" and so on, and ignores leading whitespace.
EDIT
Don't be caught out by empty strings, otherwise the end pointer will be on the nul-termination (and therefore return true). The above code should be:
bool isFloat(const std::string& str)
{
if (str.empty())
return false;
char* ptr;
strtof(str.c_str(), &ptr);
return (*ptr) == '\0';
}
With C++17:
bool isNumeric(std::string_view s)
{
double val;
auto [p, ec] = std::from_chars(s.data(), s.data() + s.size(), val);
return ec == std::errc() && p == s.data() + s.size();
}
Both checks on return are necessary. The first checks that there are no overflow or other errors. The second checks that the entire string was read.
You can use the methods described in How can I convert string to double in C++?, and instead of throwing a conversion_error, return false (indicating the string does not represent a float), and true otherwise.
The main issue with other responses is performance
Often you don't need every corner case, for example maybe nan and -/+ inf, are not as important to cover as having speed. Maybe you don't need to handle 1.0E+03 notation. You just want a fast way to parse strings to numbers.
Here is a simple, pure std::string way, that's not very fast:
size_t npos = word.find_first_not_of ( ".+-0123456789" );
if ( npos == std::string::npos ) {
val = atof ( word.c_str() );
}
This is slow because it is O(k*13), checking each char against 0 thur 9
Here is a faster way:
bool isNum = true;
int st = 0;
while (word.at(st)==32) st++; // leading spaces
ch = word.at(st);
if (ch == 43 || ch==45 ) st++; // check +, -
for (int n = st; n < word.length(); n++) {
char ch = word.at(n);
if ( ch < 48 || ch > 57 || ch != 46 ) {
isNum = false;
break; // not a num, early terminate
}
}
This has the benefit of terminating early if any non-numerical character is found, and it checks by range rather than every number digit (0-9). So the average compares is 3x per char, O(k*3), with early termination.
Notice this technique is very similar to the actual one used in the stdlib 'atof' function:
http://www.beedub.com/Sprite093/src/lib/c/stdlib/atof.c
You could use atof and then have special handling for 0.0, but I don't think that counts as a particularly good solution.
This is a common question on SO. Look at this question for suggestions (that question discusses string->int, but the approaches are the same).
Note: to know if the string can be converted, you basically have to do the conversion to check for things like over/underflow.
What you could do is use an istringstream and return true/false based on the result of the stream operation. Something like this (warning - I haven't even compiled the code, it's a guideline only):
float potential_float_value;
std::istringstream could_be_a_float(MyString)
could_be_a_float >> potential_float_value;
return could_be_a_float.fail() ? false : true;
it depends on the level of trust, you need and where the input data comes from.
If the data comes from a user, you have to be more careful, as compared to imported table data, where you already know that all items are either integers or floats and only thats what you need to differentiate.
For example, one of the fastest versions, would simply check for the presence of "." and "eE" in it. But then, you may want to look if the rest is being all digits. Skip whitespace at the beginning - but not in the middle, check for a single "." "eE" etc.
Thus, the q&d fast hack will probably lead to a more sophisticated regEx-like (either call it or scan it yourself) approach. But then, how do you know, that the result - although looking like a float - can really be represented in your machine (i.e. try 1.2345678901234567890e1234567890). Of course, you can make a regEx with "up-to-N" digits in the mantissa/exponent, but thats machine/OS/compiler or whatever specific, sometimes.
So, in the end, to be sure, you probably have to call for the underlying system's conversion and see what you get (exception, infinity or NAN).
I would be tempted to ignore leading whitespaces as that is what the atof function does also:
The function first discards as many
whitespace characters as necessary
until the first non-whitespace
character is found. Then, starting
from this character, takes as many
characters as possible that are valid
following a syntax resembling that of
floating point literals, and
interprets them as a numerical value.
The rest of the string after the last
valid character is ignored and has no
effect on the behavior of this
function.
So to match this we would:
bool isFloat(string s)
{
istringstream iss(s);
float dummy;
iss >> skipws >> dummy;
return (iss && iss.eof() ); // Result converted to bool
}
int isFloat(char *s){
if(*s == '-' || *s == '+'){
if(!isdigit(*++s)) return 0;
}
if(!isdigit(*s)){return 0;}
while(isdigit(*s)) s++;
if(*s == '.'){
if(!isdigit(*++s)) return 0;
}
while(isdigit(*s)) s++;
if(*s == 'e' || *s == 'E'){
s++;
if(*s == '+' || *s == '-'){
s++;
if(!isdigit(*s)) return 0;
}else if(!isdigit(*s)){
return 0;
}
}
while(isdigit(*s)) s++;
if(*s == '\0') return 1;
return 0;
}
I was looking for something similar, found a much simpler answer than any I've seen (Although is for floats VS. ints, would still require a typecast from string)
bool is_float(float val){
if(val != floor(val)){
return true;
}
else
return false;
}
or:
auto lambda_isFloat = [](float val) {return (val != floor(val)); };
Hope this helps !
ZMazz

How should I read a format string of variable length in C++ from stdin?

sorry for such a stupid question but I couldn't find any obvious answer.
I need to read from stdin first an int n with the size of an array, and then integer values from a string in the format "1 2 3 4 5 6" with n elements.
If I knew the number of parameters at compile time I could use something like a scanf (or the safe alternatives) with a format string like "%d %d %d %d %d %d", but here I will only know that value at run time.
What would be the best way to do this in C++? Performance is important but more than that safety.
How should I read a format string of variable length in C++ from stdin?
You should not attempt to do such thing. Only ever use constant format strings.
I need to read from stdin first an int n with the size of an array, and then integer values
What would be the best way to do this in C++?
Read one value at a time. Repeat using a loop.
Here's a function that does what errorika describes:
const int SIZE = //as much of your memory as you'd like the user to have access to
***caller function must include this:
//allocate a string to hold some data;
char* buffer = NULL;
buffer = malloc (SIZE * sizeof(char));
if (buffer == NULL) {
printf("malloc error terminating\n");
return;
}
***
void getEntry(char* buffer) {
int count = 0;
int maxlen = SIZE - 1;
char a = '0';
for (int i = 0; i < SIZE; i++) {
buffer[i] = '0';
}
while (a != '\n' && count < maxlen) {
a = fgetc(stdin);
buffer[count] = a;
count++;
}
if (a == '\n') {
buffer[count - 1] = '\0';
}
else {
buffer[count] = '\0';
do {
a = fgetc(stdin);
} while (a != '\n');
}
}
This is all basic C code but user entry is evil. Here is what I've come up with for more C++ idiomatic user input functions (query is just the message string you pass in):
template<typename T>
void getInput(const std::string query, T& entry) {
std::string input;
std::cout << query << std::endl;
getline(std::cin, input);
std::stringstream buffer{input};
buffer >> entry;
}
OR
template<typename T>
void getInput2(std::string query, T& entry) {
bool validInput = false;
while (validInput == false)
{
validInput = true;
std::cout << query << std::endl;
std::cin >> entry;
if (std::cin.fail()) {
validInput = false;
std::cout << "Unacceptable entry\n" << std::endl;
}
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}

How to only cin one input per line (C++)

I'm new to C++ and working on a simple guessing game where you get 5 tries to guess a number between 1 and 100.
I'm having issues dealing with user inputs.
I've made it so that the program only accepts numbers between 1 and 100, and it ignores characters without crashing. The problem is that when I type in gibberish like 34fa1e8, the loop will run three times, using 34 the first time, 1 the second time, and 8 the last time, instead of ignoring the input like I want it to.
The code im using is here:
int check_guess() {
int guess;
do {
cin >> guess;
if (cin.fail()) {
cin.clear();
cin.ignore();
}
} while (guess < 1 || guess > 100);
return guess;
}
How can I make the program dismiss inputs like these instead of accepting them separately?
You could use getline and stol.
I've given an answer like this before; an explanation can be found here.
You can even extended the solution to check for the specified range:
template <int min, int max>
class num_get_range : public std::num_get<char>
{
public:
iter_type do_get( iter_type it, iter_type end, std::ios_base& str,
std::ios_base::iostate& err, long& v) const
{
auto& ctype = std::use_facet<std::ctype<char>>(str.getloc());
it = std::num_get<char>::do_get(it, end, str, err, v);
if (it != end && !(err & std::ios_base::failbit)
&& ctype.is(ctype.alpha, *it))
err |= std::ios_base::failbit;
else if (!(min <= v && v <= max))
err |= std::ios_base::failbit;
return it;
}
};
Now you can imbue the stream with the new locale and you need to restructure your loop to discard valid input. For example:
std::locale original_locale(std::cin.getloc());
std::cin.imbue(std::locale(original_locale, new num_get_range<1, 100>));
int check_guess()
{
int guess;
while (!(std::cin >> guess))
{
std::cin.clear();
std::cin.igore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
Use ifstream::getline to store the input in a char array. Then convert it to an integer using a function like this:
int convertToInteger(char const *s){
if ( s == NULL || *s == '\0'){
return 0;
}
int result = 0, digit = 1000;
while((*s) && (digit > 0)){
if ( *s >= '0' && *s <= '9' ){
result += digit * (*s - '0');
}else{
return 0;
}
++s;
digit /= 10;
}
return result;
}
The reason it works is because it will return 0 in case of failure and that's something your loop's condition will not accept. You also don't have to worry about negative numbers since your loop won't accept them anyways.

Matching balanced and nested braces in input text

I attended a quiz, I gave the code but the auto-test shows that one of the eight test cases failed.
I myself tested my code many times, but all passed. I can't find where is the problem.
The question is to design a algorithm to check whether the brackets in a string match.
1) Just consider rounded brackets () and square brackets [], omit ohter chars.
2) Each pair brackets should match each other. That means ( matches ), and [ matches ].
3) Intercrossing is not allowed, such as : ([)]. There are two pairs of brackets, but they intercross each other.
To solve the problem, my method is described as follows:
Search each char in the whole input string, the index from 0 to str.size() - 1.
Use two stacks to record the opening tag (, and [, each type in one stack. When encountering one of them, push its index in the corresponding stack.
When encouterning the closing tag ) and ], we pop the corresponding stack.
Before popping, check the top of two stacks, the current stack should have the max index, otherwise that means there are unmatched opening tag with the other type, so the intercrossing can be checked this way.
My Code is Here:
#include <iostream>
#include <stack>
using namespace std;
int main()
{
string str;
cin >> str;
stack<int> s1, s2;
int result = 0;
for (int ix = 0, len = str.size(); ix < len; ix++)
{
if (str[ix] == '(')
{
s1.push(ix);
}
else if (str[ix] == '[')
{
s2.push(ix);
}
else if (str[ix] == ')')
{
if (s1.empty() || (!s2.empty() && s1.top() < s2.top()))
{
result = 1;
break;
}
s1.pop();
}
else if (str[ix] == ']')
{
if (s2.empty() || (!s1.empty() && s2.top() < s1.top()))
{
result = 1;
break;
}
s2.pop();
}
else
{
// do nothing
}
}
if (!s1.empty() || !s2.empty())
{
result = 1;
}
cout << result << endl;
}
As methoned before, this question can be solved by just on stack, so I modified my code, and here is the single stack version. [THE KEY POINT IS NOT TO ARGUE WHITCH IS BETTER, BUT WHAT'S WRONG WITH MY CODE.]
#include <iostream>
#include <stack>
using namespace std;
int main()
{
string str;
cin >> str;
stack<char> s;
const char *p = str.c_str();
int result = 0;
while (*p != '\0')
{
if (*p == '(' || *p == '[')
{
s.push(*p);
}
else if (*p == ')')
{
if (s.empty() || s.top() != '(')
{
result = 1;
break;
}
s.pop();
}
else if (*p == ']')
{
if (s.empty() || s.top() != '[')
{
result = 1;
break;
}
s.pop();
}
else
{
// do nothing
}
p++;
}
if (!s.empty())
{
result = 1;
}
cout << result << endl;
}
When using formatted input to read a std::string only the first word is read: after skipping leading whitespate a string is read until the first whitespace is encountered. As a result, the input ( ) should match but std::cin >> str would only read (. Thus, the input should probably look like this:
if (std::getline(std::cin, str)) {
// algorithm for matching parenthesis and brackets goes here
}
Using std::getline() still makes an assumption about how the input is presented, namely that it is on one line. If the algorithm should process the entire input from std::cin I would use
str.assign(std::istreambuf_iterator<char>(std::cin),
std::istreambuf_iterator<char>());
Although I think the algorithm is unnecessary complex (on stack storing the kind of parenthesis would suffice), I also think that it should work, i.e., the only problem I spotted is the way the input is obtained.

Polynomial Calculator

I'm doing a polynomial calculator and i'll need some help as i'll progress with the code.
For now I made only the polinom class which i represented it as a linked list with terms and some functions(only read and print the polynomial functions for now).
Here's the main program which for now only read a polynomial and prints it:
#include "polinom.h"
int main()
{
polinom P1;
bool varStatus = false;
char var = '\0', readStatus = '\0';
cout << "P1 = ";
P1.read(readStatus, var, varStatus); // i don't need readStatus yet as i haven't implemented the reset and quit functions
cout << "\n\nP = ";
P1.print(var);
getch();
return 0;
}
And the header file polinom.h:
#ifndef _polinom_h
#define _polinom_h
#include <iostream>
#include <list>
#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <conio.h>
using namespace std;
class polinom
{
class term
{
public:
int coef;
int pow;
term()
{
coef = 1;
pow = 0;
}
};
list<term> poly;
list<term>::iterator i;
public:
bool printable(char c)
{
return (
((int(c) > 42 && int(c) < 123) || isspace(c)) && int(c) != 44 && int(c) != 46 && int(c) != 47 &&
int(c) != 58 && int(c) != 59 &&
int(c) != 60 && int(c) != 61 && int(c) != 62 && int(c) != 63 && int(c) != 64 && int(c) != 65 &&
int(c) != 91 && int(c) != 92 && int(c) != 93 && int(c) != 95 && int(c) != 96
);
}
void read(char &readStatus, char &var, bool &varStatus)
{
term t; // term variable to push it into the list of terms
char c, lc, sign; // c = current char, lc = lastchar and sign the '+' or '-' sign before a coefficient
int coef, pow; //variables to pass the coef and power to term t
bool coefRead = false, powRead = false; //reading status of coef and power
while (c != '\r') { //we read characters until carriage return
c = getch(); // get the new imputed char
if (tolower(c) == 'r' || tolower(c) == 'q') { //if the user inputed r or q we reset the input or quit the program
readStatus = c; //pass current char value to readStatus so the program will know what to do next
return; //aborting the reading process
}
else
{
if (printable(c)) cout << c; //print on screen only the correct characters
if (!coefRead && !powRead) //we set term coef to the inputed value
{
if (isdigit(c)) {
if (isdigit(lc)) coef = coef * 10 + int(c); //if the last char was also a digit we multiply the last value of coef by 10 and add current char
else {
if (sign == '-') coef = -(int(c));//if the current coef has '-' before we set coef to it's negative value
else coef = int(c); //this means a new term's coef is read
}
if (!isdigit(c) && isdigit(lc)) coefRead = true; //if the last char was a digit and we reached the var name we stop reading the coefficient
}
else if (coefRead && !powRead) //after coefficient is read we get the term's varname and power
{
if (isdigit(c)) { // just like in the case with coefficient we read the power until the current char is not a digit
if (isdigit(lc)) pow = pow * 10 + int(c);
else pow = int(c);
}
else if (isalpha(c) && isdigit(lc) && !varStatus) { //if the last char was a digit and the current not we reached the var name
var = c; //also even though the variable is inputed more than once we save it only once
varStatus = true; //we mark the var name as read
}
else {
if (isdigit(lc)) powRead = true;
}
}
else {
if (c == '+' || c == '-') { // if a sign was inputed it means a new term is coming and we push the current term to the list and reset
t.coef = coef; // coefRead and powRead so we can read another term
t.pow = pow;
poly.push_back(t);
sign = c;
coefRead = false;
powRead = false;
}
}
lc = c; // we save the last character
}
}
}
void print(char var)
{
for ( i=poly.begin() ; i != poly.end(); i++ ) { //going through the entire list to retrieve the terms and print them
if (i == poly.end() - 1) { // if we reached the last term
if (*(i->pow == 0) //if the last term's power is 0 we print only it's coefficient
cout << *(i->coef);
else
cout << *(i->coef) << var << "^" << *(i->pow); //otherwise we print both
}
else {
if (*(i->coef > 0) //if the coef value is positive
cout << *(i->coef) << var << "^" << *(i->pow) << " + "; //we also add the '+' sign
else
cout << *(i->coef) << var << "^" << *(i->pow) << " - "; // otherwise we add '-' sign
}
}
}
};
#endif
EDIT
All compile errors fixed now thanks to JonH, but the read function is not working as the input characters aren't correctly inserted into the list. I know it may be trivial for you guys, but it would be great if you help me out.
Thanks!
I found MANY missing curly braces and closing parens all throughout your code. After having spent several minutes fixing at least 10 of these, I thought you would be better served if I helped you to learn to fish, rather than giving you fish for tonight's dinner.
Your code is written like a stream of consciousness. As you are building your code your mind jumps around, thinking of other things you need to build and new requirements introduced by whatever you just wrote. When you think of these things, you go write them and come back to where you were. Before you know it, you have written hundreds of lines of code by jumping around, writing bits here and there. The problem with this is that you can't possibly keep juggling sections of code like this without missing little syntax bits along the way.
You should take a more iterative approach to writing code. How exactly you do this will come with experience, but here's some guidance:
Start by stubbing out a class declaration with a few (preferably 1) core methods and member variables.
Compile. You'll get linker errors and the like, but you shouldn't get any syntax errors like missing parens or semicolons. Fix any you do find before moving on.
Implement the methods/functions you just stubbed. Compile & fix non-linker errors.
As you think of minor or dependant requirements that came up during the above steps, write comments in your code, like // TODO: Implement bool DoTheThing(int); But don't implement them yet.
Loop back to step 1, keeping the scope of what you're working on as limited and fundamental as possible. Never move beyond a compilation step without a clean compile.
Repeat until you have implemented everything. You might compile 50 times or more during this process.
Your fundamental problem is that you wrote a bunch of code down without testing it piece by piece, without thinking about it. When you write as a beginner, you should try adding one little bit at a time and making sure it compiles. Even as an advanced programmer, modularization is an extremely important part of the design and code-writing process.
That said, here are a few tips about your posted code in particular:
Your function printable is ugly as sin, and therefore impossible to debug or understand.
The number of nested if statements is indicative of design flaws.
You're missing an end brace on your if (isdigit(c)) statement.
Declaring (and especially initializing) multiple variables on the same line is bad form.
Those compile errors surely has a line number associated to them in the error message. Have you tried looking at the line indicated to see what is missing? If that does not help, please post the complete error output from the compiler so that we can se what the error is.
You are missing a few curly braces in your read function.
I redid it here:
void read(char &readStatus, char &var, bool &varStatus)
{
term t; // term variable to push it into the list of terms
char c, lc, sign; // c = current char, lc = lastchar and sign the '+' or '-' sign before a coefficient
int coef, pow; //variables to pass the coef and power to term t
bool coefRead = false, powRead = false; //reading status of coef and power
while (c != '\r') { //we read characters until carriage return
c = getch(); // get the new imputed char
if (tolower(c) == 'r' || tolower(c) == 'q')
{ //if the user inputed r or q we reset the input or quit the program
readStatus = c; //pass current char value to readStatus so the program will know what to do next
return; //aborting the reading process
}
else
{
if (printable(c))
cout << c; //print on screen only the correct characters
if (!coefRead && !powRead) //we set term coef to the inputed value
{
if (isdigit(c))
{
if (isdigit(lc))
coef = coef * 10 + int(c); //if the last char was also a digit we multiply the last value of coef by 10 and add current char
else
{
if (sign == '-')
coef = -(int(c));//if the current coef has '-' before we set coef to it's negative value
else
coef = int(c); //this means a new term's coef is read
} //end else
}//end if isdigit(c)
if (!isdigit(c) && isdigit(lc))
coefRead = true; //if the last char was a digit and we reached the var name we stop reading the coefficient
} //end if
else if (coefRead && !powRead) //after coefficient is read we get the term's varname and power
{
if (isdigit(c))
{ // just like in the case with coefficient we read the power until the current char is not a digit
if (isdigit(lc))
pow = pow * 10 + int(c);
else
pow = int(c);
}
else if (isalpha(c) && isdigit(lc) && !varStatus)
{ //if the last char was a digit and the current not we reached the var name
var = c; //also even though the variable is inputed more than once we save it only once
varStatus = true; //we mark the var name as read
}
else
{
if (isdigit(lc))
powRead = true;
}
} //end else if
else
{
if (c == '+' || c == '-')
{ // if a sign was inputed it means a new term is coming and we push the current term to the list and reset
t.coef = coef; // coefRead and powRead so we can read another term
t.pow = pow;
poly.push_back(t);
sign = c;
coefRead = false;
powRead = false;
}
}
lc = c; // we save the last character
} //end else
} //end while
} //end function
EDIT
I also fixed the print function:
void print(char var)
{
for ( i=poly.begin() ; i != poly.end(); i++ ) { //going through the entire list to retrieve the terms and print them
if (i == poly.end()) { // if we reached the last term
if (i->pow == 0) //if the last term's power is 0 we print only it's coefficient
cout << i->coef;
else
cout << i->coef << var << "^" << i->pow; //otherwise we print both
}
else {
if (i->coef > 0) //if the coef value is positive
cout << i->coef << var << "^" << i->pow << " + "; //we also add the '+' sign
else
cout << i->coef << var << "^" << i->pow << " - "; // otherwise we add '-' sign
}
}
}