0

I'm trying to read every odd line into pair.first and every even line to pair.second and put it into a vector of pairs. I'd like to have a player and his overall in one pair in a vector. Here's my code :

while(getline(players, player) && players >> overall)
    {
        pick_players.push_back(make_pair(player, overall));
    }

Unfortunately it reads only the first two lines and the rest of vector output are just zeros. player is a string and overall is a integer, players is my fstream file name.

Dawid_O
  • 11
  • 2
  • Likely a duplicate of https://stackoverflow.com/questions/21567291/why-does-stdgetline-skip-input-after-a-formatted-extraction – fabian Jul 03 '22 at 10:47
  • [Why does std::getline() skip input after a formatted extraction?](https://stackoverflow.com/questions/21567291/why-does-stdgetline-skip-input-after-a-formatted-extraction) The first time it works, the next `getline` reads the leftover newline and `>>` tries to read a string into an int which fails and sets the stream in an error state so all input after that fails. Moral of the story, it's better to not mix `getline` and `>>` unless you are very careful. Use `getline` twice and a `stringstream` to convert the second line to an int. – Retired Ninja Jul 03 '22 at 10:48
  • `getline()` and a stream's `>>` operator respond to whitespace (particularly, but not limited to, handling of newlines). Because of that different handling, using them on the same stream can cause unexpected effects (e.g. one leaves a newline in the stream buffer after reading, the next operation returns immediately without reading anything - which is almost certainly not what you intend). Instead of mixing styles of input that way on the same stream, read EVERYTHING from the stream to a string using `getline()`, and then parse the string (with error checking) to extract values you seek. – Peter Jul 03 '22 at 10:49
  • Thank you very much, it's working as I wanted now! – Dawid_O Jul 03 '22 at 10:55

1 Answers1

0

It's not a good idea to interleave std::getline and operator>> for reading an input stream. Instead, you'd better stick to using std::getline twice here.

Something like this would do:

  • Loop the input stream until you can't read two lines (use std::getline for this).
  • Push back each reading into a std::vector<std::pair<std::string, int>>.
  • If there's a parsing error on the overall line, just continue looping.

[Demo]

#include <charconv>  // from_chars
#include <fmt/core.h>
#include <fmt/ranges.h>
#include <sstream>   // istringstream
#include <system_error>  // errc
#include <utility>   // pair
#include <vector>

int main() {
    std::istringstream iss{
        "player1\n"
        "1\n"
        "player2\n"
        "2ddd\n"
        "player3\n"
        "3   \n"
    };
    std::vector<std::pair<std::string, int>> players{};
    for (std::string player{}, overall_str{};
         getline(iss, player) and getline(iss, overall_str);) {
        int overall{};
        auto [ptr, ec] = std::from_chars(
            overall_str.data(), overall_str.data() + overall_str.size(), overall);
        if (ec == std::errc{}) {
            players.push_back({player, overall});
        } else {
            fmt::print("Error parsing overall line: {}\n", overall_str);
            continue;
        }
    }
    fmt::print("{}\n", players);
}

// Outputs:
//
//   [("player1", 1), ("player2", 2), ("player3", 3)]

You can strengthen the parsing of the overall line by:

  • trimming the input string, and
  • checking std::from_chars used all of the input string to convert to a number.

[Demo]

#include <boost/algorithm/string.hpp>
...
        boost::algorithm::trim(overall_str);
...
        if (ec == std::errc{} and ptr == overall_str.data() + overall_str.size()) {

// Outputs:
//
//   Error parsing overall line: 2ddd
//   [("player1", 1), ("player3", 3)]
rturrado
  • 7,699
  • 6
  • 42
  • 62