2010-11-16 3 views
5

Pour ce programme, j'ai uniquement utilisé des séparateurs de champs à partir de fichiers de données dans un script shell. Mais j'essaie d'utiliser la fonction de bibliothèque standard ifstream() pour lire à partir d'un fichier de données. Le seul problème est que je suis obtenir les données comme siC++ fonction ifstream et séparateurs de champs

A: KT5: 14: bureau exécutif:

Ceci est une table de hachage, et je dois séparer les valeurs dans la ligne de la structure de données ainsi que le type de transaction. J'ai regardé autour du Web et n'ai pas trouvé beaucoup sur les séparateurs de champ et ce que j'ai trouvé était assez confus. La question étant alors, est-il un moyen de définir un séparateur de champ avec la fonction ifstream ou existe-t-il une autre bibliothèque d'E/S standard que je devrais utiliser?

Merci.

Répondre

4

getline vous permet de spécifier un délimiteur. Vous pouvez ensuite lire l'entrée d'un flux comme une séquence de string séparés par _Delim:

template<class CharType, class Traits, class Allocator> 
    basic_istream< CharType, Traits >& getline(
     basic_istream< CharType, Traits >& _Istr, 
     basic_string< CharType, Traits, Allocator >& _Str, 
     CharType _Delim 
    ); 

Si ce sont des données uniformément structuré, il pourrait être utile de définir une structure pour contenir et mettre en œuvre operator>> pour charger chaque instance de le flux, en utilisant la fonction ci-dessus interne au code de l'opérateur.

Si vous devez traiter plusieurs lignes (de sorte que le saut de ligne est un séparateur d'enregistrements et un séparateur de champs), chargez chaque ligne dans stringstream en utilisant basic_istream::getline, puis post-traitez la ligne dans les champs comme indiqué.

+1

Wow ... et pas dans le bon sens. Je pense que je vais m'en tenir à C. – onemasse

+0

merci Steve !!! – rajh2504

+0

@onemasse, erm: la déclaration ressemble à: 'istream & getline (istream & is, chaîne & str, char delim);', n'est-ce pas si mal maintenant? :), sans parler de ce que 'std :: getline' vous donne sur l'équivalent C! :) – Nim

5

@Steve Townsend a déjà souligné une possibilité. Si vous préférez utiliser operator>> au lieu de std::getline, vous pouvez le faire aussi. Un istream traite toujours les espaces comme séparateur. Chaque flux a des paramètres régionaux associés et chaque paramètre régional inclut une facette ctype. Cette facette ctype est ce que le istream utilise pour déterminer quels caractères d'entrée sont des espaces. Dans votre cas, vous voulez apparemment que le flux traite uniquement les lignes et les deux-points comme des "espaces blancs" (par exemple, des séparateurs), tandis que le caractère espace réel est simplement traité comme un caractère "normal" et non comme un séparateur.

Pour ce faire, vous pouvez créer une facette comme ctype ceci:

struct field_reader: std::ctype<char> { 

    field_reader(): std::ctype<char>(get_table()) {} 

    static std::ctype_base::mask const* get_table() { 
     static std::vector<std::ctype_base::mask> 
      rc(table_size, std::ctype_base::mask()); 

     rc['\n'] = std::ctype_base::space; 
     rc[':'] = std::ctype_base::space; 
     return &rc[0]; 
    } 
}; 

Pour utiliser cela, vous devez « imprégner » le flux avec un lieu en utilisant cette facette:

int main() { 
    std::stringstream input("A:KT5:14:executive desk:"); 

    // have the stream use our ctype facet: 
    input.imbue(std::locale(std::locale(), new field_reader())); 

    // copy fields from the stream to standard output, one per line: 
    std::copy(std::istream_iterator<std::string>(input), 
       std::istream_iterator<std::string>(), 
       std::ostream_iterator<std::string>(std::cout, "\n")); 
    return 0; 
} 

Je suis le premier à admettre, cependant, que cela a quelques défauts. Tout d'abord, les locales et les facettes sont généralement assez mal documentées, donc les programmeurs C++ sont susceptibles de trouver cela assez difficile à comprendre (surtout quand tout le vrai travail se passe "sous les couvertures", pour ainsi dire).

Une autre possibilité est d'utiliser Boost Tokenizer. En toute honnêteté, c'est un peu plus de travail à utiliser - il faudra que vous fassiez quelque chose comme lire une chaîne, puis la séparer séparément.En même temps, il est bien documenté, assez largement connu, et cadre assez bien avec les idées préconçues des gens sur la façon de faire des choses comme ça, que beaucoup de gens trouveront probablement plus facile à suivre malgré la complexité supplémentaire.

+1

Je reconnais que les Locales/facettes sont mal comprises par beaucoup de développeurs C++. Mais c'est parce que peu de gens se soucient vraiment de leur code I18N ou L10N et donc ne s'embarrassent jamais d'apprendre ce genre de choses. Mais c'est une autre raison de continuer à le faire aussi souvent que possible pour montrer que lorsqu'il est utilisé correctement, il rend le code principal beaucoup plus facile à écrire et à comprendre. C'est un autre truc que j'ai appris avec des facettes et j'ai l'intention de l'utiliser dès que possible. –

Questions connexes