wake-up-neo.com

std :: vector <std :: string> in char * -Array

Ich habe einen std::vector<std::string>, den ich für das Argument einer C-Funktion verwenden muss, das char* foo liest. Ich habe gesehenwie einen std::string in char* konvertieren. Als Neuling in C++ versuche ich zusammenzubringen, wie diese Konvertierung für jedes Element des Vektors durchgeführt und das char*-Array erzeugt wird.

Ich habe mehrere eng verwandte SO - Fragen gesehen, aber die meisten scheinen Wege aufzuzeigen, in die andere Richtung zu gehen und std::vector<std::string> zu erstellen.

26

Sie können std::transform verwenden als:

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

Dafür müssen Sie convert() implementieren als:

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

Testcode:

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

Ausgabe:

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

Online-Demo: http://ideone.com/U6QZ5

Sie können &vc[0] verwenden, wo immer Sie char** benötigen. 

Da wir new verwenden, um Speicher für jeden std::string (in convert-Funktion) zuzuordnen, müssen wir den Speicher am Ende freigeben. Dies gibt Ihnen die Flexibilität, den Vektor vs zu ändern. Sie können Push_back weitere Zeichenfolgen hinzufügen, die vorhandene aus vs löschen und vc (d. h. vector<char*> bleibt gültig!

Wenn Sie diese Flexibilität jedoch nicht wünschen, können Sie diese convert-Funktion verwenden:

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

Und Sie müssen std::vector<char*> in std::vector<const char*> ändern.

Wenn Sie nach der Umwandlung vs durch Einfügen neuer Zeichenfolgen oder durch Löschen der alten Zeichenketten ändern, werden alle char* in vc möglicherweise ungültig. Das ist ein wichtiger Punkt. Ein weiterer wichtiger Punkt ist, dass Sie delete vc[i] nicht mehr in Ihrem Code verwenden müssen.

31
Nawaz

Am besten weisen Sie einen std::vector von const char* zu, der dieselbe Größe wie Ihr Vektor hat. Gehen Sie dann jedes Element des Vektors ab und rufen Sie c_str() auf, um das String-Array abzurufen, und speichern Sie das entsprechende Element des Arrays. Dann können Sie den Zeiger auf das erste Element dieses Vektors an die betreffende Funktion übergeben.

Der Code würde so aussehen:

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

Beachten Sie, dass Sie durch können nicht zulassen, dass der ursprüngliche Vektor von Strings zwischen dem Zeitpunkt, zu dem Sie den const char*s vom std::strings abrufen, und dem Zeitpunkt, zu dem Sie die C-Funktion aufrufen, in der Größe geändert werden.

10
Nicol Bolas

Das sollte funktionieren:

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

BEARBEITEN:

Gehen Sie wie folgt vor, wenn Sie diese Datenstrukturen freigeben würden, vorausgesetzt, vec hat immer noch die richtige Anzahl von Elementen. Wenn Ihre C-Funktion dieses Array ändert, müssen Sie möglicherweise die Größe auf andere Weise ermitteln.

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

Nochmals bearbeiten:

Es ist möglicherweise nicht erforderlich, die Zeichenfolgen zu kopieren, wenn Ihre C-Funktion die Zeichenfolgen nicht ändert. Wenn Sie wissen können, wie Ihre Benutzeroberfläche aussieht, bin ich sicher, dass wir Ihnen eine bessere Hilfe bieten könnten.

7
GWW

Eine C++ 0x-Lösung, bei der garantiert garantiert ist, dass Elemente von std::string zusammenhängend gespeichert werden:

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

Wenn die Funktion null ihr Argument beendet, ist (s.begin(), s.end()) nach dem Aufruf möglicherweise nicht aussagekräftig. Sie können das Problem nachbearbeiten:

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

Eine aufwendigere Version, die jeden String separat in einen char[] kopiert:

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