2009-12-01 5 views
0

Je suis en train de lire un fichier en C++ en utilisant des flux, en particulier fstream, pas ifstream.Stockage de chaînes de taille variable dans des structures

blah blah blah\n 
blah blah\n 
blah blah blah blah \n 
end 

Cela se répète encore et avec

  1. nombre Varble de bla de dans chaque ligne,
  2. nombre constant de lignes entre chaque extrémité, à la fin est le délimiteur ici

I vouloir lire un ensemble de données, puis le stocker dans un tableau de caractères, dans une structure de style C. J'ai commencé en essayant d'utiliser getline() mais le délimiteur ne peut être qu'un caractère, pas trois. Je ne peux évidemment pas essayer de lire un nombre d'octets en utilisant simplement read(), car le nombre sera différent pour chaque ensemble.

Donc, je suis déchiré sur ce que la chose la plus facile (et la plus robuste) à faire ici est. Devrais-je appeler getline jusqu'à ce que je trouve une chaîne 'end', tout en ajoutant chaque chaîne encore et encore?

J'ai essayé un tableau de char 2D, mais je l'ai copié était une sorte de douleur. Puis-je utiliser strncpy ici? Je ne pense pas que cela a fonctionné

char buf[10][10]; 
strncpy(buf[1], "blah blah",10); 

