在结构中存储可变大小的字符串

时间:2009-12-01 18:35:48

标签: c++ string

我正在使用流读取C ++中的文件,特别是fstream,而不是ifstream。

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

这一遍又一遍地重复

  1. 每行中有多少个blah,
  2. 每一端之间的行数为常数,end是此处的分隔符
  3. 我想读取一组数据,然后将其存储在C样式结构的字符数组中。我开始尝试使用getline(),但分隔符只能是一个字符,而不是三个字符。我显然不能尝试使用read()读取一定数量的字节,因为每个集合的数字都不同。

    所以我对这里最简单(最强大)的事情感到震惊。我应该调用getline直到找到'end'字符串,同时反复追加每个字符串?

    我尝试了一个2D char数组但是我复制到它有点痛苦。我可以在这里使用strncpy吗?我认为这不起作用

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

    我在这里有一些想法,但我不确定哪一个(或者我没有的那个)是最好的。

    编辑: 因此,这适用于网络应用程序,因此char数组(或字符串)的大小应始终相同。此外,结构中应该没有指针。

    相关问题:char数组和std :: string存储在内存中的方式是一样的吗?我总是在std :: string上有一些开销。

4 个答案:

答案 0 :(得分: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
*/
};

答案 1 :(得分:3)

我建议使用字符串而不是字符数组。

答案 2 :(得分:2)

(我在底部描述的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

答案 3 :(得分:0)

这实际上是您正在描述的解析问题。一旦你意识到问题所在,你就已经解决了这个问题。

很难让您更具体,因为您没有真正描述您需要对数据做些什么。但通常你可以做简单的解析inlne。在这种情况下,也许你想要一个识别“blah”和EOL和“end”的小程序,并告诉你它在给定的字符串位置找到了它。

然后你可以有一个识别整行的parse_line例程(期望以EOL结尾的任何数量的“blah”)。

然后你可以有一个解析例程,调用parse_line你给定的次数(10?),然后错误,如果找不到“end”。