This is a c++ question. I want the code of the desired c++ function (see below) to be (as much as possible) in c-style and using c string library functions because I think that this will lead the quickest code in terms of execution time. (Am I wrong ? If so, how much ?) Yes, I value performance more than readability for this question because the desired function will be called a lot (millions) of times.
I am receiving const char *'s of the form "25Dec2016" that represent dates and I am parsing them to back out from them three int's (one for the day, the second for the month and the last for the year (that I assumed to be a number between 0 and 9999)) thanks to a function
Parse(const char * cDate, int & day, int & month, int & year)
I coded such a function and tested it : it works on correct const char*s (those that indeed represent date in my format), but I feel that my use of c functions (atoi for instance) is incorrect, even if I don't see why. There are also other problems :
the code is inelegant (the big if ... else if ... if) : one cannot do a switch statement on a string, but is there an elegant way to do this without resorting the std::map and c++11 ?
surely problematic from a c string point of view (I am not an expert) : for instance, I am really not happy with the way I extract the three substring into "buffers" ... Plus it appears I have problems with not null terminated char arrays that I'd like to correct. I could force a \0 at the end of _day and _year as I did for _month but I find that doing so is awful, so that I suspect a bad "design" of my parsing function
quite bad from an error handling point of view : the function is not a constructor for now, but could finally be, this is the reason why I throw.
I am open to any comments !
Here is the initial code :
Parse(const char * cDate, int & day, int & month, int & year)
{
if (0 == cDate)
{
throw "Error : null string pointer";
}
else
{
if (strlen(cDate) < 8)
{
throw "Error : invalid string format";
}
else
{
char _day[2];
char _month[4];
char _year[5]; // implictely the biggest year we authorize is 99999 ; it should suffice
for (int i = 0; i < 2; ++i)
{
_day[i] = cDate[i];
}
day = atoi(_day); // if fail, Undefined behaviour, see strtol for a more robust cross-platform alternative
char c;
for (int i = 2; i < 5; ++i)
{
c = cDate[i];
_month[i-2] = toupper(c);
}
_month[3] = '\0';
if (0 == strcmp("JAN", _month))
{
month = 1;
}
else if (0 == strcmp("FEB", _month))
{
month = 2;
}
else if (0 == strcmp("MAR", _month))
{
month = 3;
}
else if (0 == strcmp("APR",_month))
{
month = 4;
}
else if (0 == strcmp("MAY", _month))
{
month = 5;
}
else if (0 == strcmp("JUN", _month))
{
month = 6;
}
else if (0 == strcmp("JUL", _month))
{
month = 7;
}
else if (0 == strcmp("AUG", _month))
{
month = 8;
}
else if (0 == strcmp("SEP", _month))
{
month = 9;
}
else if (0 == strcmp("OCT",_month))
{
month = 10;
}
else if (0 == strcmp("NOV", _month))
{
month = 11;
}
else if (0 == strcmp("DEC", _month))
{
month = 12;
}
else
{
throw "Error : invalid month string";
}
for (int i = 5; i < 10; ++i)
{
_year[i-5] = cDate[i];
}
year = atoi(_year);
}
}
}
I finally opted for the function to be a constructor of a Date class, and inspired myself from rici's answer also using strtol as I intended initially (see comment in my initial code) instead of atoi:
#include <cstring> // for strlen
#include <ctype.h> // for toppuer
#include <stdlib.h>
int up(char c)
{
return toupper((unsigned char)(c));
}
Date::Date(const char * cDate)
{
if (0 == cDate)
{
throw "Error : null string pointer";
}
else
{
if (strlen(cDate) < 8)
{
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case"; // for now, valid format is 24Oct1979
}
else
{
char * ppEnd;
int day = strtol(cDate, &ppEnd, 10);
if (0 == day)
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case";
m_Day = day;
char cMonth[4];
int month;
memcpy(cMonth, &ppEnd[0], 3);
switch (up(cMonth[0]))
{
case 'A':
{
switch (up(cMonth[1]))
{
case 'P': if (up(cMonth[2]) == 'R') month = 4;
break;
case 'U': if (up(cMonth[2]) == 'G') month = 8;
break;
}
break;
}
case 'D':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'C')
month = 12;
break;
}
case 'F':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'B')
month = 2;
break;
}
case 'J':
{
switch (up(cMonth[1]))
{
case 'A': if (up(cMonth[2]) == 'N')
month = 1;
break;
case 'U': switch (up(cMonth[2]))
{
case 'N': month = 6;
case 'L': month = 7;
}
break;
}
break;
}
case 'M':
{
if (up(cMonth[1]) == 'A')
{
switch (up(cMonth[2]))
{
case 'R': month = 3;
case 'Y': month = 5;
}
}
break;
}
case 'N':
{
if (up(cMonth[1]) == 'O' && up(cMonth[2]) == 'V') month = 11;
break;
}
case 'O':
{
if (up(cMonth[1]) == 'C' && up(cMonth[2]) == 'T') month = 10;
break;
}
case 'S':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'P') month = 9;
break;
}
}
m_Month = (Month)month;
int year = strtol(ppEnd + 3, &ppEnd, 10);
if (0 == year)
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case";
m_Year = year;
updateSerial();
}
}
}
Remark. Being lazy, I did not throw everywhere I should in the "month" part of the code.
If your system is Posix-compatible, then you could just use strptime with the format %d%b%Y:
bool Parse(const char* date, int& day, int& month, int& year) {
struct tm components;
const char* rest = strptime(date, "%d%b%Y", &components);
if (rest == NULL || *rest != '\0') return false;
day = components.tm_mday;
month = components.tm_mon;
year = components.tm_year + 1900;
return true;
}
That is likely to be as fast as a naive parser, and it is certainly a lot easier to write :)
Otherwise, you should use strtol rather than atoi, since it will let you know both whether the parse was successful and where the next character is. And if you want to parse the month names quickly, you'll want to build a trie, either as a table or directly in code (the table is probably faster, fwiw):
static int up(char c) { return toupper((unsigned char)(c)); }
int parseMonth(const char* p) {
switch (up(p[0])) {
case 'A': {
switch (up(p[1])) {
case 'P': if (up(p[2]) == 'R') return 4;
break;
case 'U': if (up(p[2]) == 'G') return 8;
break;
}
break;
}
case 'D': {
if (up(p[1]) == 'E' && up(p[2]) == 'C') return 12;
break;
}
case 'F': {
if (up(p[1]) == 'E' && up(p[2]) == 'B') return 2;
break;
}
case 'J': {
switch (up(p[1])) {
case 'A': if (up(p[2]) == 'N') return 1;
break;
case 'U': switch (up(p[2])) {
case 'N': return 6;
case 'L': return 7;
}
break;
}
break;
}
case 'M': {
if (up(p[1]) == 'A') {
switch (up(p[2])) {
case 'R': return 3;
case 'Y': return 5;
}
}
break;
}
case 'N': {
if (up(p[1]) == 'O' && up(p[2]) == 'V') return 11;
break;
}
case 'O': {
if (up(p[1]) == 'C' && up(p[2]) == 'T') return 10;
break;
}
case 'S': {
if (up(p[1]) == 'E' && up(p[2]) == 'P') return 9;
break;
}
}
return -1;
}
Solution based on boost Spirit X3
#include <iostream>
#include <memory>
#include <string.h>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
namespace x3 = boost::spirit::x3;
struct Month_ : x3::symbols<std::uint8_t>
{
Month_()
{
add
("Jan", 1)
("Feb", 2)
("Mar", 3)
("Apr", 4)
("May", 5)
("Jun", 6)
("Jul", 7)
("Aug", 8)
("Sep", 9)
("Oct", 10)
("Nov", 11)
("Dec", 12);
}
}month_;
using ResultType = std::tuple<int, int, int>;
namespace grammar
{
using namespace x3;
auto const start_ = rule<struct start_, ResultType>{"start"}
= int_ >> month_ >> int_;
};
int main(int athc, char* argv[])
{
std::string str{"25Dec2016"};
auto beg = std::begin(str);
auto end = std::end(str);
ResultType res;
auto ret = x3::parse(beg,end, grammar::start_, res);
if(ret && (beg==end) )
{
std::cout << "parse done :" << std::get<0>(res) << " " << std::get<1>(res) << " " << std::get<2>(res) << "\n";
}
else
{
std::cout << "parse failed '" << std::string(beg, std::next(beg, 10)) << "'\n";
}
return 0;
}
Note, this works also with char*
Live On Coliru
Here are remarks on your code and some possible simplifications:
You can remove the else branches from the error handling if tests that throw an exception. As a matter of fact, you could just return a completion status instead of throwing exceptions.
Structured exceptions would be more precise than just throwing strings, but I'm not a C++ expert.
If the input string is null-terminated after the year part, there is no need to extract the number fields, but you might want to verify correct formatting.
Using an array to match the month part would greatly simplify that part.
Here is a simpler version:
typedef enum ParseStatus {
Parse_OK = 0,
Parse_NullStringPointer,
Parse_InvalidStringFormat,
Parse_InvalidDayNumber,
Parse_InvalidMonthName,
Parse_InvalidYearNumber
} ParseStatus;
ParseStatus Parse(const char *cDate, int &day, int &month, int &year) {
static const char months[4][12] = {
"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
};
static const int maxday[12] = {
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
char mon[4];
unsigned int dd, mm, yyyy;
char c;
if (!cDate) {
return Parse_NullStringPointer;
}
/* Using sscanf for a simple solution.
* If the string has the correct form, `sscanf` will convert 3 fields.
* extra spaces will be accepted and ignored.
*/
if (sscanf(cDate, "%u%3s%u%c", &dd, mon, &yyyy, &c) != 3) {
return Parse_InvalidStringFormat;
}
/* If you have `strupr(char *);`, you could use it here */
mon[0] = toupper((unsigned char)mon[0];
mon[1] = toupper((unsigned char)mon[1];
mon[2] = toupper((unsigned char)mon[2];
for (mm = 0; mm < 12; mm++) {
if (!strcmp(mon, months[mm]))
break;
}
if (mm >= 12) {
return Parse_InvalidMonthName;
}
/* Further validation on the day number */
if (dd < 1 || dd > 31 || dd > maxday[mm]) {
return Parse_InvalidDayNumber;
}
/* check for leap year, assuming gregorian calendar */
if (dd == 29 && mm == 1 &&
(yyyy % 4 != 0 || (yyyy % 100 == 0 && yyyy % 400 != 0)) {
return Parse_InvalidDayNumber;
}
/* check some limits for the year */
if (yyyy < 1 || yyyy > 9999) {
return Parse_InvalidYearNumber;
}
day = dd;
month = mm + 1;
year = yyyy;
return Parse_OK;
}
If you do not want to use sscanf(), you can use strtol() this way, but it is more cumbersome:
char *p;
int i;
dd = strtol(cDate, &p, 10);
for (i = 0; i < 3 && isalpha((unsigned char)p[i]); i++) {
mon[i] = toupper((unsigned char)p[i]);
}
mon[i] = '\0';
yyyy = strtol(p, &p, 10);
if (*p != '\0') {
return Parse_InvalidStringFormat;
}
When it comes to low level parsing I think std::strtol is your friend because it keeps track of the current pointer position in the string you are parsing. Not that that is critical in this type of string with fixed length components. Also std::strtol has easy error checking.
Also using iterators and algorithms helps to keep things neat and tidy.
This is about as "elegant" as I can make this using low level constructs:
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
void parse(const char* cDate, int& day, int& month, int& year)
{
static const char* months[] =
{
"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
};
char* pos;
day = std::strtol(cDate, &pos, 10);
if(std::distance(cDate, static_cast<const char*>(pos)) != 2)
throw std::runtime_error("bad date format");
char mon[4] = {};
for(auto m = std::begin(mon); m != std::end(mon) - 1; ++m)
*m = std::toupper(*pos++);
auto found = std::find_if(std::begin(months), std::end(months),
[&](const char* m){ return !std::strcmp(mon, m); });
if(found == std::end(months))
throw std::runtime_error("bad month format");
month = std::distance(months, found) + 1;
char* end;
year = std::strtol(pos, &end, 10);
if(std::distance(pos, end) != 4)
throw std::runtime_error("bad year format");
}
int main()
{
try
{
auto s = "25Dec2016";
int d, m, y;
parse(s, d, m, y);
std::cout << d << "/" << m << "/" << y << '\n';
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
N.B. Not heavily tested may contain bugs.
I am making a program for my C++ course to validate the date using different functions but mostly boolean. My problem is that it won't give false when it is. I have tried it using the else command instead of leaving the return false; without the else but it didn't seem to change anything. Here is the code though:
int main()
{
char Data[80];
int Month,Day,Year;
int *pMonth,*pDay,*pYear;
pMonth = &Month;
pDay = &Day ;
pYear = &Year ;
cout << "\n\t\tGive me date : ";
cin >> Data;
trial();
PauseScreen(28,20,3);
return 0;
}
void SetCursorPosition(int X, int Y)
{
COORD XY = { Y,X };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),XY);
}
void SetTextColor(int Color)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), Color);
}
void ClearScreen()
{
system("cls");
}
void PauseScreen(int x, int y, int color)
{
SetCursorPosition(x,y);
SetTextColor(color);
system("pause");
}
int InputValues(char *A, int *pM, int *pD, int *pY)
{
char Buffer[10];
Buffer[0] = A[0];
Buffer[1] = A[1];
Buffer[2] = '\0';
*pM = atoi(Buffer);
Buffer[0] = A[3];
Buffer[1] = A[4];
Buffer[2] = '\0';
*pD = atoi(Buffer);
Buffer[0] = A[6];
Buffer[1] = A[7];
Buffer[2] = A[8];
Buffer[3] = A[9];
Buffer[4] = '\0';
*pY = atoi(Buffer);
return strlen(A);
}
bool ValidateMonth(int A)
{
if ( A > 0 && A < 13 )
{
return true;
}
return false;
}
bool ValidateDay(int day,int month)
{
if ( month == 1 || month == 3 || month == 5 || month == 7 || month == 9|| month == 10|| month == 12 && (day > 0 && day < 32) )
{
return true;
}
return false;
}
bool ValidateDayTwo(int day,int month)
{
if ( month == 4 || month == 6 || month == 8 || month == 11 && (day > 0 && day < 31) )
{
return true;
}
return false;
}
void trial()
{
if(ValidateDay && ValidateDayTwo && ValidateMonth)
{
SetCursorPosition(10,10);
cout << "Date is Valid";
}
else
{
SetCursorPosition(10,10);
cout << "You done messed up BALAKI";
}
}
You're not actually calling your functions in your if statement. ValidDay, ValidDayTwo, ValidMonth
if(ValidateDay && ValidateDayTwo && ValidateMonth)
Instead you'll need to invoke the function by passing in arguments
if(ValidateDay(somearg1) && ValidateDayTwo(somearg2) && ValidateMonth(somearg2))
bool ValidateMonth(int A)
{
if ( A > 0 && A < 13 )
{
return true;
}
return false;
}
There is no need to say "If the condition is true, return true; otherwise, return false". You can simply return the result of evaluating the condition:
bool ValidateMonth(int A)
{
return A > 0 && A < 13;
}
You didn't call the functions correctly (well, you actually didn't call them at all) - there are no arguments:
if(ValidateDay(?) && ValidateDayTwo(?) && ValidateMonth(?))
For example, your ValidateDayTwo function takes two parameters.
The fact that the functions' return types are bool doesn't change anything, this doesn't do what you think it does. The name of the function is a pointer to the function itself, and you didn't get false as expected, because that pointer is not NULL.
I'm working on a program for a computer science class. I have everything completed and separated into the interface/implementation and for some reason, the program loops infinitely and says "Stack is empty!" after the peek() function is called.
I've tried inserting cout << statements to see if I can pinpoint the issue, however, no luck. I would appreciate it if someone else could take a look at it.
Thank You
Header
#ifndef STACK_H
#define STACK_H
/////////////////////////////////////////////////////
////Includes/////////////////////////////////////////
/////////////////////////////////////////////////////
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <stdlib.h>
#include <iomanip>
#include <sstream>
#include <stdio.h>
/////////////////////////////////////////////////////
using namespace std;
/*-------------------------------------------------------------------------------------------------------------*/
template <typename Object>
class Stack
{
private:
class stackListNode
{
public:
Object data;
stackListNode *next;
private:
//Nothing to declare-->placeholder since class
//sets data members to private initially
};
stackListNode *top;
public:
/////////////////////////////////////////////////
//Constructor Function//////////////////////////
Stack() {top = NULL;}
/////////////////////////////////////////////////
//Rest of functions defined inline for simplicity
void push(char token) // Push token onto the stack and create new node for top of stack
{
stackListNode *newNode = new stackListNode;
newNode->data = token;
newNode->next = top;
top = newNode;
}
int pop()
{
if(empty())
{
cout << "Stack is empty!\n" << endl;
return NULL;
}
stackListNode *temp = top;
top = temp->next;
}
char peek()
{
if(empty())
{
cout << "Stack is empty!\n" << endl;
return NULL;
}
return top->data;
}
int empty()
{
return top == NULL;
}
};
#endif
Main File:
/////////////////////////////////////////////////////
////Includes/////////////////////////////////////////
/////////////////////////////////////////////////////
#include <iostream>
#include <fstream>
#include <string>
#include "Stack.h"
/////////////////////////////////////////////////////
using namespace std;
int precendence(char stack_beginning); //ensures order of operations.
int precendence(char*myArray);
void InFixToPostfix(ifstream& in_file);
double EvaluatePostfix(double first_operand, double second_operand, char*myArray);
int main()
{
////VARS/////////////////////////////////////////////
string absolutePath;
cout << endl;
cout << "Please type in the name of the file you would to open \n";
cin >> absolutePath;
ifstream in_file;
in_file.open(absolutePath.c_str());
if(!in_file)
{
cout << "failed to open input file\n" ;
return 1 ;
}
else
{
InFixToPostfix(in_file); //kicks off program
}
}
void InFixToPostfix(ifstream& in_file)
{
string infix;
int right_parentheses =0;
int left_parentheses =0;
while(getline(in_file, infix))
{
char myArray[infix.size()];
strcpy(myArray, infix.c_str());
for(int i = 0; i < sizeof(myArray); i++)
{
if(myArray[i] == '(')
{
right_parentheses++;
}
if(myArray[i] == ')')
{
left_parentheses++;
}
}
if(right_parentheses!=left_parentheses)
{
cout << endl;
cout << "There is a typo in one of the expressions in your file \n";
cout << "Please fix it and rerun the program \n";
exit(1);
}
for(int i = 0; i < sizeof(myArray); i++)
{
//int number = int(myArray[i]);
//deferences the pointer and reads each char in the array
//as an int rather than a character.
//int number = myArray[i];
if(isxdigit(myArray[i]) > 0) //function used to check for hexidecimal values (i.e. int's)
{
goto exit_out;
}
else if(myArray[i] == '(' || myArray[i] == ')' || myArray[i] == '+' || myArray[i] == '-' || myArray[i] == '*' || myArray[i] == '/' || myArray[i] == '\\' || myArray[i] == '\n')
{
goto exit_out;
}
else if(myArray[i] == char(32)) //checks to see if there is a space
{
goto exit_out;
}
else
{
cout << endl;
cout << "There is an invalid character in the file\n";
exit(1);
}
exit_out:;
}
////////Declares a STRING Stack////////////////////////////////
Stack<char> stack_string;
////////Declares an Int Stack/////////////////////////////////
Stack<int> stack_int;
//////////////////////////////////////////////////////////////
for(int i = 0; i < sizeof(myArray); i++)
{
int number = isxdigit(myArray[i]);
if(number > 0)
{
cout << number;
//outputs the number b/c it is an operand
}
if(myArray[i] == '(')
{
stack_string.push(myArray[i]);
}
if(myArray[i] == ')')
{
//cout << "test";
while(stack_string.peek() != '(')
{
cout << stack_string.peek();
stack_string.pop(); //pops to the peek
}
stack_string.pop(); // if there is a ), pops to the peek
}
if(myArray[i] == '+' || myArray[i] == '-' || myArray[i] == '/' || myArray[i] == '*')
{
if(stack_string.empty())
{
stack_string.push(myArray[i]); //if there's nothing on the stack, pushes the current character
//goto done;
break; //breaks out of the statement
}
char stack_beginning = stack_string.peek();
int stack_top = precendence(stack_beginning);
//int stack_top = precendence(stack_string.peek());
int operatorHierarchy = precendence(myArray);
//must be declared here because i will have been interated through array
if(operatorHierarchy > stack_top)
{
stack_string.push(myArray[i]);
}
else if(operatorHierarchy <= stack_top) //could also be an if
{
dump_out:;
cout << stack_string.peek();
stack_string.pop();
int rerunThroughStack =0;
char new_stack_beginning = stack_string.peek();
rerunThroughStack = precendence(new_stack_beginning);
if(operatorHierarchy < stack_top)
{
stack_string.push(myArray[i]);
goto done; //could break
}
if(stack_string.peek() == '(')
{
stack_string.push(myArray[i]);
goto done;
}
if(stack_string.empty())
{
stack_string.push(myArray[i]);
goto done;
}
goto dump_out;
}
}
done:;
}
cout << stack_string.peek() << endl;
//////////Evaluate Section/////////////////////////////
for(int i = 0; i < sizeof(myArray); i++)
{
if(isxdigit(myArray[i]) > 0) //this is a number
{
stack_int.push(isxdigit(myArray[i]));
goto end;
}
else if(myArray[i] == '*' || myArray[i] == '+' || myArray[i] == '-' || myArray[i] == '/')
{
double first_operand;
first_operand = stack_int.peek(); //fetches first operand on the stack_int
stack_int.pop();
//////////////////
double second_operand;
second_operand = stack_int.peek();
stack_int.pop();
//////////////////
double answer;
answer = EvaluatePostfix(first_operand, second_operand, myArray); //THIS PROBABLY IS NOT RIGHT
stack_int.push(answer);
}
end:;
}
/*
int size_of_stack;
size_of_stack = stack_int.size();
if(size_of_stack == 1)
{
cout << stack_int.peek();
}
*/
}
}
double EvaluatePostfix(double first_operand, double second_operand, char* myArray) //might have to pass array as reference
{
/*
Cycle through the characters passed in through myArray[i];
*/
for(int i = 0; i < sizeof(myArray); i++)
{
if(myArray[i] == '*')
{
double first_answer;
first_answer = (second_operand * first_operand);
return first_answer;
}
else if(myArray[i] == '/')
{
double second_answer;
second_answer = (second_operand / first_operand);
return second_answer;
}
else if(myArray[i] == '+')
{
double third_answer;
third_answer = (second_operand + first_operand);
return third_answer;
}
else if(myArray[i] == '-')
{
double fourth_answer;
fourth_answer = (second_operand - first_operand);
return fourth_answer;
}
/*
else
{
cout << "There must be an error in your file" <<endl;
break;
exit(1);
}
*/
}
}
int precendence(char stack_beginning)
{
int precendence;
if(stack_beginning == '*' || stack_beginning == '/')
{
precendence = 2;
return precendence;
}
//by making it 2, the precendence is dubbed less than +/-
if(stack_beginning == '+' || stack_beginning == '-')
{
precendence = 1;
return precendence;
}
} //by making it 1, the precendence is dubbed greater than */"/"
int precendence(char*myArray)
{
int precendence;
for(int i = 0; i < sizeof(myArray); i++)
{
if(myArray[i] == '*' || myArray[i] == '/')
{
precendence = 2;
return precendence;
}
//by making it 2, the precendence is dubbed less than +/-
if(myArray[i] == '+' || myArray[i] == '-')
{
precendence = 1;
return precendence;
}
} //by making it 1, the precendence is dubbed greater than */"/"
}
So here's my problem:
I'm supposed to write a c++ program that checks a string to be balanced. So far I have the code working to make sure that it has the same number of ('s and )'s (the same with ['s and {'s). The problem is that this works for almost everything, but it doesn't work for strings where the {'s, ('s and ['s all get mixed up.
For example: "{ { [ ( ) ] } ( ) }" comes back as balanced (true) as it should. However, "{ ( [ ] } )" comes back true, but it shouldn't.
What are some ideas in the logic and/or code that would check for when they're out of order?
Thanks for any help!
In case it helps, my code follows:
bool ExpressionManager::isBalanced(string expression)
{
//remove whitespace
string edited;
for(int i = 0; i < expression.length(); i++)
{
if(expression[i] == ' ')
{
continue;
}
else
{
edited += expression[i];
}
}
expression = edited;
//set up brckets
string brackets;
for(int i = 0; i < expression.length(); i++)
{
if (expression.at(i)=='(')
{
brackets += expression.at(i);
}
if (expression.at(i)=='[')
{
brackets += expression.at(i);
}
if (expression.at(i)=='{')
{
brackets += expression.at(i);
}
if (expression.at(i)=='}')
{
brackets += expression.at(i);
}
if (expression.at(i)==']')
{
brackets += expression.at(i);
}
if (expression.at(i)==')')
{
brackets += expression.at(i);
}
}
int parenbal = 0;
int brackbal = 0;
int mustachebal = 0;
for (int i = 0; i<(brackets.size());i++)
{
if(brackets[i]=='(')
parenbal++;
if(brackets[i]=='[')
brackbal++;
if(brackets[i]=='{')
mustachebal++;
if(brackets[i]==')')
parenbal--;
if(brackets[i]==']')
brackbal--;
if(brackets[i]=='}')
mustachebal--;
}
bool isbalanced = false;
if ((mustachebal==0)&&(brackbal==0)&&(parenbal==0))
{
isbalanced = true;
}
//check for brackets mixed up with other stuff.
return isbalanced;
}
If you employ a Stack to store those tokens, then you are always looking for the closing-counterpart corresponding to the one on the top of the stack or an open-token.
The flow would be
If the token is an open token, push it onto the stack.
If the token is a close token, check if the top of the stack is the corresponding open-token. If it is, then pop the stack as you found them balanced. If it is not, then it's an error.
Seems more like a homework assignment. So I would comment accordingly and allow you to learn a few things.
Always initialize your variables. strings are not initialized in your code.
You do not iterate over the string three time, you can check the string only once.
Use if-else if-else structure instead of if-if-if structure.
Always use brackets braces
Be consistent with your usage, either use at() or [], but dont mix them in code.
//this code may help you check string for balanced brackets with no
//repeated brackets,paranthesis or braces (e.g. [2*{3/(1+2)}].Note: no repeatance of
//brackets
#include <iostream.h>
#include <conio.h>
#include "IntStack.h"
#include <stdio.h>
void main(void)
{
char bracket[20];
gets (bracket);
char arr[6];
int i=0;
while(i<20)
{
switch(bracket[i])
{
case '[':
{
arr[0]=1;
break;
}
case '{':
{
arr[1]=2;
break;
}
case '(':
{
arr[2]=3;
break;
}
case ')':
{
arr[3]=3;
break;
}
case '}':
{
arr[4]=2;
break;
}
case ']':
{
arr[5]=1;
break;
}
default:
cout<<"";
}
i++;
}
if(arr[3]==arr[2])
cout<<"";
else
cout<<" ) or ( is missing "<<endl;
if(arr[1]==arr[4])
cout<<"";
else
cout<<" } or { is missing "<<endl;
if(arr[5]==arr[0])
cout<<"";
else
cout<<" ] or [ is missing"<<endl;
}
void check_brackets (string bituy)
{
int flag = 1
int count[6] = {0, 0, 0, 0, 0, 0};
stack<char> barstack;
for (int i = 0; i < bituy.length(); i++)
{
if (bituy[i] == '{')
count[0]++;
else if (bituy[i] == '}')
count[1]++;
else if (bituy[i] == '(')
count[2]++;
else if (bituy[i] == ')')
count[3]++;
else if (bituy[i] == '[')
count[4]++;
else if (bituy[i] == ']')
count[5]++;
}
for (int i = 0; i < 6; i += 2)
{
if (count[i] != count[i+1])
{
cout << "Wrong Syntax!" << endl;
flag = 0;
break;
}
}
if (flag)
{
for (int i = 0; i < bituy.length(); i++)
{
if (bituy[i] == '{' || bituy[i] == '(' || bituy[i] == '[')
barstack.push(bituy[i]);
else
{
if ((barstack.top() == '{' && bituy[i] == '}') || (barstack.top() == '(' && bituy[i] == ')') || (barstack.top() == '[' && bituy[i] == ']'))
barstack.pop();
else
{
cout << "Wrong Syntax!" << endl;
flag = 0;
break;
}
}
}
}
if (flag)
cout << "No Errors!" << endl;
}
#include<bits/stdc++.h>
using namespace std;
bool isBalance(char n[],int size){
int i,count=0;
//int size=strlen(n);
for(i=0;i<size;i++){
if(n[i]=='{'||n[i]=='['||n[i]=='('){
count ++;
}
else if(n[i]=='}'||n[i]==']'||n[i]==')'){
count --;
}
else return -1;
}
if(count==0)
return true;
else
return false;
}
int main(){
char n[1000];
gets(n);
int size=strlen(n);
bool result=isBalance(n,size);
if(result==true)
cout<<"Balance";
else
cout<<"Not Balance";
return 0;
}
//bracket Chaecker program
void bracket_checker()
{
int i=0;
char d;
char ch;
int count=0;
char *s = new char[21];
fstream out;
out.open("brace.txt",ios::in);
while(out>>d)
{
if(d =='}'|| d ==')' || d == '{' || d =='(')
{
s[i]=d;
i++;
}
}
if (i % 2 != 0)
cout <<"\ninvalid braces";
else if (( s[0] == '}' || s[0]==')' || s[0]==']') || (s[i]=='{' || s[i]=='(' || s[i]=='[' ))
cout <<"\n invalid braces";
else
{
for(int a=0; a<i; a++)
{
if (s[a] == '(' || s[a] == '{' || s[a] =='[' )
push1(s[a]);
if((s[a]=='(' && (s[a+1]=='{' || s[a+1]=='}')) || (s[a]=='[' && (s[a+1]=='{' || s[a+1]=='}')))
break;
else
if (s[a] == ')' || s[a] == '}' )
{
if (head != NULL)
{
ch = pop1();
if( ch == '{' && s[a] == '}' || ch == '(' && s[a] == ')' || ch=='[' && s[a]==']')
cout <<" ";
else
break;
}
else
break;
}
}
if(head==NULL)
cout <<" valid";
else
cout <<"In Valid";
}
}