【问题标题】:vector <unsigned char> vs string for binary data向量 <unsigned char> 与二进制数据的字符串
【发布时间】:2010-12-06 02:06:16
【问题描述】:

对于保存和访问二进制数据,哪个 C++ 容器更好?

std::vector<unsigned char>

std::string

一个比另一个更有效吗?
一种更“正确”的用法吗?

【问题讨论】:

标签: c++ string stl vector binary-data


【解决方案1】:

您应该更喜欢 std::vector 而不是 std::string。在常见情况下,这两种解决方案几乎是等效的,但 std::strings 专为字符串和字符串操作而设计,这不是您的预期用途。

【讨论】:

  • “假设默认的字符特征决定 'a' 和 'á' 是等价的” 这是一个错误的假设。请参阅我写的答案作为此评论的延续。
  • 我重新检查过,你是对的,标准确实定义了专业化char_traits&lt;char&gt;,并且在标准专业化中,赋值、比较和排序被定义为内置 char 类型的等价物。
  • 所以使用默认的 char_traits std::string 与对应的 std::vector 相比没有什么不同?
  • @kalaxy:正确。无论如何,每个课程都是有目的的,std::vector 可以更好地满足您对缓冲区的需求,所以如果只是因为意图更清晰(正如 fnieto 在他的回答中指出的那样),我更喜欢std::vector
  • @DavidRodríguez-dribeas:我编辑了您的答案,因为我(来自 cmets)了解到以前的版本不正确。
【解决方案2】:

两者都是正确且同样有效的。使用其中之一而不是普通数组只是为了简化内存管理并将它们作为参数传递。

我使用向量是因为意图比使用字符串更清晰。

编辑: C++03 标准不保证std::basic_string 内存连续性。然而,从实际的角度来看,没有商业的非连续实现。 C++0x 设置为standardize that fact

【讨论】:

  • 来自 Sgi:“basic_string 类表示一个字符序列。它包含序列的所有常用操作,此外,它还包含标准字符串操作,例如搜索和连接。”。为什么这是不正确的?我同意这不是最好的方法(正如我在回答中所说的那样),但它不是不正确的。
  • 所以字符串和向量一样有效,因为它在某种意义上扩展了向量的功能,但我需要的唯一功能([] 等)包含在两者中? (是的,我意识到字符串实际上并不是从向量继承的。)
  • 是的,但从概念上讲是一个更糟糕的选择,并且具有对缓冲区没有意义的方法。如果你只想要内存管理和运算符[],为什么要使用像 std::string 这样复杂的类。
【解决方案3】:

一个比另一个更有效吗?

这是一个错误的问题。

一种更“正确”的用法吗?

这是正确的问题。
这取决于。数据是如何使用的?如果您要在像 fashon 这样的字符串中使用数据,那么您应该选择 std::string ,因为使用 std::vector 可能会使后续维护者感到困惑。另一方面,如果大多数数据操作看起来像普通数学或向量,那么 std::vector 更合适。

