2009-12-01 8 views
0

Я читаю файл на C++, используя потоки, в частности, fstream, а не ifstream.Хранение строк с переменным размером в структурах

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

Это повторяется снова и снова с

  1. varble количество бла-х годов в каждой строке,
  2. постоянное число линий между каждым концом, конец является разделителем здесь

I хотите прочитать один набор данных, а затем сохранить его в массиве символов, в структуре стиля C. Я начал с попытки использовать getline(), но разделитель может быть только одним символом, а не тремя. Я, очевидно, не могу попытаться прочитать заданное количество байтов, используя только read(), поскольку число будет различным для каждого набора.

Итак, я разорван, что самое легкое (и самое надежное), что нужно сделать здесь. Должен ли я называть getline, пока не нахожу строку «конец», добавляя каждую строку снова и снова?

Я пробовал массив двумерных символов, но копирование на него было чем-то вроде боли. Могу ли я использовать strncpy здесь? Я не думаю, что это работало

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

У меня есть несколько идей здесь, но я просто не уверен, один (или одна я не хотя) является лучшим.

EDIT: Так что это для сетевого приложения, поэтому размер массива char (или строки) всегда должен быть одинаковым. Кроме того, в структуре не должно быть указателей.

Связанный вопрос: так ли хранится массив символов и std :: string в памяти? Я всегда, хотя были некоторые накладные расходы с std :: string.

+0

Вы можете получить строку C-типа, указатель обугленного, только для чтения из станда :: строки, используя метод c \ _str. Метод данных также работает, но не гарантирует нулевого завершения. То, как строка точно хранится в памяти, является деталью реализации (некоторые относятся к подсчету ссылок, например), но, безусловно, наиболее распространенный сводится к массиву символов. Однако все же проще использовать std :: string и позволить ему управлять массивом. – 2009-12-01 21:15:10

ответ

2

(My push_back Утилита, описанная внизу.)

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; 
} 

Пример сериализации:

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'; 
    } 
} 

Я использовал простой формат bencoding сериализации. Пример подходящий выходной итератор, который только записывает в поток:

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; 
    } 
}; 

Пример использование:

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

Другого возможный выход итератор, который записывает в file descriptor (например, сетевая розетка):

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); 
    } 
}; 

двигаться семантика бедняка:

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

Это позволяет легко использовать ход семантики, чтобы избежать ненужных копий:

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

Мне очень нравится этот ответ, но как я могу использовать его с сетевым приложением? Если я отброшу & блоки к void *, я могу просто просто отбросить его обратно на приемник и ожидать, что он сработает? – devin

+0

Нет. Вы отправляли каждый блок в любом формате, который вы используете, объединяя строки в нем. Упрощенный пример включен выше. – 2009-12-01 20:54:04

7

Ну, вы сказали «в структуре стиля C», но, возможно, вы можете просто использовать 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

Логика чтения файлов неверна - подумайте, что произойдет, если файл пуст. Coincedentally, я только blogged об этой проблеме на http://punchlet.wordpress.com/ – 2009-12-01 18:47:57

+1

Yay, Нил прокомментировал! : D Привет, мистер Баттерворт. – GManNickG

3

Я бы рекомендовал использовать строки вместо массивов символов.

0

Это действительно парсинг проблема, которую вы описываете. Как только вы осознаете, в чем проблема, вы уже больше всего на пути к решению.

С вами сложно связаться с вами, так как вы действительно не описываете, что вам нужно сделать с данными. Но обычно вы можете выполнять простой синтаксический анализ inlne. В этом случае, возможно, вам понадобится небольшая рутина, которая распознает «бла» и EOL и «конец», и сообщит вам, что она обнаружила в заданном месте строки.

Затем вы можете иметь процедуру parse_line, которая распознает всю строку (ожидая, что любое количество «бла» заканчивается EOL).

Тогда вы можете иметь процедуру parse, которая вызывает parse_line ваше заданное количество раз (10?), А затем ошибки, если «конец» не найден.

Смежные вопросы