-4

I want to read an input file in C++ which includes the following lines

- numberOfStates
- numberOfSymbols
- numberOfFinalStates
- list of final states (one per line)
- numberOfTransitions
- listOfTransitions (one per line. The transitions include two ints and one char)

It's important to say that the numbers are different in each input file. I have to read a different amount of lines for each file.

This is an example inputFile

10 
3 
1 
9 
12 
0 1 f 
0 3 f 
1 2 a 
2 9 f 
3 4 f 
3 8 f 
4 5 b 
5 6 f 
6 7 a 
8 9 f 

How can I declare each variable while reading the input file?

This is where I'm stuck.. don't really know what to do

ifstream fin("inputFile.txt");

    int numberOfStates;
    int numberOfSymbols;
    int numberOfFinalStates;
    // I'm not sure how to declare the next variables because they will vary in size each time 


    while (fin >> numberOfStates >> numberOfSymbols >> numberOfFinalStates)
    {
        cout << numberOfStates << numberOfSymbols << numberOfFinalStates << endl;
    }

I'd like to work with vectors if possible.

Salo Charabati
  • 147
  • 1
  • 2
  • 12

3 Answers3

1

A bit late, but I'll post it anyway to show how you can create your own stream operators and use them when creating composite classes.

#include <iostream>
#include <fstream>
#include <vector>

struct transition {
    // rename the variables into something meaningful
    int int1;
    int int2;
    char a_char;

    friend std::istream& operator>>(std::istream&, transition&);
    friend std::ostream& operator<<(std::ostream&, const transition&);
};
// input stream function for reading one transition
std::istream& operator>>(std::istream& is, transition& t) {
    is >> t.int1 >> t.int2 >> t.a_char;
    return is;
}
// output stream function for writing one transition
std::ostream& operator<<(std::ostream& os, const transition& t) {
    os << t.int1 << " " << t.int2 << " " << t.a_char;
    return os;
}
//-----------------------------------------------------------------------------
struct entity {
    int numberOfStates;
    int numberOfSymbols;
    std::vector<int> finalStates;
    std::vector<transition> transitions;