【讨论】:

    【解决方案4】:

    很长一段时间以来,我都同意这里的大多数答案。然而,就在今天,我突然想到为什么实际上使用 std::string 而不是 std::vector&lt;unsigned char&gt; 可能更明智。

    正如大多数人所同意的那样,使用任何一个都可以。但很多时候,文件数据实际上可以是文本格式(现在更常见,因为 XML 已成为主流)。当它变得相关时,这使得在调试器中查看变得很容易(并且这些调试器通常会让您导航字符串的字节)。但更重要的是,可以在字符串上使用的许多现有函数可以很容易地用于文件/二进制数据。我发现自己编写了多个函数来处理字符串和字节数组,并意识到这一切毫无意义。

    【讨论】:

      【解决方案5】:

      这是对 dribeas 回答的评论。我写它作为能够格式化代码的答案。

      这是 char_traits 比较函数,行为相当健康:

      static bool
      lt(const char_type& __c1, const char_type& __c2)
      { return __c1 < __c2; }
      
      template<typename _CharT>
      int
      char_traits<_CharT>::
      compare(const char_type* __s1, const char_type* __s2, std::size_t __n)
      {
        for (std::size_t __i = 0; __i < __n; ++__i)
      if (lt(__s1[__i], __s2[__i]))
        return -1;
      else if (lt(__s2[__i], __s1[__i]))
        return 1;
        return 0;
      }
      

      【讨论】:

      • 标准中是否明确定义了这种行为?
      • +1: @gnud: 不是一般情况,但 fnieto 是对的(我刚刚检查过),因为标准定义了 char 的特化,其中 assigneq 和 @ 987654324@ 必须定义为内置运算符 =、== 和 char。
      【解决方案6】:

      就可读性而言,我更喜欢std::vector。在这种情况下,std::vector 应该是默认容器:意图更清晰,正如其他答案已经说过的那样,在大多数实现中,它也更有效。

      有一次我确实更喜欢 std::string 而不是 std::vector。让我们看看它们在 C++11 中的移动构造函数的签名:

      vector (vector&& x);

      string (string&& str) noexcept;

      那次我真的需要一个 noexcept 移动构造函数。 std::string 提供它而 std::vector 没有。

      【讨论】:

        【解决方案7】:

        如果您只想存储二进制数据,可以使用优化空间分配的bitset。否则请选择vector,因为它更适合您的使用。

        【讨论】:

        • bitset 不是一个好的选择。您将如何在不进行强制转换的情况下恢复数据?您如何轻松地从位集中读取一个字节?这不是 bitset 的正确应用程序。
        • 因此,“如果您只想存储二进制数据”。这在一些内存密集型进程中很重要 - 例如在处理二进制图像时,您可能希望暂时存储它们,然后再使用它们。
        • 您实际上“只存储数据”的频率如何?如果我要存储它,我会使用一个文件或者只是一个数组或向量。 bitset 对存储有什么好处?您甚至如何将二进制数据放入位集中?为此目的,Bitset 有非常糟糕的构造函数。你真的尝试过这样做吗? Bitset 有一个默认构造函数、一个采用无符号长整数的构造函数和一个采用字符串的构造函数。为此目的并不方便。
        • 将它存储在数组或向量中会破坏存储的目的,因为我们使用 bitset 来优化 bits 的分配。传递一串位并不是那么困难。至于应用程序,二进制图像就是其中之一:RGB 1024x768 以 2.25MB 存储为 uchars - 想象一下存储一小批帧(这并非不现实)。此外,文件的 r/w 比将其临时存储为 bitset 慢得多。此外,我确实提到如果存储不是主要动机,vector 会更好。
        • 要初始化一个 2.25 MB 的位集,你需要一个 10 MB 的字符串;字符串中的每个 character 仅代表 bitset 中的 一位。此外,您需要知道在编译时需要多少位。只有两种方法可以整体提取 bitset 的内容:如果您的位多于 long 中的位,则 to_ulong 将毫无用处,而 to_string 返回一串零和不易在任何其他数据类型中使用的一串。所以,是的,如果您只想存储预设数量的数据,bitset 可能没问题。如果您想要返回数据,或者大小不确定,那么这是一个糟糕的选择。
        【解决方案8】:

        比较这 2 个并选择更适合您的自己。两者都非常强大,使用 STL 算法...选择对您的任务更有效的自己

        【讨论】:

          【解决方案9】:

          我个人更喜欢 std::string,因为当我希望二进制缓冲区以 C 兼容形式返回时,string::data() 对我来说更直观。我知道向量元素可以保证连续存储,在代码中执行此操作感觉有点令人不安。

          这是个人开发者或团队应该为自己做出的风格决定。

          【讨论】:

          • 您更喜欢将字符串用于非字符串数据?而不是使用设计的容器来连续存储任何类型的数据?
          • 别忘了这是风格问题。可以使用这些类中的任何一个创建完全可行且符合标准的二进制缓冲区代码。我认为向量也不是设计为二进制缓冲区。它是兼容的,但您必须恢复使用算法或 C 技巧才能完成工作。并非所有字符串操作都是安全的,但其中一些对于使代码更简洁和更易于维护非常有用。
          • Vector 非常适合存储二进制数据,例如矢量 v(256)。我不认为 &v[0] 是“C 技巧”。
          • 不,&v[0] 很好,s.data() 也很好。字符串 s 的向量替代方案是什么? s.assign(BinaryBuffer, BinaryBufferSize); ?
          • 向量 v; v.assign(BinaryBuffer, BinaryBuffer + BinaryBufferSize);
          猜你喜欢
          • 1970-01-01
          • 2018-09-14
          • 1970-01-01
          • 1970-01-01
          • 2016-02-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多