简体   繁体   中英

How to assign class values to vector<string>?

I have a large textfile in which each line represents a city with postalcode and state. It is written like this:

Brandenburg  35432 Potsdamm
Niedersachsen 35698 Hannover

I've already read the file in a vector and I have written a class and want to assign the classvalues to the vector.

class City
{
    private:
        float lat; //latitude
        float lon; //longitude
    public:
        City cityclass(std::string state, std::string zipCode, std::string name);
        //std::string name;
        //std::string state;
        //std::string zipCode;

        float getLatitude() const
        {
            return lat;
        }
        float getLongitude() const
        {
            return lon;
        }
};

So I have a class with std::string zipCode, the state and the cityname. I assume that this would be better to work with, especially when I want to be able to search for city's by zip or name. How can I realize this? I thought about to simply modify my while-loop as it follows, but I'm realy not sure if this is the way to go. Here is my full code:

class City
{
    private:
        /*float lat; //latitude
        float lon; //longitude*/
    public:
        std::string zipCode;
        std::string name;
        std::string state;

        /*float getLatitude() const
        {
            return lat;
        }
        float getLongitude() const
        {
            return lon;
        }*/
};

int main ()
{
    std::ifstream input("bundesland_plz_ort_de.txt");
    //initilazing a vector of type string to store the data
    std::vector<City> cityVector;
    City city; //creating instance of class


    //check if file can be accessed
    if(!input)
    {
        std::cout << "ERROR!\tFile could not be opened!" << std::endl;
    }
    else
    {   

        while(input >> city.state >> city.zipCode >> city.name)
        {
            cityVector.push_back(city);
        }
        input.close(); // close after finishing
    }
}

I would recommend a slightly different approach. You defined a class City, but you read the data outside of the class.

In C++ we should put data on operations that are working on that data, all in one class. In this case you would (in your class) overwrite the inserter and extractor operator. The class knows, how to read and write its data. Even if you change the algorithm later, the rest of the code will work without modification.

In the following example code I put an ultra simple extractor and inserter (No error checking). Reading all data from the source file is then just one simple statement.

It is the definition of the variable "cl" as std::vector , using its range constructor. Very short and simple.

Usage of range constructor for std::vector.

We can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction").

Additionally, you can see that I do not use the "end()"-iterator explicitely.

This iterator will be constructed from the empty brace-enclosed initializer list with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that.

Having modified the class as described, you can then use all algorithm from the std library.

Please see:

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

struct City {
    // Data
    std::string state{};
    std::string zipCode{};
    std::string name{};

    // Member functions
    // Extractor
    friend std::istream& operator >> (std::istream& is, City& c) {
        return is >> c.state >> c.zipCode >> c.name;
    }
    // Inserter
    friend std::ostream& operator << (std::ostream& os, const City& c) {
        return os << "\nState:    " << c.state << "\nZip Code: " << c.zipCode << "\nName:     " << c.name;
    }
};

int main() {

    // Try to open file and check, if it worked
    if (std::ifstream sourceFile("r:\\bundesland_plz_ort_de.txt"); sourceFile) {

        // Read complete source file into a city list
        std::vector cl(std::istream_iterator<City>(sourceFile), {});

        // Give some Debug output
        std::copy(cl.begin(), cl.end(), std::ostream_iterator<City>(std::cout, "\n"));
    }
    else {
        std::cerr << "\nError: Source file could not be opened\n";
    }
}

Of course there are many other possible solutions . . .

just go on
first obtaining the number of object approximately by a loop precede it to count the total space delimited string then it's divided by the number of member data

 // ...
 //City city; //creating instance of class


 if(!input)
    {
        std::cout << "ERROR!\tFile could not be opened!" << std::endl;
    }
 else if (input.is_open())  {

    string buf(25);
    int i=0;
    while( input >> buf) ++i;

    std::vector<City> cityVector(i /3 +5);   // the number of member data plus some extra for sureness

    input.clear();
    input.seekg(0, ios::beg);                // point back to start
    i=0;
        while( input >> cityVector[i].state >> cityVector[i].zipCode >> cityVector[i].name) ++i;


    input.close(); // close after finishing
    }

  //..

this way need estimate the array size earlier approximately plus some guarding amount, then can resize, refit it eg. cityVector.resize(i+1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM