2010-02-22 64 views
0

Je dispose d'un fichier:Entrée C++ simple à partir du fichier ... comment?

P 0.5 0.6 0.3 
30 300 
80 150 
160 400 
200 150 
250 300 
T 
r 45 0 0 
s 0.5 1.5 0 0 
t 200 –150 
. 
. 
. 

Quand je lis dans « P » Je sais que 3 flotteurs suivront. Cela sera suivi d'un nombre fini de coordonnées X et Y. Le nombre variera jusqu'à ce qu'un «T» soit atteint que je dois reconnaître. Ensuite, il pourrait y avoir un 'r', 's' ou 't' suivi de quelques valeurs. Quoi qu'il en soit, je sais comment reconnaître 'P' et ensuite prendre les 2 flotteurs, mais alors je sais que je dois avoir une boucle while pour les coordonnées X et Y qui s'arrêteront quand j'arriverai à un 'T'. Je ne connais pas assez le C++ pour que la boucle s'arrête et reconnaisse le 'T' et fasse ensuite autre chose.

Un exemple à expliquer serait apprécié. Merci d'avance!

+0

BTW votre question m'a fait poser celui-ci: http://stackoverflow.com/questions/2309604 "Est-ce que quelqu'un utilise réellement les opérateurs d'extraction de flux?" – Dan

Répondre

9

Je vais vous montrer ce que je pense que c'est la façon correcte de faire ça en C++. Tout d'abord définir une classe pour représenter votre première ligne et pour faire son IO:

struct FirstLine 
{ 
    double x, y, z; 
    friend std::istream & operator>>(std::istream & is, FirstLine & data) 
    { 
     std::string line, ignore; 
     std::getline(is, line); 
     std::istringstream iss(line); 
     iss >> ignore >> data.x >> data.y >> data.z; 
     assert(ignore == "P" && iss); 
     return is; 
    } 
    friend std::ostream & operator<<(std::ostream & os, FirstLine const & data) 
    { 
     return os << "P " << data.x << " " << data.y << " " << data.z; 
    }  
}; 

J'ai ajouté une erreur de base de vérification avec assert, vous voudrez probablement quelque chose de plus solide dans votre programme final.

maintenant une classe pour les lignes moyennes:

struct MiddleLine 
{ 
    double x, y; 
    friend std::istream & operator>>(std::istream & is, MiddleLine & data) 
    { 
     std::string line; 
     std::getline(is, line); 
     if(line == "T") 
      is.clear(std::ios::failbit); 
     else 
     { 
      int n = sscanf(line.c_str(), "%lf %lf", &data.x, &data.y); 
      assert(n == 2); 
     } 
     return is; 
    } 
    friend std::ostream & operator<<(std::ostream & os, MiddleLine const & data) 
    { 
     return os << data.x << " " << data.y; 
    }  
}; 

Quand nous arrivons à la fin de la section où les lignes moyennes sont nous sommes censés rencontrer un « T ». Dans ce cas, nous élevons le bit d'échec du flux, ce qui indiquera au client qu'il n'y a plus de lignes intermédiaires à lire.

Enfin une classe pour les dernières lignes:

struct LastLine 
{ 
    std::string identifier; // r, s or t 
    std::vector<double> values; 
    friend std::istream & operator>>(std::istream & is, LastLine & data) 
    { 
     std::string line; 
     std::getline(is, line); 
     std::istringstream iss(line); 
     iss >> data.identifier; 
     assert(data.identifier == "r" || data.identifier == "s" 
       || data.identifier == "t"); 
     std::copy(std::istream_iterator<double>(iss), 
        std::istream_iterator<double>(), std::back_inserter(data.values)); 
     return is; 
    } 
    friend std::ostream & operator<<(std::ostream & os, LastLine const & data) 
    { 
     os << data.identifier << " "; 
     std::copy(data.values.begin(), data.values.end(), 
        std::ostream_iterator<double>(os, " ")); 
     return os; 
    }  
}; 

dernières lignes sont plus compliquées becase nous ne savons pas combien de valeurs sont dans chaque, donc nous lisons tout autant que nous le pouvons.

C'était la partie délicate. Maintenant, notre fonction principale sera simplement lire une première ligne, puis un nombre inconnu de lignes intermédiaires, et enfin un nombre inconnu de dernières lignes:

