代码之家  ›  专栏  ›  技术社区  ›  devin

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

  •  0
  • devin  · 技术社区  · 15 年前

    我正在使用流读取C++文件,具体来说是fStand,而不是IFSt流。

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

    这个重复了一遍又一遍

    1. 每行的blah的可变数目,
    2. 每端之间的行数是常量,这里的分隔符是end。

    我想读取一组数据,然后将其存储在C样式的字符数组中。我开始尝试使用getline(),但delimter只能是一个字符,不能是三个字符。显然,我不能尝试使用just read()读取一组字节数,因为每个组的字节数是不同的。

    所以,我为这里最简单(也是最健壮)的事情感到很苦恼。我应该调用getline直到找到一个“end”字符串,同时反复附加每个字符串吗?

    我尝试了一个二维字符数组,但我复制到它是一种痛苦。我可以在这里用Strncpy吗?我觉得这个不管用

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

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

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

    相关问题:char数组和std::string在内存中的存储方式是否相同?我一直认为std::string有一些开销。

    4 回复  |  直到 15 年前
        1
  •  2
  •   Roger Pate    15 年前

    (我的 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
    
        2
  •  7
  •   GManNickG    15 年前

    好吧,你说“在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
  •  3
  •   Fred Larson    15 年前

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

        4
  •  0
  •   T.E.D.    15 年前

    这真是一个 句法分析 你描述的问题。一旦你意识到问题是什么,你就已经是解决问题的绝大多数方法了。

    很难对您进行更具体的描述,因为您没有真正描述您需要对数据做什么。但通常您可以进行简单的内联解析。在这种情况下,您可能需要一个能够识别“blah”、“eol”和“end”的小例程,并告诉您在给定的字符串位置找到了哪个例程。

    然后,您可以有一个解析行例程来识别整行(期望任何数量的“blah”以eol结尾)。

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