std :: vector <std :: string> в массив char * - PullRequest
28 голосов
/ 13 августа 2011

У меня есть std::vector<std::string>, который мне нужно использовать для аргумента C функции, которая читает char* foo видел как преобразовать std::string в char*.Как новичок в C++, я пытаюсь собрать воедино, как выполнить это преобразование для каждого элемента вектора и создать массив char*.

Я видел несколько тесно связанных SO вопросов,но большинство, кажется, иллюстрируют способы пойти в другом направлении и создать std::vector<std::string>.

Ответы [ 6 ]

33 голосов
/ 13 августа 2011

Вы можете использовать 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 (т.е. 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] в своем коде.

10 голосов
/ 13 августа 2011

Лучшее, что вы можете сделать, - это выделить std::vector из 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* s из std::strings и временем, когда вы вызываете функцию C.

7 голосов
/ 13 августа 2011

Это должно работать:

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;

Снова отредактируйте:

Возможно, нет необходимости копировать строки, если ваша функция C не изменяет строки. Если вы можете уточнить, как выглядит ваш интерфейс, я уверен, что мы могли бы помочь вам лучше.

0 голосов
/ 24 июля 2014

Элементы вектора хранятся непрерывно , поэтому лучший и простой способ:

std::vector<char> v;
char* c = &v[0];
0 голосов
/ 13 августа 2011

Решение C ++ 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); });
0 голосов
/ 13 августа 2011

const char * также совпадает с char *, отличается только в const_ness, ваш интерфейсный метод принимает как const, так и неконстантную строку.

Не возвращает ли c_str () constобугливается?Будет ли это проблемой, если мне просто нужен символ *?

Да, он возвращает константную строку, и нет проблем не должно

const char*a="something";
////whatever it is here
const char* retfunc(const char*a)
{
   char*temp=a;
   //process then return temp
}

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

...