    friend std::istream& operator>>(std::istream&, entity&);
    friend std::ostream& operator<<(std::ostream&, const entity&);
};
// read one entity from a stream
std::istream& operator>>(std::istream& is, entity& e) {
    int numberOfFinalStates, numberOfTransitions;
    int value;

    if(is >> e.numberOfStates >> e.numberOfSymbols >> numberOfFinalStates) {
        // read to value and put it in its vector
        while(numberOfFinalStates-- && is >> value) e.finalStates.push_back(value);

        if(is >> numberOfTransitions) {
            transition ttmp;
            // read to the temporary transition and put it in its vector
            while(numberOfTransitions-- && is >> ttmp) e.transitions.push_back(ttmp);
            // check that we got the number of values we wanted
            // and set the failbit if we didn't (should check size() of the vectors
            // instead)
            if(numberOfFinalStates != -1 || numberOfTransitions != -1)
                is.setstate(std::ios_base::failbit);
        }
    }
    return is;
}
// write one entity to a stream
std::ostream& operator<<(std::ostream& os, const entity& e) {
    os << e.numberOfStates << "\n" << e.numberOfSymbols << "\n" << e.finalStates.size() << "\n";
    for(const int fs : e.finalStates) os << fs << "\n";
    os << e.transitions.size() << "\n";
    for(const transition& t : e.transitions) os << t << "\n";
    return os;
}
//-----------------------------------------------------------------------------
int main() {
    std::ifstream fs("inputfile.txt");
    if(fs) {
        entity e;
        // stream the opened file into the entity 
        if(fs >> e) {
            std::cout << "loaded these values:\n";
            std::cout << e;
        } else {
            std::cerr << "failed loading file\n";
        }
    } else {
        std::cerr << "failed opening file\n";
    }
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • Please add a sample input file so new users like me can run the code and learn it. – Mohammad Mar 16 '20 at 01:37
  • @Mohammad The sample input file in OP:s question should work. – Ted Lyngmo Mar 16 '20 at 06:32
  • Sorry, but what does OP:s stand for? – Mohammad Mar 16 '20 at 22:56
  • 1
    @Mohammad Oh, there's a good description here: [What is an OP when referring to Stack Exchange?](https://meta.stackoverflow.com/questions/253162/what-is-an-op-when-referring-to-stack-exchange) – Ted Lyngmo Mar 17 '20 at 06:31
  • Excellent, Original Poster (OP) - I can use online IDE for execution to your sample code above. This does not accept input file. Any ideas? I have Windows 10 laptop machine ONLY. – Mohammad Mar 17 '20 at 13:31
  • 1
    @Mohammad It seems like OP has a bug in the original data :-) It says that there should be 12 transitions but there are only 10. I've put the data in an `std::istringstream` instead of in a file so you can see that it's working: [Demo](https://godbolt.org/z/u5_Etu) – Ted Lyngmo Mar 17 '20 at 13:48
  • Perfect. I can see that it's working. I also installed VS2019 for free, and ran the code successfully. However, I would need to modify code in such a way that save output in a .csv /excel file instead of output Console Window. In addition to this, can we read data from .csv instead of putting data in std::istringstream. – Mohammad Mar 17 '20 at 15:01
  • 1
    @Mohammad Sure, the `operator<<` and `operator>>` overloads work with any `ostream`/`istream`, like `fstream`s. If your values are comma separated you'll need to adapt those operators though since right now they work with whitespace separated values. – Ted Lyngmo Mar 17 '20 at 15:10
  • But, how to adapt those operators? Let us create a simple example - e.g. format a report on COVID19 cases :) Input Headers below: Country, States, Cases USA, Michigan, 54 Italy, Lombardy, 14649 …. The code should read this from .csv file and write the results into .csv file. It is nice to also code output total number of cases. Can you modify above code for this simpler - basic example, please. – Mohammad Mar 17 '20 at 15:28
  • @Mohammad There should be a lot of examples how to read a stream with comma separated values here on SO so just search around for it. Some takes care of quoted strings and some don't so you'll have to know the exact format you need to support and then try to implement the operators to support it. If it doesn't work, create a question here at SO. The comments section isn't really a good place to make elaborate answers. Bad formatting :-) – Ted Lyngmo Mar 17 '20 at 15:36
  • 1
    Agreed with you on elaborated answers in comments box and bad formatting! Unfortunately, many examples are not generic, neat, and clean - as far as I have searched. My goal was to simply create an added value to your answer on this question above by asking a revised version of code. Thanks Ted. – Mohammad Mar 17 '20 at 15:47
  • This here at https://stackoverflow.com/questions/38173857/how-to-calculate-something-in-c maybe a good one to be modified. Can you adopt that to syntax that you used at your answer above and posted the modified code here, please. – Mohammad Mar 17 '20 at 16:07
  • @Mohammad That would not be very good since SO is supposed to be in Q&A format. I can't post an answer to a question that's not been asked. You should try to adapt it yourself and if you get stuck, post a question about the problem you got stuck on. If you provide a [mcve] and describe what you expect and what happens instead, you should get answers very quickly. – Ted Lyngmo Mar 17 '20 at 16:18
  • Sure - I have already created a new question with minimal reproducible example from SO and described about the problem I got stock on. – Mohammad Mar 17 '20 at 16:37
0

doing

while (fin >> name >> var1 >> var2 >> var3)
{
    cout << name << var1 << var2 << var3 << endl;
}

you rewrite all the time on the same variables, you need to put the value in a vector as you say in your question

you need also to check if each input is correct, currently you do not detect the invalid input

and of course you need to check you open the file


Example :

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

