0
votes

Comment interpréter exactement la chaîne remplie par la fonction std :: getline (stream, string) en C ++

Je vois quelque chose que je ne comprends pas.

319528800,string1
319528801,string2
319528801,string3
319528802,string4

Le testfile dans le répertoire actuel a le format.

#include <iostream>
#include <fstream>
#include <string>
     
int main()
{

        std::ifstream someStream;
        someStream.open("testfile"); 
        std::string current_line;

        if ( someStream.good() )
        {

                while (std::getline(someStream, current_line))
                {

                        std::cout << current_line << "\n";
                        std::cout << std::string(std::begin(current_line), std::begin(current_line) + sizeof(long));
                        std::cout << "\n";

                }

        }

  return 0;

}


4 commentaires

sizeof est la taille en mémoire d'un type. Ce n'est pas le nombre de caractères nécessaires pour représenter un nombre.


Pourquoi pensez-vous que sizeof (long) va trouver la virgule? C'est une pensée magique, si vous pensez vraiment que cela fonctionnera. FYI sizeof (long) est une valeur fixe, généralement 4 ou 8.


Cela ne répond pas à la question, mais prenez l'habitude d'initialiser les objets avec des valeurs significatives plutôt que de les initialiser par défaut et d'écraser immédiatement les valeurs par défaut. Dans ce cas, cela signifie changer std :: ifstream someStream; someStream.open ("testfile"); à std :: ifstream someStream ("testfile"); . De plus, if (someStream.good () est doublement redondant. Les objets de flux ont une conversion implicite en bool , donc if (someStream) est tout c'est nécessaire. Et même cela n'est pas nécessaire; si le flux n'est pas dans un bon état, l'appel à getline échouera et vous n'obtiendrez aucune sortie.


Il n'y a aucune référence à une virgule dans le code, c'est donc important


4 Réponses :


0
votes

Voici un code qui recherche une virgule dans la ligne courante

while (std::getline(someStream, current_line))
{
    std::cout << current_line << "\n";
    // get position of comma
    size_t pos = current_line.find(',');
    // get string before comma
    std::string tmp = current_line.substr(0, pos);
    // convert to long
    long num = stol(tmp);

Notez que ce code suppose qu'il y a une virgule dans l'entrée, il plantera probablement s'il n'y en a pas. Vous devriez toujours vérifier vos données d'entrée.


1 commentaires

std :: stol (current_line) fonctionne très bien; il arrête l'analyse lorsqu'il atteint un caractère qui ne peut pas faire partie d'un entier, il n'est donc pas nécessaire d'extraire cette sous-chaîne.



0
votes

Vous compliquez excessivement l'analyse. Pour analyser une entrée du flux, vous pouvez faire quelque chose du genre

#include <fstream>
#include <iostream>
#include <string>

int main()
{
    std::ifstream someStream("testfile");

    char sep;
    unsigned long num;
    std::string current_line;
    while (someStream >> num >> sep >> current_line) {
        if (sep != ',')
            break; // Bad separator
        std::cout << num << ", " << current_line << "\n";
    }

    return 0;
}


6 commentaires

@john True, la norme ne spécifie pas la taille des types intégraux, seulement les plages. Je pense que "le plus souvent" suffirait.


semble raisonnable


std :: stoi (current_line) fonctionne très bien; il arrête l'analyse lorsqu'il atteint un caractère qui ne peut pas faire partie d'un entier, il n'est donc pas nécessaire d'extraire cette sous-chaîne.


std :: stoi devrait probablement être std :: stol , car il y a une implication que le code est censé créer un long .


@PeteBecker Merci, j'ai pensé que cela lancerait std :: invalid_argument . J'ai dû le chercher.


Re: «a dû chercher» - transformez cela en habitude.



0
votes

Tout ce dont vous avez besoin est:

long value = std::stol(current_line);

std :: stol ignore les espaces de début, puis analyse le texte restant sous forme de valeur entière. Il s'arrête lorsqu'il touche un caractère qui ne peut pas faire partie d'un entier. Donc, il s'arrêtera quand il atteindra ce , .


0 commentaires

0
votes

Préfère créer une structure qui représente chaque ligne

struct input_line {
  long number;
  std::string str;
  operator std::pair<long, std::string>() const &
  { return {number, str}; }
  operator std::pair<long, std::string>&&() &&
  { return {number, std::move(str)}; }
};
std::istream& operator>>(std::istream& in, input_line& line) {
  char comma;
  if (in >> line.number >> comma >> line.str) {
    if (number < 0) throw std::runtime_error("invalid number ");
    if (comma != ',') throw std::runtime_error("missing comma");
    if (str.empty()) throw std::runtime_error("missing string");
  }
}

int main() {
  std::ifstream someStream("testfile"); 
  std::istream_iterator<input_line> first(someStream);
  std::map<long, std::string> map(first, {});

  for(auto&& pair : map) 
    std::cout << pair.first << ',' << pair.second << '\n';
}

Et puis la lecture devient triviale:

int main() {
  std::ifstream someStream("testfile"); 
  input_line line;
  while(someStream >> line)
    std::cout << line.number << ',' << line.str << '\n';
}

en partant de cette idée, nous peut en fait utiliser istream_iterator pour lire directement du fichier dans la carte.

struct input_line {
  long number;
  std::string str;
};
std::istream& operator>>(std::istream& in, input_line& line) {
  char comma;
  if (in >> line.number >> comma >> line.str) {
    if (number < 0) throw std::runtime_error("invalid number ");
    if (comma != ',') throw std::runtime_error("missing comma");
    if (str.empty()) throw std::runtime_error("missing string");
  }
}


0 commentaires