0

I have this code as my default constructor. I am trying to read all the values correctly from my file (census2020_data.txt). The file contains letters and numbers, made up of states and a value representing population. One of the values in the data file has spaces (it is multiple words) and it makes my program not read from the file correctly.

How do I incorporate the getline() function to help read from my file correctly? I'll attach the fill contents below as an example.

Constructor:

state_class::state_class()
{
    //intially count, capacity, and pop_DB are initialized with the following values:
    count = 0;
    capacity = 5;
    ifstream in;
    pop_DB = new population_record[capacity];
    
    in.open("census2020_data.txt");
    //check if file can be opened

    while (!in.eof())
    {
        if (Is_Full())
        {
            double_size();
        }
        else
        {
            in >> pop_DB[count].state_name >> pop_DB[count].population;
            count++;
        }
    }

    in.close();
}

census2020_data.txt:

Alaska 710231
Arizona 6392017
District of Columbia 601723
Arkansas 2915918
California 37253956
Connecticut 3574097
Delaware 897934
Florida 18801310
Indiana 6483802
Maine 1328361
Oregon 3831074
Pennsylvania 12702379
Rhode Island 1052567
South Carolina 4625364
Maryland 5773552
Massachusetts 6547629
Michigan 9883640
Colorado 5029196
Minnesota 5303925
Mississippi 2967297
Iowa 3046355
Kansas 2853118
Kentucky 4339367
Louisiana 4533372
Missouri 5988927
Montana 989415
South Dakota 814180
Tennessee 6346105
Texas 25145561
Utah 2763885
Vermont 625741
Nebraska 1826341
Nevada 2700551
New Hampshire 1316470
New Jersey 8791894
Georgia 9687653
Hawaii 1360301
Idaho 1567582
Illinois 12830632
New Mexico 2059179
New York 19378102
North Carolina 9535483
North Dakota 672591
Ohio 11536504
Oklahoma 3751351
Virginia 8001024
Washington 6724540
West Virginia 1852994
Wisconsin 5686986
Wyoming 563626
Puerto Rico 3725789
Alabama 4779736
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Brooke O
  • 3
  • 3
  • You could use `std::getline` to read a line, and then parse the line using a `std::regex` or make a parsing grammar using Boost Spirit X3. – Eljay Sep 16 '21 at 21:10
  • 1
    `while (!in.eof())` -> `while (std::getline(in, astring))`. Then operate on `astring` with your choice of parsing tools. – user4581301 Sep 16 '21 at 21:10
  • 1
    Side note: [Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons) – user4581301 Sep 16 '21 at 21:12

1 Answers1

1

operator>> stops reading on whitespace, which is not what you need in this case.

I would suggest using std::getline() to read an entire line, then use std::string::rfind() to find the last space character before the numbers, and then use std::string::substr() to split the text from the numbers. For example:

state_class::state_class()
{
    //intially count, capacity, and pop_DB are initialized with the following values:
    count = 0;
    capacity = 5;
    pop_DB = new population_record[capacity];
    
    //check if file can be opened
    std::ifstream in("census2020_data.txt");

    std::string line;
    while (std::getline(in, line))
    {
        if (Is_Full())
            double_size();

        auto pos = line.rfind(' ');
        pop_DB[count].state_name = line.substr(0, pos);
        pop_DB[count].population = std::stoi(line.substr(pos+1));
        ++count;
    }
}

That being said, rather than maintaining your own population_record[] array manually, consider using std::vector<population_record> instead. Let the standard library handle the memory management for you:

private:
    std::vector<population_record> pop_DB;

state_class::state_class()
{
    //check if file can be opened
    std::ifstream in("census2020_data.txt");

    std::string line;
    while (std::getline(in, line))
    {
        auto pos = line.rfind(' ');
        population_record rec;
        rec.state_name = line.substr(0, pos);
        rec.population = std::stoi(line.substr(pos+1));
        pop_DB.push_back(rec);
    }
}

// use pop_DB.size() and pop_DB[index] as needed...

I would even go as far as suggesting to implement operator>> for reading population_record entries directly from the ifstream:

std::istream& operator>>(std::istream& in, population_record &rec)
{
    std::string line;
    if (std::getline(in, line))
    {
        auto pos = line.rfind(' ');
        rec.state_name = line.substr(0, pos);
        rec.population = std::stoi(line.substr(pos+1));
    }
    return in;
}

...

private:
    std::vector<population_record> pop_DB;

state_class::state_class()
{
    //check if file can be opened
    std::ifstream in("census2020_data.txt");

    population_record rec;
    while (in >> rec) {
        pop_DB.push_back(rec);
    }
}

// use pop_DB.size() and pop_DB[index] as needed...
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770