0

I am trying to read the logic gates names and their inputs from a file. I have been given a .bench file which gives the information about the gate name and its inputs. I have written a code below which gives me perfect results if the information is given in the following format:

firstGate = NAND(inpA, inpB, inpC)
secGate = NAND(1, 2)
30 = NAND(A, B)

PROBLEM: But if there is a change in the "white space" before = sign , after , or at some other place then my code doesn't work. For example, if the file is given in the following format then i am not able to read it correctly

first=NAND(inpA, inpB, inpC) //no space before and after "="
sec = NAND(1,2) //no space after ","

My code which is working for the first case is below:

int main(int argc, char* argv[])
{
    //Reading the .bench file
    ifstream input_file;
    input_file.open("circuit.bench");
    if(input_file.fail())
    {
        cout << "Failed to open Bench file.\n";
        return 1;
    }
    ///////

    string line;        
    while (getline( input_file, line ))  
    {
        ///For NAND
        size_t  first_index_nand, second_index_nand;
        string gate_name;

        const string nand_str = "NAND(";
        if ((first_index_nand = line.find(nand_str)) != string::npos)
        {
            gate_name = line.substr(0, first_index_nand - 3);
            cout<<"\nGate name: "<<gate_name;

            first_index_nand += nand_str.length() - 1;
            cout<<"\nInput to this gate: ";
            for (; first_index_nand != string::npos; first_index_nand = second_index_nand)
            {
                if ((second_index_nand = line.find_first_of(",)", first_index_nand)) != string::npos)
                {
                    string input_name = line.substr(first_index_nand + 1, second_index_nand++ - first_index_nand - 1);  
                    cout<<" "<<input_name;
                }
            }
        }
        cout<<"\n";
    }

    return 0;

}

Query: How should i modify my code in such a way that it should be able to read the name of gate and its inputs irrespective of their position w.r.t whitespaces?

Note: I have to deal with this problem using C++ code and its libraries only.

skm
  • 5,015
  • 8
  • 43
  • 104
  • 1
    I'm going to unhelpfully suggest, "Don't Do It Like This". If the datafiles you are trying to read are likely to be more complex, consider writing a parser (a search for "flex" and "bison" would be useful there). If the datafiles aren't likely to be any more complex, consider using a regular expression library. Clang's C++ library supports the new std::regex stuff, for example, but there are plenty of alternatives. – Rook May 12 '14 at 12:56
  • Thanks for your comment. The basic structure of datafiles will be like that only BUT whitespaces might change....so, what should be my logice to deal with that situation/. – skm May 12 '14 at 13:07
  • Find a regex library. Failing that, write a preprocessor in a scripting language that's more amenable to text manipulation (I'd use perl, for example) and get that to generate a cleaned-up file your simple C++ parser can read. Writing complex string manipulation routines by hand in low-level languages is a difficult and thankless task, especially when there are so many easier ways to deal with the problem. – Rook May 12 '14 at 13:09
  • oh...actually i have to deal with the problem only using C++ things..i am not supposed to use some other stuff – skm May 12 '14 at 13:14
  • Ahh, irrational homework requirements. Parser generators (like flex and bison) can be made to output C++ classes and functions, I believe. What compiler (and version) are you using? You might be able to use std::regex. – Rook May 12 '14 at 13:15
  • @Rook: i am using `gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)` – skm May 12 '14 at 13:17
  • Ah, the regex implementation in the older version of the gnu c++ library is broken. So I'll repeat my earlier suggestion of using flex/bison (or whatever) and generating a c++ parser class that you can easily use in your code. – Rook May 12 '14 at 13:23
  • how about removing all the whitespaces (using `#include `)and then finding the gate name and its inputs? – skm May 12 '14 at 13:28

2 Answers2

1

First answer: never write a handcrafted parser yourself :-)

1) use code generators for parsers like lex, yacc, bison ( a lot more ... )

2) you can get support for parsing from expect or regexp

3) look for serialization e.g. boost::serialize. If you modify the writer/reader it is possible to serialize into more complex formats which contains something like your configuration files.

If you really want to write your own parser, it mostly recommended to write a more or less complex state machine. But this can be done by tools much easier then by hand.

Sorr ythat I will not dig through your code, but my personal experience is, that it ends in tons of code lines to get a real working parser. And mostly the code is not maintainable anymore. So I want to advice you to use one of the three ( or any other option ) I provided :-)