int main() 
{ 
    std::string const data = "P 0.5 0.6 0.3\n 
          "30 300\n" 
          "80 150\n" 
          "160 400\n" 
          "200 150\n" 
          "250 300\n" 
          "T\n" 
          "r 45 0 0\n" 
          "s 0.5 1.5 0 0\n" 
          "t 200 –150"; 

    std::istringstream iss(data); 

    FirstLine first_line; 
    iss >> first_line; 

    std::vector<MiddleLine> middle_lines; 
    std::copy(std::istream_iterator<MiddleLine>(iss), 
       std::istream_iterator<MiddleLine>(), 
       std::back_inserter(middle_lines)); 
    iss.clear(); 

    std::vector<LastLine> last_lines; 
    std::copy(std::istream_iterator<LastLine>(iss), 
       std::istream_iterator<LastLine>(), 
       std::back_inserter(last_lines)); 
    assert(iss.eof());  

    std::cout << first_line << "\n"; 
    std::copy(middle_lines.begin(), middle_lines.end(), 
       std::ostream_iterator<MiddleLine>(std::cout, "\n")); 
    std::copy(last_lines.begin(), last_lines.end(), 
       std::ostream_iterator<LastLine>(std::cout, "\n")); 
    return 0; 
} 

Ceci est la sortie que vous obtiendrez ::

P 0.5 0.6 0.3 
30 300 
80 150 
160 400 
200 150 
250 300 
r 45 0 0 
s 45 0 0 0.5 1.5 0 0 
t 45 0 0 0.5 1.5 0 0 200 

J'ai utilisé une chaîne comme source de mes données mais vous voudrez probablement lire un fichier.

Et c'est tout, vous pouvez voir que je n'ai pas écrit une seule boucle.

Here's the code dans le codepad.

+0

Excellent travail montrant comment faire cela avec 'op >>'. Quelques questions: 1) Pourquoi 'sscanf' pour' MiddleLine'? 2) Pourquoi 'getline' une ligne à la fois dans un' istringstream' au lieu de travailler directement avec le paramètre 'istream'? 3) Est-ce que 'failbit' est le bon bit à régler? 'istream_iterator' va jusqu'à la fin du flux, alors pourquoi pas' eofbit'? – Dan

+0

1) Je pensais que ce serait moins bavard de cette façon 2) Mélanger getline et les opérations formatées (op >>) vous oblige à utiliser 'clear' +' ignore' tout le temps, ce qui est pénible. 3) Ce n'est pas vraiment la fin du flux, juste la fin "logique" de la section des lignes médianes. Mais EOF aurait pu travailler, c'est vrai. – Manuel

+0

On dirait que c'est trop, n'est-ce pas? – user204724

0
  • Gardez une sorte de « état global »
  • Ecrire une boucle qui lit une ligne à partir du fichier jusqu'à la fin de fichier.
  • Lire la ligne dans un tampon
  • Vérifiez le premier caractère du tampon, si elle est P ou T ou r ou s ou t, changer l'état global de l'application
  • Si le premier caractère est un T , utilisez un sscanf (Buffer + 1, "% lf% lf% lf", & d'abord, & ensuite, & troisième) pour lire le reste de la ligne.
  • Faire quelque chose de similaire si le premier caractère est r, s ou t.
  • Si l'application est dans le 'P-state' scan juste le tampon à l'aide sscanf (tampon, "% lf% lf", & premier, & seconde)
0

Je pense que vous pouvez utiliser les flux standards
pour vérifier "P" et "T"
utiliser get (char & ch);
et le retrait (ch) pour le repousser au flux
et
yourstream >> x >> y >> endl;

http://www.cplusplus.com/reference/iostream/istream/putback/

// Example 
// istream putback 
#include <iostream> 
using namespace std; 

int main() { 
    char c; 
    int n; 
    char str[256]; 

    cout << "Enter a number or a word: "; 
    c = cin.get(); 

    if ((c >= '0') && (c <= '9')) 
    { 
    cin.putback (c); 
    cin >> n; 
    cout << "You have entered number " << n << endl; 
    } 
    else 
    { 
    cin.putback (c); 
    cin >> str; 
    cout << " You have entered word " << str << endl; 
    } 

    return 0; 
} 
+1

Pourquoi n'utilisez-vous pas 'cin.peek()' au lieu de 'get()' suivi de 'putback()'? – Manuel

0
utilisateur

lire la ligne de fichier texte en ligne, puis mot de chaîne d'exécution;

ifstream fin(your file) 
while(! fin.eof()) 
{ 
    string line = fin.getline(); 
    istringstream iss(line, istringstream::in); 
    string token; 
    while(iss >> token)  
    { 
     if (token.compare("T")) { 
     ... 
     } else { 
     float f = atof(token.c_str()); 
    } 
    } 
}