J'ai quelques idées, mais je ne suis pas sûr que l'un (ou celui que j'ai pas même si bien) est le meilleur.

EDIT: Donc, ceci est pour une application de mise en réseau, de sorte que la taille du tableau char (ou chaîne) doit toujours être la même. De plus, il ne devrait pas y avoir de pointeurs dans la structure.

Question connexe: est la façon dont un tableau char et une chaîne std :: string sont stockés dans la même mémoire? J'ai toujours pensé qu'il y avait un peu de surcharge avec std :: string.

+0

Vous pouvez obtenir une chaîne de style C, un pointeur char, pour la lecture d'un std :: string uniquement, en utilisant la méthode c \ _str. La méthode de données fonctionne également, mais ne garantit pas la terminaison nulle. La manière dont la chaîne est exactement stockée en mémoire est un détail d'implémentation (certains font par exemple le comptage des références), mais de loin le plus répandu se résume à un tableau char. Il est toujours plus facile d'utiliser une chaîne std :: et de la laisser gérer le tableau, cependant. –

Répondre

2

(Mon push_back utilitaire décrit en bas.)

typedef std::vector<std::string> Block; 

int main() { 
    using namespace std; 

    vector<Block> blocks; 
    string const end = "end"; 

    // no real difference from using ifstream, btw 
    for (fstream file ("filename", file.in); file;) { 
    Block& block = push_back(blocks); 
    for (string line; getline(file, line);) { 
     if (line == end) { 
     break; 
     } 
     push_back(block).swap(line); 
    } 
    if (!file && block.empty()) { 
     // no lines read, block is a dummy not represented in the file 
     blocks.pop_back(); 
    } 
    } 

    return 0; 
} 

sérialisation Exemple:

template<class OutIter> 
void bencode_block(Block const& block, OutIter dest) { 
    int len = 0; 
    for (Block::const_iterator i = block.begin(); i != block.end(); ++i) { 
    len += i->size() + 1; // include newline 
    } 
    *dest++ = len; 
    *dest++ = ':'; 
    for (Block::const_iterator i = block.begin(); i != block.end(); ++i) { 
    *dest++ = *i; 
    *dest++ = '\n'; 
    } 
} 

Je l'ai utilisé un format de sérialisation bencoding. Exemple itérateur de sortie approprié, qui écrit simplement un flux:

struct WriteStream { 
    std::ostream& out; 
    WriteStream(std::ostream& out) : out(out) {} 

    WriteStream& operator++() { return *this; } 
    WriteStream& operator++(int) { return *this; } 
    WriteStream& operator*() { return *this; } 

    template<class T> 
    void operator=(T const& value) { 
    out << value; 
    } 
}; 

utilisation Exemple:

bencode_block(block, WriteStream(std::cout)); 

autre itérateur de sortie possible, qui écrit à une file descriptor (comme une prise réseau):

struct WriteFD { 
    int out; 
    WriteFD(int out) : out(out) {} 

    WriteFD& operator++() { return *this; } 
    WriteFD& operator++(int) { return *this; } 
    WriteFD& operator*() { return *this; } 

    template<class T> 
    void operator=(T const& value) { 
    if (write(value) == -1) { 
     throw std::runtime_error(strerror(errno)); 
    } 
    } 

    //NOTE: write methods don't currently handle writing less bytes than provided 
    int write(char value) { 
    return write(out, &value, 1); 
    } 
    int write(std::string const& value) { 
    return write(out, value.data(), value.size()); 
    } 
    int write(int value) { 
    char buf[20]; 
    // handles INT_MAX up to 9999999999999999999 
    // handles INT_MIN down to -999999999999999999 
    // that's 19 and 18 nines, respectively (you did count, right? :P) 
    int len = sprintf(buf, "%d", value); 
    return write(out, buf, len); 
    } 
}; 

sémantique de mouvement de l'homme pauvre:

template<class C> 
typename C::value_type& push_back(C& container) { 
    container.push_back(typename C::value_type()); 
    return container.back(); 
} 

Ceci permet une utilisation facile de la sémantique de déplacement pour éviter des copies inutiles:

container.push_back(value); // copies 
// becomes: 
// (C is the type of container) 
container.push_back(C::value_type()); // add empty 
container.back().swap(value); // swap contents 
+0

J'aime beaucoup cette réponse, mais comment puis-je l'utiliser avec une application réseau? Si je lance les blocs & pour annuler * puis-je simplement le rejeter sur le récepteur et m'attendre à ce qu'il fonctionne? – devin

+0

Non. Vous devez envoyer chaque bloc dans le format que vous utilisez, en concaténant les lignes qu'il contient. Exemple simplifié inclus ci-dessus. –

7

Eh bien, vous avez dit "dans une structure de style C", mais peut-être que vous pouvez simplement utiliser std::string?

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

int main(void) 
{ 
    std::fstream file("main.cpp"); 
    std::vector<std::string> lines; 

    std::string line; 
    while (getline(file, line)) 
    { 
     if (line == "end") 
     { 
      break; 
     } 

     std::cout << line << std::endl; 
     lines.push_back(line); 
    } 

    // lines now has all the lines up-to 
    // and not including "end" 

/* this is for reading the file 
end 

some stuff that'll never get printed 
or addded blah blah 
*/ 
}; 
+2

Votre logique de lecture de fichier est erronée. Pensez à ce qui se passe si le fichier est vide. Coincedentally, je viens de bloguer sur ce problème à http://punchlet.wordpress.com/ –

+1

Yay, Neil a commenté! : D Bonjour, monsieur Butterworth. – GManNickG

3

Je vous recommande d'utiliser des chaînes au lieu de tableaux char.

0

C'est vraiment un analysant le problème que vous décrivez. Une fois que vous réalisez quel est le problème, vous êtes déjà la plupart du temps à la solution.

Il est difficile d'être plus précis avec vous, car vous ne décrivez pas vraiment ce dont vous avez besoin avec les données. Mais généralement, vous pouvez faire l'analyse simple inlne. Dans ce cas, vous voudrez peut-être une petite routine qui reconnaît "blah" et EOL et "fin", et vous indique ce qu'il a trouvé à un emplacement de chaîne donné.

Ensuite, vous pouvez avoir une routine parse_line qui reconnaît une ligne entière (s'attendre à un nombre quelconque de "bla" se terminant par un EOL).

Ensuite, vous pouvez avoir une routine d'analyse qui appelle parse_line votre nombre de fois donné (10?), Et ensuite les erreurs si "fin" n'est pas trouvé.

Questions connexes