简体   繁体   中英

Reading from input file

I'm having trouble reading from an input file. The input file looks like this

Lionel Messi -10 43

Fernando Torres 9 -29

Cristiano Ronaldo 7 31

Wayne Rooney 10 37

Neymar 17 29

Andres Iniesta 8 32

Robin van Persie 19 20

Lionel Messi 10 43

Xavi Hernandez 6 36

Mesut Özil 10 38

Didier Drogba 10 35

Fernando Torres 9 29

Kaká 10 17

The problem is that I cant use the getline function because I want to store the name into a single variable to store into an array, and the first number into a variable and the second into another variable. I also tried to use the peek function but I have never learned that so I had no success with it. If anyone knows how to read until the end of the name and store it into a single variable that would be much appreciated.

This is what my code looks like when im reading from the input file

while(!fin.eof())
    {

     fin >> first >> last >> num >> point;

     if (num > 0 && point > 0)
     {
             list[i].firstname = first;
             list[i].lastname = last;
             list[i].number = num;
             list[i].points = point;
             i++;
     }
     else if (num < 0 || point < 0)
     {
             reject[j].firstname = first;
             reject[j].lastname = last;
             reject[j].number = num;
             reject[j].points = point;
             j++;
     }

    }

This works perfectly if the input has a first and a last name. I know the problem is on the fin >> first >> last >> num >> point;

but i am not exactly sure how to put first and last (and possibly middle) together

You can use std::getline to extract the lines, the parse the line into a std::vector of space separated words. Then you know that words.size() - 2 of the words are part of the name. For example:

std::fstream in("in.txt");
std::string line;

// Extract each line from the file
while (std::getline(in, line)) {
  std::istringstream line_stream(line);

  // Now parse line_stream into a vector of words
  std::vector<std::string> words(std::istream_iterator<std::string>(line_stream),
                                 (std::istream_iterator<std::string>()));

  int name_word_count = words.size() - 2;
  if (name_word_count > 0) {
    // Concatenate the first name_word_count words into a name string
    // and parse the last two words as integers
  }
}

It looks to me like you need to use getline , and then parse the line. One solution for parsing it might be to split the line just before the first digit, then trim the first half, and use it for the name, and parse the second half using an std::istringstream to read the two numbers. This will fail, of course, if someone has a digit as part of their name, but that seems to me to be a legitimate limitation. In other words, for each line, you'd do:

std::string::iterator first_digit
        = std::find_if( line.begin(), line.end(), IsDigit() );
if ( first_digit == line.end() ) {
    //  format error...
} else {
    name = trim( std::string( line.begin(), first_digit ) );
    std::istringstream parser( std::string( first_digit, line.end() ) );
    parser >> firstNumber >> secondNumber >> std::ws;
    if ( !parser || parser.get() != EOF ) {
        //  format error...
    } else {
        //  Do what ya gotta do.
    }
}

Something like this should work:

std::string str;
while(getline(infile, str))
{
  std::string::size_type pos;
  pos = str.find_last_of(' ');
  if (pos == std::string::npos || pos < 1)
  {
      cout << "Something not right with this string: " << str << endl;
      exit(1);
  }
  int last_number = stoi(str.substr(pos));
  str = str.substr(0, pos-1);    // Remove the number and the space.
  pos = str.find_last_of(' ');
  if (pos == std::string::npos || pos < 1)
  {
      cout << "Something not right with this string: " << str << endl;
      exit(1);
  }
  int first_number = stoi(str.substr(pos));
  str = str.substr(0, pos-1); 
  // str now contains the "name" as one string. 

  // ... here you use last_number and first_number and str to do what you need to do. 
}

Actually, you can use getline(); Use the two parameter method, passing it a stream and '\\n' as the delimiter.

Assign the entire line into one string, then split the string using a space as a delimiter, and covert the second two into integers.

1.) Open the file

std::ifstream myFile("somewhere.txt");

2.) Check to see if the file is open

if(myFile.is_open())

3.) Read until end of file

while(!myFile.eof())

4.) Read the first name into a first name array

myFile >> firstName[numberOfPeople];

5.) Read the last name into the last name array

myFile >> lastName[numberOfPeople];

6.) Read the integer into an integer array

myFile >> integer[numberOfPeople];

7.) Increment number of people

numberOfPeople++;

8.) When while is done, close file

myFile.close();

9.) If file did not open, report the error.

else
   std::cout << "\nFile did not open. " << std::endl;

You can use the getline function, you just have to parse the string. This can be achieved using strtok.

I have implemented a solution to your problem (mostly for my own learning). It works as intended, but it feels a bit lengthy.

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

struct PlayerData {
  std::string name;
  int number;
  int points;
};

std::ostream& operator<<(std::ostream& os, const PlayerData& p) {
  os<<"Name: "<<p.name<<", Number: "<<p.number<<", Points: "<<p.points;
  return os;
}

PlayerData parse(const std::string& line) {
  PlayerData data;  
  std::stringstream ss(line);  
  std::vector<std::string> tokens;
  std::copy(std::istream_iterator<std::string>(ss),
            std::istream_iterator<std::string>(),
            std::back_inserter<std::vector<std::string>>(tokens));
  data.points = std::stoi(tokens.at(tokens.size() - 1));
  data.number = std::stoi(tokens.at(tokens.size() - 2));
  for(auto it=tokens.begin(); it!=tokens.end()-2; ++it) {
    data.name.append(" ");
    data.name.append(*it);
  }
  return data;
}

int main(int argc, char* argv[]) {
  std::string line;
  std::vector<PlayerData> players;  

  { // scope for fp                                    
    std::ifstream fp(argv[1], std::ios::in);
    while(!fp.eof()) {
      std::getline(fp, line);
      if(line.size()>0) {
        players.push_back(parse(line));
      }
    }
  } // end of scope for fp

  // print list of players, or do whatever you want with it.
  for(auto p:players) {
    std::cout<<p<<std::endl;    
  }

  return 0;
}

Compile with a version of g++ supporting C++11 (in my case gcc 4.7.2).

[Prompt] g++ -oparseline parseline.cpp -std=c++11 -O2
[Prompt] ./parseline players.txt
Name:  Fernando Torres, Number: 9, Points: -29
Name:  Cristiano Ronaldo, Number: 7, Points: 31
Name:  Wayne Rooney, Number: 10, Points: 37
Name:  Neymar, Number: 17, Points: 29
Name:  Andres Iniesta, Number: 8, Points: 32
Name:  Robin van Persie, Number: 19, Points: 20
Name:  Lionel Messi, Number: 10, Points: 43
Name:  Xavi Hernandez, Number: 6, Points: 36
Name:  Mesut Özil, Number: 10, Points: 38
Name:  Didier Drogba, Number: 10, Points: 35
Name:  Fernando Torres, Number: 9, Points: 29
Name:  Kaká, Number: 10, Points: 17

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