Storing text file into a class

2019-09-03 03:17发布

问题:

I am currently trying to read in characters into a class that looks like this

 struct data {
    string segment; string name;
    double length; double radius; double wall_thickness;
    double young_modulus; double compliance;

};

I also have a vector that contains the following elements:

  2A
  Aorta_Ascendens
  2
  1.47
  .164
  4
  53.4
  2B
  Aorta_Ascendens
  2
  1.44
  .161
  4
  51.0
  3A

I want to read in the text file into each part, and currently this is my algorithm to continuously read through the text file and add each part respectively.

int place = 0;

        while (place != temp_data.size()){
            int counter = 0;
            for (counter; counter <= 7; ++counter){
                istringstream is(temp_data[place + counter]);

                if (counter == 0){ is >> a.segment; }
                if (counter == 1){ is >> a.name; }
                if (counter == 2){ is >> a.length; }
                if (counter == 3){ is >> a.radius; }
                if (counter == 4){ is >> a.wall_thickness; }
                if (counter == 5){ is >> a.young_modulus; }
                if (counter == 6){ is >> a.compliance; }

            }
            counter = counter - 1;     //since the next segment is at    temp_data[7], must subtract one to have place = 7.
            place = counter + place;

            v.push_back(a);
        }

The problem I ran into is trying to find a way to make sure the right part of the text goes into the right part of the object. for the first seven lines of text the object should be like this :

segment: 2A
name: Aorta_Ascendens
length: 2
radius: 1.47
wall_thickness: .164
young modulus: 4
compliance: 53.4

This should repeat the same way for the whole of the text file. temp_data is a vector that holds the elements that need to be added into the object, but I cant find a good way to continually cycle through the vector and put the elements into the right place.

The algorithm I made has a counter and a place holder, the counter would cycle through the vector points and the place holder would hold spot for the first object's data member, in this case segment. At the end of the algorithm, place should equal the size of temp_data and get out of the loop. But I am having a problem when it compiles and runs, it seems to be putting the wrong element into the wrong object member.

Any ideas?

回答1:

A simple, clean way to approach this is to write a custom streaming operator:

struct data {
    string segment; string name;
    double length; double radius; double wall_thickness;
    double young_modulus; double compliance;

    friend std::istream& operator>>(std::istream& is, data& d)
    {
        return is >> d.segment >> d.name >> d.length >> d.radius >> d.wall_thickness
                  >> d.young_modulus >> d.compliance;
    }
};

No temp_data, istringstream or counter.

To use this:

data d;
while (input_stream >> d)
    v.push_back(d);
if (input_stream.fail())
    ...print error / exit or whatver...
else
    ...use v...

(There are more declarative ways to use Standard algorithms and back_inserter iterators to copy from the stream to the vector, but IMHO simple and obvious is good)



回答2:

  • Manual approach

Well you could use a more classic approach of using a mapping in your fileformat (that is, if you have access to the spec (the content) of your files).

You already give the perfect example of what your text should look like:

segment: 2A
name: Aorta_Ascendens
length: 2
radius: 1.47
wall_thickness: .164
young modulus: 4
compliance: 53.4

What's the problem with that format ? So it gives better flexibility (safety) because you can check for sure that a member is what you expect to read. You could separate objects with a delimiter:

segment: 2A
name: Aorta_Ascendens
length: 2
radius: 1.47
wall_thickness: .164
young modulus: 4
compliance: 53.4
**@**
segment: 2B
name: Aorta_Ascendens
length: 3
radius: 1.00
wall_thickness: .164
young modulus: 4
compliance: 53.4

Use ifstream.readlines() to get an array of C++ style strings, which will help you parse using find() function (returns string::npos if not found).
In the Parse() function it involves iterating over the lines of the file, first, use find to look for : character, takes the left part using substr to check which member to assign the value (and how to parse, integer, enumeration ?...) and another substr with a little calculation (eg size - posfound) to take the right part, and put that into your lexical_cast of choice. Either boost::lexial_cast or std::istringstream.

  • boost way

Otherwise, you have the choice of using boost::serialization which will let you write only one super simple function to read this kind of formats. (cf annex)

  • data binding (to xml, jason, ini...)

Or more complex data binding frameworks like Code Synthesis XSD (Xerces?):
http://www.codesynthesis.com/products/xsd/

  • Python !

And even more simple, use python. you just have to do eval()

PS: Annex (with boost::serialization) taken from the doc :

class gps_position
{
private:
    friend class boost::serialization::access;
    // When the class Archive corresponds to an output archive, the
    // & operator is defined similar to <<.  Likewise, when the class Archive
    // is a type of input archive the & operator is defined similar to >>.
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & degrees;
        ar & minutes;
        ar & seconds;
    }
    int degrees;
    int minutes;
    float seconds;
public:
    gps_position(){};
    gps_position(int d, int m, float s) :
        degrees(d), minutes(m), seconds(s)
    {}
};

isn't it stupid simple ? and its guaranteed to work. (well as long as you wrote the file with the same serializer).
refer to http://www.boost.org/doc/libs/1_55_0/libs/serialization/doc/index.html