int main()
{
  std::ifstream fin("inputFile.txt");

  if (!fin.is_open()) {
    std::cerr << "cannot open inputFile.txt" << std::endl;
    return -1;
  }

  int numberOfStates, numberOfSymbols, numberOfFinalStates;

  if ((! (fin >> numberOfStates >> numberOfSymbols >> numberOfFinalStates))
      || (numberOfStates < 0)
      || (numberOfSymbols < 0)
      || (numberOfFinalStates < 0)) {
    std::cerr << "invalid file" << std::endl;
    return -1;
  }

  // final states are int, so memorize them in a vector of int
  // because their number is known I can size it rather than 
  // to 'push_back' each value
  std::vector<int> finalStates(numberOfFinalStates);

  for (int & ft : finalStates) {
    if (! (fin >> ft)) {
      std::cerr << "invalid file reading final states" << std::endl;
      return -1;
    }
  }

  int numberOfTransitions;

  if (!(fin >> numberOfTransitions) || (numberOfTransitions < 0))  {
    std::cerr << "invalid file reading the number of transitions" << std::endl;
    return -1;
  }

  // you say a transition contains 2 int and a char,
  // I define a structure for
  // i1 i2 and c are 'poor' names but I don't know their goal
  struct Transition {
    int i1, i2; 
    char c;
  };

  // the transitions saved in a vector
  std::vector<Transition> transitions(numberOfTransitions);

  for (Transition & tr : transitions) {
    if (!(fin >> tr.i1 >> tr.i2 >> tr.c)) {
      std::cerr << "invalid file reading transitions" << std::endl;
      return -1;
    }
  }

  // print to check

  std::cout << "numberOfStates=" << numberOfStates << std::endl;
  std::cout << "numberOfSymbols=" << numberOfSymbols << std::endl;
  std::cout << "numberOfFinalStates=" << numberOfFinalStates << "\nfinalStates:";
  for (int ft : finalStates) 
     std::cout << ' ' << ft;
  std::cout << std::endl;
  std::cout << "numberOfTransitions=" << numberOfTransitions
    << "\ntransitions:" << std::endl;
  for (const Transition & tr : transitions)
    std::cout << '\t' << tr.i1 << ' ' << tr.i2 << ' ' << tr.c << std::endl;

  return 0;
}

Compilation and execution :

pi@raspberrypi:/tmp $ g++ -pedantic -Wextra -Wall a.cc
pi@raspberrypi:/tmp $ cat inputFile.txt
10 
3 
1 
9 
12 
0 1 f 
0 3 f 
1 2 a 
2 9 f 
3 4 f 
3 8 f 
4 5 b 
5 6 f 
6 7 a 
8 9 f 
pi@raspberrypi:/tmp $ ./a.out
invalid file reading transitions

The error comes because the file contains only 10 transitions rather than 12 expected, modifying the file to expect 10 transitions :

pi@raspberrypi:/tmp $ ./a.out
numberOfStates=10
numberOfSymbols=3
numberOfFinalStates=1
finalStates: 9
numberOfTransitions=10
transitions:
    0 1 f
    0 3 f
    1 2 a
    2 9 f
    3 4 f
    3 8 f
    4 5 b
    5 6 f
    6 7 a
    8 9 f
pi@raspberrypi:/tmp $ 
bruno
  • 32,421
  • 7
  • 25
  • 37
0

If you have a variable number of something (that is defined at runtime) you use an array:

std::vector<Type>   store;

To add things to an vector you use push_back() (there are other methods but let us keep it simple for a beginner).

store.push_back(value);

To read multiple things and store them in a vector you simply use a loop.

for(int loop = 0; loop < numberOfThings; ++loop) {
    Type  temp;
    fin >> temp;
    store.push_back(temp);
}

So what is this mysterious Type? You use an appropriate type name here. For "Finale State" it would be an integer (int), but for "Transition" it would be a class type that matches (int/int/char).

 std::vector<int>   finalState;
 for(int loop = 0; loop < finalState; ++loop) {
     int nextFinal;
     find >> nextFinal;
     finalState.push_back(nextFinal);
 }

 ......
 std::vector<Transition>  Transitions;
 ... Just like above.
Martin York
  • 257,169
  • 86
  • 333
  • 562