I have refactored one of my old homework assignments (mainly to utilize std::stack
and some C++11), and I'm still having trouble making it less repetitive.
It reads in a text file, character by character, and determines whether all of the opening and closing brackets ({}
, ()
, and []
) match properly. All other characters are ignored.
If a mismatch is found, an error message will be displayed to specify the specific one, and the program will terminate. Otherwise, at the end, a message will be displayed to indicate that they all match.
Here are the possible error types:
missing opening bracket:
int main() { /*...*/ } ] // missing [
missing closing bracket:
int main() { /*...*/ // missing }
opening bracket closed with the wrong closing bracket:
int main() { /*...*/ ] // should close with }
My questions:
- Is pushing each opening bracket onto a stack a practical way of doing this? I feel that my approach with it isn't too practical as it involves a lot of conditionals. When a closing bracket is found, there has to be some way to determine if they match properly.
- Would an
std::map
be beneficial in serving as a look-up table for associating the opening and closing brackets with each other? I feel that it may help with DRY here. - Although it may seem easy to maintain all the error-checking in one function, should they still be split into separate functions? If so, should the messages be displayed in them or in
main()
? - Does it make sense to return
EXIT_SUCCESS
if the program terminated from a matching error but not a file error? I'm not sure if I should returnEXIT_FAILURE
instead, even though it already does that if the file cannot be opened.
I don't mind following an entirely different procedure if this one isn't very practical. If you have something more complicated in mind, please share it. I want to approach this the right way.
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <stack>
#include <string>
typedef std::stack<char> Brackets;
void pushOpeningBrackets(Brackets& opening, char ch)
{
if (ch == '{')
opening.push('{');
else if (ch == '(')
opening.push('(');
else if (ch == '[')
opening.push('[');
}
bool errorsFound(Brackets& stack, char openingBracket, char closingBracket)
{
// unmatched?
if (stack.empty())
{
std::cerr << "Unmatched " << closingBracket;
return true;
}
char topBracket = stack.top();
stack.pop();
// not a match?
if (topBracket != openingBracket)
{
if (topBracket == '{')
std::cerr << "Expected } but found " << closingBracket;
else if (topBracket == '(')
std::cerr << "Expected ) but found " << closingBracket;
else if (topBracket == '[')
std::cerr << "Expected ] but found " << closingBracket;
return true;
}
return false;
}
int main()
{
std::cout << "Enter a text file name: ";
std::string filename;
std::getline(std::cin, filename);
std::ifstream inFile(filename.c_str(), std::ios::in);
if (!inFile) return EXIT_FAILURE;
Brackets stack;
std::string fileLine;
while (inFile >> fileLine)
{
for (char ch : fileLine)
{
pushOpeningBrackets(stack, ch);
if (ch == '}')
{
if (errorsFound(stack, '{', '}'))
{
return EXIT_SUCCESS;
}
}
else if (ch == ')')
{
if (errorsFound(stack, '(', ')'))
{
return EXIT_SUCCESS;
}
}
else if (ch == ']')
{
if (errorsFound(stack, '[', ']'))
{
return EXIT_SUCCESS;
}
}
}
}
// checks for missing bracket or full match
if (!stack.empty())
{
char topBracket = stack.top();
stack.pop();
if ('{' == topBracket)
std::cerr << "Missing }";
else if ('(' == topBracket)
std::cerr << "Missing )";
else if ('[' == topBracket)
std::cerr << "Missing ]";
}
else
std::cout << "All brackets match!";
}