Хранение строк переменного размера в структурах - PullRequest
0 голосов
/ 01 декабря 2009

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

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

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

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

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

Так что я растерялся из-за того, что самое простое (и самое надежное) занятие здесь. Должен ли я вызывать getline, пока не найду строку 'end', добавляя каждую строку снова и снова?

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

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

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

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

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

Ответы [ 4 ]

7 голосов
/ 01 декабря 2009

Ну, вы сказали "в структуре стиля 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
*/
};
3 голосов
/ 01 декабря 2009

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

2 голосов
/ 01 декабря 2009

(Моя 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';
  }
}

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

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

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

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 голосов
/ 01 декабря 2009

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...