【问题标题】:Is there a better way to print a string with cout up to N characters?有没有更好的方法来打印一个最多包含 N 个字符的字符串?
【发布时间】:2009-02-15 12:19:24
【问题描述】:

-edit- 我发送的是二进制而不是字符串。我的测试使用的是 html 页面,所以在这个例子中我只使用了一个字符串,但我的问题是关于二进制、向量和使用 ostream 进行调试。我这样做可以消除一些混乱。

我有以下代码:

cout << string(&v[0]).substr(0, len);

有没有更好的方法来打印字符串 v 并增加 cout 的长度 len?我想过做 v[len] = 0 但我抛出一个大小为 1 的断言。我的代码是:

vector<char> v;
v.reserve(1024*16); //required
v.resize(1); //so we can do &v[0]
recv(sockfd, &v[0], v.capacity(), 0);
while (l > 0)
{
    cout << string(&v[0]).substr(0, l);
    recv(sockfd, &v[0], v.capacity(), 0);
}
cout << "the size is " << v.size();

【问题讨论】:

  • 此代码已损坏。太可怕了。使用向量来分配数组(这实际上是那里发生的事情)不仅危险(向量意味着重新分配其内部数组!)而且毫无意义。
  • 如果您必须使用矢量,至少使用 v.resize(1024*16),但正如 dionadar 所述,这样做不是一个好主意,基本上无论如何都不能保护您免受任何伤害,并且可能会导致调试此代码的一些有趣的夜晚。
  • 他使用矢量有什么问题?如果尺寸是动态的,我看不出有什么不好
  • (虽然在这种情况下,它不是动态的......但它可能在他的代码中,我们不知道)
  • 他使用了一个vector,它在内部创建了一个char[]来保存你放在那里的任何东西。但是调整数组的大小是不可能的,所以如果您决定调整向量的大小(有意或无意),它会创建一个新字符。然而,他现在在第一个数组的开头有一个无效的 char*。不好。

标签: c++ vector


【解决方案1】:

您可以在 cout 对象上使用 ostream::write 方法:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
  vector<char> test;
  test.push_back('a');
  test.push_back('b');
  test.push_back('c');

  cout.write(&test[0], 3);
  cout << endl;
}

输出:

abc

由于 ostream::write 返回一个包含 *this 的 ostream&,你甚至可以这样做

cout.write(&test[0], 3) << endl;

但我不确定这实际上是否更好(或更清晰)。

