web-dev-qa-db-fra.com

std :: vector <std :: string> à char * array

J'ai un std::vector<std::string> que je dois utiliser pour l'argument d'une fonction C qui se lit comme suit: char* foo. J'ai vucomment convertir un std::string en char*. En tant que nouveau venu dans C++, j'essaie de comprendre comment effectuer cette conversion sur chaque élément du vecteur et produire le tableau char*.

J'ai vu plusieurs questions étroitement liées SO, mais la plupart semblent illustrer des façons de changer de direction et de créer std::vector<std::string>.

26
Christopher DuBois

Vous pouvez utiliser std::transform comme:

std::transform(vs.begin(), vs.end(), std::back_inserter(vc), convert);  

Ce qui vous oblige à implémenter convert() en tant que:

char *convert(const std::string & s)
{
   char *pc = new char[s.size()+1];
   std::strcpy(pc, s.c_str());
   return pc; 
}

Code de test:

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

Sortie:

std::string
std::vector<std::string>
char*
std::vector<char*>

Démo en ligne: http://ideone.com/U6QZ5

Vous pouvez utiliser &vc[0] partout où vous avez besoin de char**

Notez que, puisque nous utilisons new pour allouer de la mémoire pour chaque std::string (dans la fonction convert), nous devons libérer la mémoire à la fin. Cela vous donne la possibilité de changer le vecteur vs; vous pouvez Push_back plusieurs chaînes, supprimer le fichier existant de vs et vc (i.e vector<char*> sera toujours valide!

Mais si vous ne voulez pas cette flexibilité, alors vous pouvez utiliser cette fonction convert:

const char *convert(const std::string & s)
{
   return s.c_str();
}

Et vous devez changer std::vector<char*> en std::vector<const char*>.

Maintenant, après la transformation, si vous modifiez vs en insérant de nouvelles chaînes ou en supprimant les anciennes, toutes les char* dans vc risquent de ne plus être valides. C'est un point important. Un autre point important est que vous n'avez plus besoin d'utiliser delete vc[i] dans votre code.

31
Nawaz

Le mieux que vous puissiez faire est d’attribuer un std::vector de const char* de la même taille que votre vecteur. Ensuite, parcourez chaque élément du vecteur en appelant c_str() pour obtenir le tableau de chaînes et en le stockant avec l’élément correspondant du tableau. Ensuite, vous pouvez passer le pointeur sur le premier élément de ce vecteur à la fonction en question.

Le code ressemblerait à ceci:

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

Notez que vous ne pouvez pas autoriser le redimensionnement du vecteur de chaînes d'origine entre le moment où vous extrayez le const char*s du std::strings et le moment où vous appelez la fonction C.

10
Nicol Bolas

Cela devrait fonctionner:

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

MODIFIER:

Voici comment libérer ces structures de données en supposant que vec ait toujours le nombre correct d’éléments. Si votre fonction C modifie ce tableau d’une manière ou d’une autre, vous devrez peut-être obtenir une autre taille.

for(size_t i = 0; i < vec.size(); i++){
    delete [] arr[i];
}
delete [] arr;

EDIT à nouveau:

Il peut ne pas être nécessaire de copier les chaînes si votre fonction C ne les modifie pas. Si vous pouvez préciser votre interface, nous pourrons vous fournir une meilleure aide.

7
GWW

Une solution C++ 0x, dans laquelle les éléments de std::string sont assurés d'être stockés de manière contiguë:

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

Si la fonction null termine son argument, l'appel (s.begin(), s.end()) risque de ne plus être significatif après l'appel. Vous pouvez post-traiter pour résoudre ce problème:

s = std::string(s.begin(), std::find(s.begin(), s.end(), '\0'));

Une version plus élaborée qui copie séparément chaque chaîne dans un 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
Luc Danton