Is there a better way to print a string with cout

2019-04-27 08:38发布

问题:

-edit- I am sending binary and not a string. My test is using html pages so in this example i am only using a string but my question is about binary, vectors and debugging with ostream. I make this clears some confusion.

I have the following code:

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

Is there a better way to print the string v with cout up the length len? I thought of doing v[len] = 0 but I an assertion is thrown with a size of 1. My code is:

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();

回答1:

You can use the method ostream::write on the cout object:

#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;
}

Outputs:

abc

Since ostream::write returns an ostream& containing *this, you can even do

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

but I'm not sure that is actually any better (or clearer).



回答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);

That code has a bug. Calling reserve will only guarantee you that you can push_back at least that many elements until references and iterators to the vector are invalidated again (through a possible reallocation of the used buffer). It will not allow you to write into v[0..1024*16-1], as you do there with recv. You have to do

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

to actually have that many elements available and actually pass v.size() instead of v.capacity().

For your substring operation, i would probably do

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

Where n ranges from 0 up to v.size(). You can use std::min(n, v.size()) to guarantee that, if n could be larger in your case and you need an upper limit.

(on a side node, i would avoid having a variable called "l" (ell) there, because it can look very much like an "1" (one), which can confuse the hell out of people)



回答3:

Why are you setting the size to 1?
When you reserve the space the space is available for the vector to grow into (without reallocating). But who said it was safe to use directly? I have seen (debug) implementations that add a warning buffer just after size() modify these bits and it would generate an assert next time it checked. You should only be reading/writing from 0 -> size().

NB This will then also allow you to use 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);
}

Note this is probably not the best way to read a string.
I would pass length information over the stream so you know when there is not more information to read then read only as much as is required.



回答4:

See comments: I stand corrected. I was mininformed. However, I still think it's nuts to rely on internals like this. The last microsoft compiler I used violated C99 standards causing me no end of grief. If they can't get return values right on vsnprinf() or new, do you really want to rely on errata like this?

You are making assumptions regarding how vector is implemented. You are assuming that v[1] comes right after v[0] in memory.

There is a difference between char buf[]; & buf[1] == & buf[0] + 1 and vector v; & v[1] == & v[0] + 1. The char array uses pointer arithmetic. The vector uses operator[]. How the vector stores data internally, whether it is adjacent or not, is up to that vector class.

While your code may still work, this is still a BAD thing! It makes your software brittle, causing it to break in strange and expected ways when you least expect it!

This is a ideal situation for a temporary char array on the local stack. The size is small. You have a hardcoded maximum size.

If the size wasn't constant, I'd still use a small local char-array buffer on the stack. I'd just append it to a C++ std::string after each iteration. (Yes, std::strings can store binary values including multiple null characters.)

recv() returns how many bytes it read. Vector v doesn't automagically pick this up. So you need to store and use that value.

I suggest:

#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;


标签: c++ vector