2010-11-26 2 views
13

je dois analyser dans un fichier texte qui contient quelque chose comme:Parse dans un fichier texte, la création d'instance de nouvel objet C++

1|Song Title|Release date||"ignore me"|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0 

qui est le numéro du morceau, suivi de la date de sortie, suivi d'un site web que je dois ignorer, suivi d'une série de 0 et de 1 qui pourrait représenter un vecteur de genres.

J'ai besoin d'un moyen de séparer ces données, et d'ignorer celui qui dit le site tout en créant une nouvelle instance d'un objet Song qui a un: (int songNumber, string songTitle, vector * genres, string releaseDate)

Merci!

Répondre

4
  • Définir une classe Song qui contient les données sous la forme dont vous avez besoin, comme vous l'avez dit plus haut
  • mettre en œuvre Song::operator>>(const istream&); pour peupler la classe en analysant les données ci-dessus à partir d'un flux d'entrée
  • lire la ligne de fichier par ligne en utilisant string::getline
  • pour chaque ligne, convertissez en stringstream puis utilisez votre operator>> pour remplir les champs dans une instance de Song.

Il est facile de marquer la chaîne avec le signe '|' caractère comme un séparateur, ce qui serait la majeure partie du travail.

int main() 
{ 
    std::string token; 
    std::string line("1|Song Title|Release date||\"ignore me\"|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0"); 

    std::istringstream iss(line); 
    while (getline(iss, token, '|')) 
    { 
     std::cout << token << std::endl; 
    } 
    return 0; 
} 

Code levé de here.

+0

comment puis-je savoir que le 1 et le titre de la chanson sont séparés les uns des autres par exemple? – Edward

+0

@Edward - voir EDIT sur la façon d'analyser chaque ligne d'entrée en jetons, chacun étant 'std :: string'. Vous pouvez modifier ceci pour mettre les jetons directement dans le numéro de morceau «int» et votre «vecteur» de genres si votre entrée est connue pour être bien formée. –

3

Typiquement, vous faites cela par une surcharge operator>> pour le type d'objet:

struct song_data { 
    std::string number; 
    std::string title; 
    std::string release_date; 
    // ... 
}; 

std::istream &operator>>(std::istream &is, song_data &s_d) {   
    std::getline(is, s_d.number, '|'); 
    std::getline(is, s_d.title, '|'); 
    std::getline(is, s_d.release_date, '|'); 
    std::string ignore; 
    std::getline(is, ignore, '|'); 
    // ... 
    return is; 
} 

Selon que il y a plus de champs que vous voudrez peut-être ignorer (en particulier les champs de fuite), il peut parfois être plus pratique lire la ligne entière dans une chaîne, puis mettez cela dans un istringstream, et analyser les champs individuels à partir de là. En particulier, cela permet d'éviter les travaux supplémentaires en lisant plus de champs qui ne vous intéressent pas, au lieu de passer à la ligne suivante lorsque vous avez analysé les champs qui vous intéressent. Edit: Je traiterais probablement les genres en ajoutant un std::vector<bool> genres;, et en lisant les 0 et les 1 dans ce vecteur. Je puis ajouter une énumération spécifiant quel genre est désigné par une position particulière dans le vecteur, donc (par exemple) tester si une chanson est classée comme « pays » ressemblerait à quelque chose comme:

enum { jazz, country, hiphop, classic_rock, progressive_rock, metal /*, ... */}; 

if (songs[i].genres[country]) 

if (songs[i].genres[hiphop]) 
    process_hiphop(songs[i]); 

Bien sûr , les genres exacts et leur ordre sont quelque chose que je ne connais pas, donc j'ai juste inventé quelques possibilités - vous devrez (évidemment) utiliser les genres (et l'ordre) définis pour le format de fichier.

En ce qui concerne traiter des centaines de chansons, la manière habituelle serait (comme implicite ci-dessus) de créer quelque chose comme: std::vector<song_data> songs;. En utilisant une extraction de flux comme ci-dessus, vous pouvez copier les données du fichier au vecteur:

std::copy(std::istream_iterator<song_data>(infile), 
      std::istream_iterator<song_data>(), 
      std::back_inserter(songs)); 

Si vous êtes susceptible de rechercher principalement des chansons par nom (pour un exemple), vous pouvez préférer utiliser std::map<std::string, song_data> songs.Il sera facile de faire quelque chose comme:

songs["new song"].release_date = Today; 
+0

Oui, je pense que le libre 'operator >>' est un meilleur pari que d'utiliser une fonction membre –

+0

Maintenant, j'ai un objet de chanson avec 4 params étant le nombre, le titre, les données de sortie et un vecteur de genres. En faisant cela, comment serais-je capable de créer une nouvelle instance de l'objet de la chanson, et y a-t-il un bon moyen de gérer les genres de 1 et de 0? Est-ce que ce serait correct aussi bien s'il y avait des centaines de lignes comme ceci ou devrait-il être eddited? – Edward

+0

@Edward - Si la liste des drapeaux de genre qui est la dernière chose sur votre ligne, vous pouvez simplement lire jusqu'à ce que vous manquiez de données sur cette ligne, en ajoutant un vecteur de genres que vous allez en utilisant push_back. Mais il n'est pas clair pour moi quelle structure est impliquée dans cette liste de 0 et de 1 - le drapeau d'un genre donné est-il toujours au même endroit? Il n'y a aucun problème avec la taille de vos données à condition que cela ne provoque pas de mémoire insuffisante sur votre ordinateur - des boucles correctement construites pour la saisie de fichiers et pour l'analyse de genre fonctionneront correctement. –

16

Le C++ String Toolkit Library (StrTk) a la solution suivante à votre problème:

#include <string> 
#include <deque> 
#include "strtk.hpp" 

struct song_type 
{ 
    unsinged int id; 
    std::string release_date; 
    std::string url; 
    char genre[8]; 
}; 

strtk_parse_begin(song_type) 
strtk_parse_type(id) 
strtk_parse_type(release_date) 
strtk_parse_type(url) 
strtk_parse_type(genre[0]) 
strtk_parse_type(genre[1]) 
strtk_parse_type(genre[2]) 
strtk_parse_type(genre[3]) 
strtk_parse_type(genre[4]) 
strtk_parse_type(genre[5]) 
strtk_parse_type(genre[6]) 
strtk_parse_type(genre[7]) 
strtk_parse_end() 

int main() 
{ 
    std::deque<song_type> song_list; 

    strtk::for_each_line("songs.txt", 
         [&song_list](const std::string& line) 
         { 
          song_type s; 
          if (strtk::parse(line,"|",s)) 
           song_list.push_back(s); 
         }); 

    return 0; 
} 

Vous trouverez plus d'exemples Here

Questions connexes