Копирование массива беззнаковых символов без нулевого окончания в std::string

Если бы массив был оканчивающимся нулем, это было бы довольно просто:

unsigned char u_array[4] = { 'a', 's', 'd', '\0' };
std::string str = reinterpret_cast<char*>(u_array);
std::cout << "-> " << str << std::endl;

Однако мне интересно, как лучше всего копировать массив без знака не завершающийся нулем без знака, как показано ниже:

unsigned char u_array[4] = { 'a', 's', 'd', 'f' };

в std::string.

Есть ли способ сделать это, не перебирая массив беззнаковых символов?

Спасибо вам всем.


person karlphillip    schedule 14.01.2011    source источник


Ответы (12)


std::string имеет конструктор, который принимает пару итераторов и unsigned char может быть преобразован (в соответствии с реализацией) на char, чтобы это работало. Нет необходимости в reinterpret_cast.

unsigned char u_array[4] = { 'a', 's', 'd', 'f' };

#include <string>
#include <iostream>
#include <ostream>

int main()
{
    std::string str( u_array, u_array + sizeof u_array / sizeof u_array[0] );
    std::cout << str << std::endl;
    return 0;
}

Конечно, шаблонная функция "размера массива" более надежна, чем вычисление sizeof.

person CB Bailey    schedule 14.01.2011
comment
Преобразование unsigned char * в char * здесь, вы должны сделать reinterpret_cast<const char *>. - person ; 14.01.2011
comment
@VladLazarenko: Но я не хочу делать это преобразование. - person CB Bailey; 14.01.2011
comment
@Charles: Тогда ваш код не скомпилируется, если вы не измените тип по умолчанию для char на unsigned в настройках компилятора;) - person ; 14.01.2011
comment
@Vlad Lazarenko: компилируется :) - person cpx; 14.01.2011
comment
@Влад Лазаренко: unsigned char* удовлетворяет требованиям для итератора ввода. Мой код компилируется и работает нормально. - person CB Bailey; 14.01.2011
comment
@Charles, прекрати использовать глючные компиляторы. Подпись конструктора, которую вам нужно вызвать, это std::string (const char *, size_t), так как unsigned не преобразуется в signed неявно, передача unsigned char * вызовет двусмысленность. Проверьте с помощью правильного компилятора или посмотрите, например, - stackoverflow .com/questions/804123/ - person ; 14.01.2011
comment
@VladLazarenko: мне не нужно вызывать этот конструктор, я вполне доволен этим конструктором: template<class InputIterator> basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator()); - person CB Bailey; 14.01.2011
comment
@Charles: О, я, должно быть, слеп. Конечно, вы используете здесь два указателя. Не заметил u_array + во втором аргументе. Виноват. +1 за ваш ответ тогда. - person ; 14.01.2011
comment
К вашему сведению, деление на sizeof u_char[0] совершенно избыточно. Стандарт гарантирует, что этот размер равен размеру char, который по определению равен 1. - person Konrad Rudolph; 14.01.2011
comment
@КонрадРудольф. У меня было два мнения о том, чтобы вынуть его и оставить. Некоторые люди приводят веские аргументы в другом месте, что он устойчив к изменению типа u_char, но в любом случае это маргинально, ИМХО. - person CB Bailey; 14.01.2011
comment
@Konrad: Я считаю, что Чарльз решил показать общий код, чтобы не вводить читателей в заблуждение, заставляя их просто выполнять sizeof, например. wchar_t. - person Cheers and hth. - Alf; 14.01.2011
comment
Или вы можете просто заменить второй параметр на std::end(u_array) (C++0x) - person Blastfurnace; 14.01.2011

Что ж, видимо std::string имеет конструктор, который можно использовать в этом случае:

std::string str(reinterpret_cast<char*>(u_array), 4);
person karlphillip    schedule 14.01.2011
comment
Больше идейной мысли, но было бы лучше не отбрасывать константность массива. Кроме того, он принимает размер вместо подверженного ошибкам жесткого кодирования 4. - person ; 14.01.2011

При построении строки без указания ее размера конструктор будет перебирать массив символов и искать нуль-терминатор, который является символом '\0'. Если у вас нет этого символа, вы должны явно указать длину, например:

// --*-- C++ --*--

#include <string>
#include <iostream>


int
main ()
{
    unsigned char u_array[4] = { 'a', 's', 'd', 'f' };
    std::string str (reinterpret_cast<const char *> (u_array),
                     sizeof (u_array) / sizeof (u_array[0]));
    std::cout << "-> " << str << std::endl;
}
person Community    schedule 14.01.2011

