【问题标题】:Using istringstream to process a memory block of variable length使用 istringstream 处理可变长度的内存块
【发布时间】:2011-01-18 18:52:16
【问题描述】:

我正在尝试使用istringstream 从一些内存中重新创建一个编码的wstring。内存布局如下:

  1. 1 字节表示 wstring 编码的开始。任意这是'!'。
  2. n 个字节以文本格式存储字符串的字符长度,例如0x31、0x32、0x33 将是“123”,即 123 个字符的字符串
  3. 1 字节分隔符(空格字符)
  4. n 字节是构成字符串的 wchars,其中 wchar_t 是每个 2 字节。

例如字节序列:

21 36 20 66 00 6f 00 6f 00

是“!6 f.o.o.” (用点表示 char 0)

我所拥有的只是一个 char* 指针(我们称之为pData),指向包含此编码数据的内存块的开头。消耗数据以重构 wstring(“foo”)并将指针移动到编码数据末尾之后的下一个字节的“最佳”方式是什么?

我正在玩弄使用 istringstream 来允许我使用前缀字节、字符串的长度和分隔符。之后,我可以计算要读取的字节数并使用流的read() 函数将其插入到适当调整大小的 wstring 中。 问题是,我如何首先将这些内存放入 istringstream?可以尝试先构造一个字符串,然后将其传递到 istringstream,例如 p>

std::string s((const char*)pData);

但这不起作用,因为字符串在第一个空字节处被截断。或者,我可以使用字符串的其他构造函数来明确说明要使用多少字节:

std::string s((const char*)pData, len);

这可行,但前提是我事先知道len 是什么。这很棘手,因为数据是可变长度的。

这似乎是一个真正可以解决的问题。我的字符串和流的新手状态是否意味着我忽略了一个简单的解决方案?还是我用整个字符串的方法叫错了树?

【问题讨论】:

    标签: c++ string stl stringstream


    【解决方案1】:

    尝试设置您的字符串流的rdbuf

    char* buffer = something;
    std::stringbuf *pbuf;
    std::stringstream ss;
    
    std::pbuf=ss.rdbuf();
    std::pbuf->sputn(buffer, bufferlength);
    // use your ss
    

    编辑:我看到这个解决方案会遇到与您的 string(char*, len) 情况类似的问题。你能告诉我们更多关于你的缓冲区对象的信息吗?如果您不知道长度,并且它不是以 null 结尾的,那将很难处理。

    【讨论】:

    • 恐怕没有缓冲区“对象”,只是一个指向内存块的指针。我得到了一个指向该内存开头的指针,我需要从中(重新)创建一个 wstring。由于 null 是有效数据(请参阅我的示例),因此我不能真正终止任何内容。而且我知道它的大小,因为它被编码在数据中,尽管它是一个文本字符串。作为一个人,我可以轻松地解析这些数据,但我正在努力想出一种优雅的方式来用代码来解析这些数据。如果您有什么特别想知道的,请尽管问。
    【解决方案2】:

    是否可以修改您对长度的编码方式,并将其设为固定大小?

    unsigned long size = 6; // known string length
    char* buffer = new char[1 + sizeof(unsigned long) + 1 + size];
    buffer[0] = '!';
    memcpy(buffer+1, &size, sizeof(unsigned long));

    缓冲区应该包含开始指示符(1 个字节)、实际大小(无符号长整数的大小)、分隔符(1 个字节)和文本本身(size)。
    这样,您可以轻松地获得“相当”的大小,然后将指针设置为超出开销,然后在字符串构造函数中使用 len 变量。
    unsigned long len;
    memcpy(&len, pData+1, sizeof(unsigned long)); // +1 to avoid the start indicator
    // len now contains 6
    char* actualData = pData + 1 + sizeof(unsigned long) + 1;
    std::string s(actualData, len);

    它是低级别且容易出错 :)(例如,如果您读取的任何内容未按照您期望的方式编码,则 len 可能会变得非常大),但您避免动态读取字符串的长度.

    【讨论】:

      【解决方案3】:

      这个订单上的东西似乎应该可以工作:

      std::wstring make_string(char const *input) { 
          if (*input != '!')
             return "";
          char length = *++input;
          return std::wstring(++input, length);
      }
      

      困难的部分是处理大小的可变长度。如果没有指定长度的内容,很难猜测何时停止将数据视为指定字符串的长度。

      至于移动指针,如果你要在函数内部进行,你需要传递对指针的引用,否则只需将找到的大小添加到收到的指针即可.

      【讨论】:

        【解决方案4】:

        在这里(ab)使用(已弃用但仍然是标准的)std::istrstream 很诱人:

        // Maximum size to read is 
        // 1 for the exclamation mark
        // Digits for the character count (digits10() + 1)
        // 1 for the space
        const std::streamsize max_size = 3 + std::numeric_limits<std::size_t>::digits10;
        
        std::istrstream s(buf, max_size);
        
        if (std::istream::traits_type::to_char_type(s.get()) != '!'){
            throw "missing exclamation";
        }
        
        std::size_t size;
        s >> size;
        
        if (std::istream::traits_type::to_char_type(s.get()) != ' '){
            throw "missing space";
        }
        
        std::wstring(reinterpret_cast<wchar_t*>(s.rdbuf()->str()), size/sizeof(wchar_t));
        

        【讨论】:

        • 有趣...所以您是说,由于我无法预先知道数据的大小,因此我将流设置为最大大小。是的,我猜这会奏效。但这会带来任何额外的开销吗?
        • @WalderFrey:istrstream 不拥有它的缓冲区,也不会复制。
        • 但是三思而后行,传递 max 可能不是一个好主意,因为流的实现最终可能会使用诸如 buf + size 之类的东西来计算其缓冲区的结尾,如果 buf 很大,则可能会溢出价值。我将更新答案以使用更安全的缓冲区大小。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-27
        • 1970-01-01
        • 2021-07-15
        相关资源
        最近更新 更多