Home > Back-end >  Parsing text file lines in C
Parsing text file lines in C

Time:01-14

I have a txt file with data such as following:

regNumber     FName         Score1   Score2   Score3
385234     John Snow         90.0     56.0     60.8
38345234   Michael Bolton    30.0     26.5     
38500234   Tim Cook          40.0     56.5     20.2
1547234    Admin__One        10.0         
                      ...

The data is separated only by whitespace.

Now, my issue is that as some of the data is missing, I cannot simply do as following:

ifstream file;
file.open("file.txt")

file >> regNo >> fName >> lName >> score1 >> score2 >> score3

(I'm not sure if code above is right, but trying to explain the idea)

What I want to do is roughly this:

cout << "Reg Number: ";
cin >> regNo;

cout << "Name: ";
cin >> name;

if(regNo == regNumber && name == fname) {
  cout << "Access granted" << endl;
}

This is what I've tried/where I'm at:

ifstream file;
file.open("users.txt");
string line;

    
while(getline(file, line)) {
   stringstream ss(line);
   string word;
   while(ss >> word) {
      cout << word << "\t";
   }
   cout << " " << endl;
}

I can output the file entirely, my issue is when it comes to picking the parts, e.g. only getting the regNumber or the name.

CodePudding user response:

I would read the whole line in at once and then just substring it (since you suggest that these are fixed width fields)

CodePudding user response:

Handling the spaces between the words of the names are tricky, but its apparent from your file that each column starts at a fixed offset. You can use this to extract the information you want. For example, in order to read the names, you can read the line starting at the offset that FName starts, and ending at the offset that Score1 starts. Then you can remove trailing white spaces from the string like this:

string A = "Tim Cook       ";
auto index = A.find_last_not_of(' ');
A.erase(index   1);

CodePudding user response:

I would define a Person class.
This knows how to read and write a Person on one line.

class Person
{
    int                 regNumber;
    std::string         FName;
    std::array<float,3> scope;

    friend std::ostream& operator<<(std::ostream& s, Person const& p)
    {
        return p << regNumber << " " << FName << " " << scope[0] << " " << scope[1] << " " << scope[2] << "\n";
    }
    friend std::istream& operator>>(std::istream& s, Person& p)
    {
        std::string line;
        std::getline(s, line);

        bool   valid = true;
        Person tmp;     // Holds value while we check
        // Handle Line.
        // Handle missing data.
        // And update tmp to the correct state.

        if (valid) {
            // The conversion worked.
            // So update the object we are reading into.
            swap(p, tmp);
        }
        else {
            // The conversion failed.
            // Set the stream to bad so we stop reading.
            s.setstate(std::ios::bad);
        }
        return s;
    }
    void swap(Person& other) noexcept
    {
        using std::swap;
        swap(regNumber, other.regNumber);
        swap(FName,     other.FName);
        swap(scope,     other.scope);
    }
};

Then your main becomes much simpler.

int main()
{
    std::ifstream file("Data");
    Person        person;
    while (file >> person)
    {
        std::cout << person;
    }
}

It also becomes easier to handle your second part. You load each person then ask the Person object to validate that credentials.

class Person
{
     // STUFF From before:
    public:
        bool validateUser(int id, std::string const& name) const
        {
             return id == regNumber && name == FName;
        }
};

int main()
{

    int reg = getUserReg();
    std::string  name = getUserName();

    std::ifstream file("Data");
    Person        person;

    while (file >> person)
    {
        if (person.validateUser(reg, name))
        {
            std::cout << "Access Granted\n";
        }
    }
}
  •  Tags:  
  • Related