Это должно сделать это:

std::string s(u_array, u_array+sizeof(u_array)/sizeof(u_array[0]));
person cpx    schedule 14.01.2011
comment
u_array имеет тип unsigned char, а конструктор std::string принимает const char *, поэтому он даже не скомпилируется. - person ; 14.01.2011
comment
@Влад Лазаренко: Нет, я проверил, все должно быть в порядке. - person cpx; 14.01.2011
comment
@Dave, тип char по умолчанию — знаковый, а не беззнаковый, и он не может неявно преобразовывать один в другой. Ваш компилятор либо обрабатывает char как unsigned, либо содержит ошибки. В любом случае универсальное решение не должно полагаться на эти особенности и использовать явное преобразование. Вы можете проверить это с Comeau онлайн или что-то в этом роде, это не работает. - person ; 14.01.2011
comment
/4 ? Массив представляет собой массив unsigned char. Почему так важно, что int составляет 4 байта (даже если это предположение верно)? - person CB Bailey; 14.01.2011
comment
@VladLazarenko: unsigned char можно преобразовать в char, они оба являются целочисленными типами (например, char x = (unsigned char)10;). Результат определяется реализацией, если значение unsigned char не выражается в char, но это допустимая конверсия. - person CB Bailey; 14.01.2011
comment
@Charles: конечно, их можно преобразовать. Но они не могут быть преобразованы неявно. Ваш компилятор должен иметь char как unsigned по умолчанию, что имеет смысл, но я думаю, что это нестандартно. Или, должно быть, очень умно заглянуть в массив констант времени компиляции и решить, что его можно преобразовать в массив со значениями со знаком. - person ; 14.01.2011
comment
@VladLazarenko: любой целочисленный тип можно преобразовать в любой другой целочисленный тип: 4.7 [conv.integral] . Сюда входят unsigned char и char. - person CB Bailey; 14.01.2011


Вы можете использовать этот конструктор std::string:

string ( const char * s, size_t n );

так что в вашем примере:

std::string str(u_array, 4);
person Benoit Thiery    schedule 14.01.2011
comment
Вы можете улучшить его, выполнив sizeof (u_array). Или еще лучше - sizeof (u_array) / sizeof(u_array[0]), который будет работать для типов данных, размер которых больше 1 байта. - person ; 14.01.2011

Вы можете создать указатель символа, указывающий на первый символ, и другой, указывающий на один за последним, и построить, используя эти два указателя в качестве итераторов. Таким образом:

std::string str(&u_array[0], &u_array[0] + 4);
person Raedwald    schedule 14.01.2011
comment
Это подвержено ошибкам, так как размер массива может измениться, и вы можете легко забыть заменить 4 новым значением. Плюс нет смысла делать &u_array[0], это эквивалентно просто u_array, что гораздо меньше печатать. - person ; 14.01.2011

По-прежнему существует проблема, когда строка сама содержит нулевой символ, и вы пытаетесь впоследствии напечатать строку:

char c_array[4] = { 'a', 's', 'd', 0 };

std::string toto(array,4);
cout << toto << endl;  //outputs a 3 chars and a NULL char

Тем не мение....

cout << toto.c_str() << endl; //will only print 3 chars.

Такие времена, когда вы просто хотите отказаться от привлекательности и использовать голый C.

person plgDavid    schedule 03.02.2012

Пытаться:

std::string str;
str.resize(4);
std::copy(u_array, u_array+4, str.begin());
person tibur    schedule 14.01.2011

std::string имеет конструктор, принимающий массив символов и длину.

unsigned char u_array[4] = { 'a', 's', 'd', 'f' };
std::string str(reinterpret_cast<char*>(u_array), sizeo(u_array));
person johannes    schedule 14.01.2011

Фу, зачем актерский состав?

 std::string str(u_array, u_array + sizeof(u_array));

Сделанный.

person Lightness Races in Orbit    schedule 14.01.2011

Хотя вопрос заключался в том, как «скопировать массив unsigned char без нулевого окончания [...] в std::string», я отмечаю, что в данном примере эта строка используется только в качестве входных данных для std::cout.

В этом случае, конечно, вы можете вообще избежать строки и просто сделать

std::cout.write(u_array, sizeof u_array);
std::cout << std::endl;

который, я думаю, может решить проблему, которую OP пытался решить.

person Ben Hekster    schedule 08.09.2018