Std::vector <std::string> до char * array
У меня есть std::vector<std::string>
, который мне нужно использовать для аргумента функции C
, который читает char* foo
. Я видел как для преобразования std::string
в char*
. Как новичок в C++
, я пытаюсь собрать все, чтобы выполнить это преобразование для каждого элемента вектора и создать массив char*
.
Я видел несколько тесно связанных вопросов SO, но большинство из них, похоже, иллюстрирует способы перехода в другое направление и создание std::vector<std::string>
.
Ответы
Ответ 1
Вы можете использовать std::transform
как:
std::transform(vs.begin(), vs.end(), std::back_inserter(vc), convert);
Для этого требуется реализовать convert()
как:
char *convert(const std::string & s)
{
char *pc = new char[s.size()+1];
std::strcpy(pc, s.c_str());
return pc;
}
Тестовый код:
int main() {
std::vector<std::string> vs;
vs.push_back("std::string");
vs.push_back("std::vector<std::string>");
vs.push_back("char*");
vs.push_back("std::vector<char*>");
std::vector<char*> vc;
std::transform(vs.begin(), vs.end(), std::back_inserter(vc), convert);
for ( size_t i = 0 ; i < vc.size() ; i++ )
std::cout << vc[i] << std::endl;
for ( size_t i = 0 ; i < vc.size() ; i++ )
delete [] vc[i];
}
Вывод:
std::string
std::vector<std::string>
char*
std::vector<char*>
Демо-версия онлайн: http://ideone.com/U6QZ5
Вы можете использовать &vc[0]
везде, где вам нужно char**
.
Обратите внимание, что поскольку мы используем new
для выделения памяти для каждой std::string
(в функции convert
), мы должны освободить память в конце. Это дает вам гибкость для изменения вектора vs
; вы можете push_back
добавить к нему больше строк, удалить существующий из vs
, а vc
(i.e vector<char*>
будет по-прежнему действителен!
Но если вам не нужна эта гибкость, вы можете использовать эту функцию convert
:
const char *convert(const std::string & s)
{
return s.c_str();
}
И вы должны изменить std::vector<char*>
на std::vector<const char*>
.
Теперь после преобразования, если вы измените vs
, вставив новые строки или удалив из него старые, то все char*
в vc
могут стать недействительными. Это один важный момент. Другим важным моментом является то, что вам больше не нужно использовать delete vc[i]
в вашем коде.
Ответ 2
Лучшее, что вы можете сделать, это выделить std::vector
of const char*
того же размера, что и ваш вектор. Затем пройдите каждый элемент вектора, вызвав c_str()
, чтобы получить строковый массив и сохранить его в соответствующем элементе массива. Затем вы можете передать указатель на первый элемент этого вектора на соответствующую функцию.
Код будет выглядеть так:
std::vector<const char *> cStrArray;
cStrArray.reserve(origVector.size());
for(int index = 0; index < origVector.size(); ++index)
{
cStrArray.push_back(origVector[index].c_str());
}
//NO RESIZING OF origVector!!!!
SomeCFunction(&cStrArray[0], cStrArray.size());
Обратите внимание, что вы не можете изменять исходный вектор строк между временем, выбранным const char*
из std::strings
, и временем, когда вы вызываете C-функцию.
Ответ 3
Это должно работать:
char ** arr = new char*[vec.size()];
for(size_t i = 0; i < vec.size(); i++){
arr[i] = new char[vec[i].size() + 1];
strcpy(arr[i], vec[i].c_str());
}
EDIT:
Здесь вы можете освободить эти структуры данных, полагая, что vec все еще имеет правильное количество элементов, если ваша функция C изменяет этот массив, вам может понадобиться получить размер другим способом.
for(size_t i = 0; i < vec.size(); i++){
delete [] arr[i];
}
delete [] arr;
EDIT Снова:
Возможно, нет необходимости копировать строки, если ваша функция C не модифицирует строки. Если вы можете уточнить, как выглядит ваш интерфейс, я уверен, что мы сможем помочь вам.
Ответ 4
Решение С++ 0x, где элементы std::string
гарантированно сохраняются смежно:
std::vector<std::string> strings = /* from somewhere */;
int nterms = /* from somewhere */;
// using std::transform is a possibility depending on what you want
// to do with the result of the call
std::for_each(strings.begin(), string.end(), [nterms](std::string& s)
{ ModelInitialize(&s[0], nterms); }
Если функция null завершает свой аргумент, то после вызова (s.begin(), s.end())
может не иметь смысла. Вы можете выполнить пост-процесс, чтобы исправить это:
s = std::string(s.begin(), std::find(s.begin(), s.end(), '\0'));
Более сложная версия, которая отдельно копирует каждую строку в char[]
:
typedef std::unique_ptr<char[]> pointer;
std::vector<pointer> args;
std::transform(strings.begin(), strings.end()
, std::back_inserter(args)
, [](std::string const& s) -> pointer
{
pointer p(new char[s.size()]);
std::copy(s.begin(), s.end(), &p[0]);
return p;
});
std::for_each(args.begin(), args.end(), [nterms](pointer& p)
{ ModelInitialize(p.get(), nterms); });
Ответ 5
const char * также совпадает с char *, только другим в const_ness, ваш метод интерфейса принимает как const, так и non-const string.
Не c_str() возвращает const char? Будет ли это проблемой, если я просто нужен char *?
Да, он возвращает строку const и нет проблем.
const char*a="something";
////whatever it is here
const char* retfunc(const char*a)
{
char*temp=a;
//process then return temp
}
Возвращение локального объекта не принимается многими людьми, и этот крошечный пример предоставляется как as-is.
Ответ 6
Элементы вектора сохраняются смежно, поэтому лучший и простой способ:
std::vector<char> v;
char* c = &v[0];