Klaus
  • 24,205
  • 7
  • 58
  • 113
  • Thank you for your answer. Actually i don't know much about string stuff so, it will be really helpful if you could explain a little more about parsers etc. ..or just suggest me some link where i can read about it alongwith some exmaple. – skm May 12 '14 at 13:09
  • simply look at boost:serialize, regexp(c++11), and the manuals for lex,yacc,bison... – Klaus May 12 '14 at 13:10
  • 1
    For the simple examples he gives, a recursive descent parser shouldn't be too hard to write. And a good scanner isn't too difficult either. Still, both together probably represent a couple of hundred lines of code, for what might be only 10 or 20 with lex and yac. – James Kanze May 12 '14 at 13:21
  • and can get more complex if error handling, case sensitivity and additional requirements come like nested expressions. :-) Homework or productive code? If it is only for a lab it is ok to write it ones to see that is painful :-) – Klaus May 12 '14 at 13:24
  • I've written more than a few, using all sorts of different techniques. If the scanner and the parser are kept well separated, and the grammar isn't too complicated, doing it by hand isn't the end of the world. (Of course, for his simple examples, he could use just a regular expression---part of C++11.) – James Kanze May 12 '14 at 13:30
0

You should do as @Rook and @Klaus suggested , maybe using a simple xml file without a dtd and a libraty like Xerces http://xerces.apache.org/xerces-c/.

If you want to use your file format you should remove all the white spaces by hand you can find how for example here: What's the best way to trim std::string? or here: remove whitespace in std::string. Only after that you can extract the data with your algorithm.

Anyway try this it should work.

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>

using namespace std;

string trimWhiteSpaces(const string& line)
{
    string l = line;

    l.erase(std::remove_if( l.begin(), l.end(), ::isspace ), l.end());

    return l;
}

int main(int argc, char* argv[])
{
    cout << "starting... \n";

    ifstream _ifile;
    string fname = "gates.bench";

    _ifile.open(fname.c_str());
    if(!_ifile.is_open())
    {
        cerr << "Failed to open Bench file" << endl;
        exit(1);
    }

    string line;

    while(getline(_ifile, line))
    {

        line =  trimWhiteSpaces(line);

        size_t  first_index_nand, second_index_nand;
                string gate_name;

                const string nand_str = "NAND(";
                if ((first_index_nand = line.find(nand_str)) != string::npos)
                {
                    gate_name = line.substr(0, first_index_nand - 3);
                    cout<<"\nGate name: "<<gate_name;

                    first_index_nand += nand_str.length() - 1;
                    cout<<"\nInput to this gate: ";
                    for (; first_index_nand != string::npos; first_index_nand = second_index_nand)
                    {
                        if ((second_index_nand = line.find_first_of(",)", first_index_nand)) != string::npos)
                        {
                            string input_name = line.substr(first_index_nand + 1, second_index_nand++ - first_index_nand - 1);
                            cout<<" "<<input_name;
                        }
                    }
                }
                cout<<"\n";

    }

}

With a more OO approch

  #include <iostream>
#include <fstream>
#include <string>
#include <algorithm>

using namespace std;

class FileParser
{
public:
    FileParser (const string fname)
    {
        ifile.open(fname.c_str());
        if(!ifile.is_open())
        {
            exit(1);
        }
    }
    ~FileParser()
    {
        ifile.close();
    }
    void Parse()
    {
        string line;

        while(getline(ifile, line)){


        line =  trimWhiteSpaces(line);

        size_t  first_index_nand, second_index_nand;
        string gate_name;

        const string nand_str = "NAND(";
        if ((first_index_nand = line.find(nand_str)) != string::npos)
        {
        gate_name = line.substr(0, first_index_nand - 3);
        cout<<"\nGate name: "<<gate_name;

        first_index_nand += nand_str.length() - 1;
        cout<<"\nInput to this gate: ";
        for (; first_index_nand != string::npos; first_index_nand = second_index_nand)
        {
            if ((second_index_nand = line.find_first_of(",)", first_index_nand)) != string::npos)
            {

                string input_name = line.substr(first_index_nand + 1, second_index_nand++ - first_index_nand - 1);
                            cout<<" "<<input_name;
            }
           }
         }
         cout<<"\n";
    }
}
private:
    string trimWhiteSpaces(const string& line)
    {
        string l = line;

        l.erase(std::remove_if( l.begin(), l.end(), ::isspace ), l.end());

        return l;
    }

    ifstream ifile;

};

int main(int argc, char* argv[])
{

    FileParser fP("gates.bench");

    fP.Parse();
}
Community
  • 1
  • 1
abbadon
  • 48
  • 4