【讨论】:

    【解决方案2】:
    vector<char> v;
    v.reserve(1024*16); //required
    v.resize(1); //so we can do &v[0]
    recv(sockfd, &v[0], v.capacity(), 0);
    

    该代码有错误。调用reserve 只会保证您可以 push_back 至少那么多元素,直到对向量的引用和迭代器再次失效(通过可能重新分配已用缓冲区)。它不允许让您写入 v[0..1024*16-1],就像您使用 recv 那样。你必须这样做

    v.resize(1024*16); //required
    

    实际上有这么多可用的元素并实际传递v.size() 而不是v.capacity()

    对于您的子字符串操作,我可能会这样做

    std::string str(&v[0], n);
    std::cout << str;
    

    其中 n 的范围从 0 到 v.size()。您可以使用std::min(n, v.size()) 来保证,如果 n 在您的情况下可能更大并且您需要一个上限。

    (在一个边节点上,我会避免在那里有一个名为“l”(ell)的变量,因为它看起来很像一个“1”(一个),这可能会让人们感到困惑)

    【讨论】:

    • 从我读到的内容,您实际上可以写入 0-1024*16-1 元素,并且可以保证它是连续的,但是从大小到容量的元素都没有被构造/初始化。
    • 不对,因为vector没有那么多元素。当然,您最多只能写入/读取 v[0..v.size()-1]。
    • 顺便说一句,如果你需要一个恒定大小的缓冲区,最好使用 boost::array v;然后通过 v.data() 和 v.size() 。或堆栈上的数组 - 尽管应避免使用原始数组......你知道,危险即将来临
    • std::string str(v.begin(), v.begin()+n);会少些hackish。
    【解决方案3】:

    为什么将大小设置为 1?
    当您保留空间时,该空间可用于向量增长(无需重新分配)。但是谁说直接使用就安全了?我已经看到(调试)实现在 size() 修改这些位之后添加一个警告缓冲区,并且它会在下次检查时生成一个断言。你应该只从 0 -> size() 读/写。

    NB 这也将允许您使用 v[len] = '\0';

    vector<char> v(1024*16);
    
    std::size_t  len = recv(sockfd, &v[0], v.size(), 0);
    while (len > 0)
    {
        v[len] = '\0';
        cout << &v[0];
        len = recv(sockfd, &v[0], v.size(), 0);
    }
    

    请注意,这可能不是读取字符串的最佳方式。
    我会通过流传递长度信息,以便您知道何时没有更多信息可供读取,然后只读取所需的信息。

    【讨论】:

    • 如果我使用 do v.clear(); v.push_back(data) 很多次。我需要再次调整它的容量吗?这将构造我所有的字符,在字符情况下没有ctor,而是将其初始化为fillData?这很烦人,特别是如果我写/发送许多 128 字节并且需要至少 16k 的缓冲区。有没有办法...
    • 有没有办法在不将数据初始化为 fillValue 的情况下调整大小?如果不是那个调试实现会在这个项目中惹恼我,它是调试实现与通过调整大小避免潜在的大量开销。还有,ostream.write(ptr, len);解决了我的问题。
    【解决方案4】:

    见 cmets: 我的立场是正确的。我被告知了。但是,我仍然认为依靠这样的内部结构是疯狂的。我使用的最后一个 microsoft 编译器违反了 C99 标准,这让我悲痛欲绝。如果他们无法在 vsnprinf()new 上获得正确的返回值, 你真的要依赖这样的勘误表吗?

    您正在对向量的实现方式做出假设。您假设 v[1] 紧跟在内存中的 v[0] 之后。

    char buf[]; 之间有区别& buf[1] == & buf[0] + 1向量 v; & v[1] == & v[0] + 1。 char 数组使用指针算法。向量使用运算符 []。向量如何在内部存储数据,无论是否相邻,都取决于该向量类。

    虽然您的代码可能仍然有效,但这仍然是一个糟糕的事情!它使您的软件变得脆弱,导致它在您最不期望的时候以奇怪和预期的方式崩溃!

    这是本地堆栈上临时 char 数组的理想情况。尺寸很小。您有一个硬编码的最大尺寸。

    如果大小不是恒定的,我仍然会在堆栈上使用一个小的本地字符数组缓冲区。我只是在每次迭代后将它附加到 C++ std::string 中。 (是的,std::strings 可以存储包含多个空字符的二进制值。)

    recv() 返回它读取的字节数。 Vector v 不会自动拾取它。所以你需要存储和使用这个值。

    我建议:

    #define BUFFER_SIZE  (1024*16)
    #define FLAGS        0
    
    int  received = 0;
    int  total    = 0;
    char buffer [ BUFFER_SIZE + 1 ];
    
    memset( buffer, 0, BUFFER_SIZE + 1 );
    
    received = recv( sockfd, buffer, BUFFER_SIZE, FLAGS );
    
    if ( received > 0 )
    {
      copy( buffer + total,
            buffer + total + received,
            ostream_iterator<char>(cout) );
    
      total += received;
    }
    
    while( (received > 0) && (total < BUFFER_SIZE) )
    {
      received = recv( sockfd, buffer + total, BUFFER_SIZE - total, FLAGS );
    
      if ( received > 0 )
      {
        copy( buffer + total,
              buffer + total + received,
              ostream_iterator<char>(cout) );
    
        total += received;
      }
    }
    
    buffer [ total ] = '\0';
    buffer [ BUFFER_SIZE ] = '\0';
    
    cout << "The total size is " << total << endl;
    

    【讨论】:

    • 好吧。实际上向量是连续的是它的关键特征之一。如果向量包含至少 2 个元素,则可以保证 &v[1] == &v[0] + 1 为真。
    • 在 c++98 中不是这样的。但它已在 c++03 中修复。阅读herbsutter.wordpress.com/2008/04/07/…
    • 我的立场是正确的。原帖已更新。但我仍然认为依靠这样的内部结构是疯狂的。我使用的最后一个 microsoft 编译器违反了 C99 标准,这让我悲痛欲绝。你真的要依赖这样的勘误表吗?尤其是在不需要的时候?
    • Microsoft 编译器何时声称支持 C99?
    猜你喜欢
    • 2011-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    相关资源
    最近更新 更多