std :: string to char *

335

Je veux convertir une chaîne std :: en un type de données char * ou char [] .

std::string str = "string";
char* chr = str;

Résultats: "erreur: impossible de convertir 'std :: string' en 'char' ..." .

Quelles sont les méthodes disponibles pour ce faire?


la source

Réponses:

672

Il ne se convertira pas automatiquement (Dieu merci). Vous devrez utiliser la méthode c_str()pour obtenir la version de la chaîne C.

std::string str = "string";
const char *cstr = str.c_str();

Notez qu'il renvoie un const char *; vous n'êtes pas autorisé à modifier la chaîne de style C renvoyée par c_str(). Si vous souhaitez le traiter, vous devrez d'abord le copier:

std::string str = "string";
char *cstr = new char[str.length() + 1];
strcpy(cstr, str.c_str());
// do stuff
delete [] cstr;

Ou en C ++ moderne:

std::vector<char> cstr(str.c_str(), str.c_str() + str.size() + 1);
orlp
la source
4
@Kerrek SB: C'était un exemple, utiliserait un pointeur intelligent dans du vrai code, ou plus probablement éviterait c_strcomplètement cette folie.
orlp
14
La réponse est volumineuse, inélégante, non locale, utilise des tableaux bruts et nécessite une attention à la sécurité d'exception. vectora été inventé précisément comme un wrapper pour les tableaux dynamiques, donc ne pas l'utiliser semble au mieux une opportunité manquée, et en violation de l'esprit du C ++ au pire.
Kerrek SB
21
Tout d'abord, oui, la réponse est volumineuse. D'abord, j'explique l'erreur de l'OP (pensant que std::stringcela se convertirait automatiquement), puis j'explique ce qu'il devrait utiliser, avec un exemple de code court. En pensant à l'avenir, j'explique également certains effets secondaires de l'utilisation de cette fonction, dont l'un est que vous ne pouvez pas modifier la chaîne renvoyée par c_str(). Vous voyez par erreur mes courts exemples comme un véritable code de résolution de problèmes, ce qui n'est pas le cas.
orlp
8
J'ai rétrogradé la réponse. Cette réponse n'est en effet pas utile et le fait qu'elle ait été acceptée est très regrettable. Cette réponse ignore complètement les bonnes pratiques de programmation C ++ et la sécurité des exceptions et il existe des solutions bien supérieures, dont certaines sont données dans d'autres réponses à cette question (par exemple la réponse donnée par ildjarn qui utilise std::vector<char>).
James McNellis
114
@ james-mcnellis, j'ai choisi cette réponse comme la bonne parce qu'elle répondait EXACTEMENT à ce que je demandais ... Pas plus, pas moins. Je n'ai pas demandé ce que vous pensez que je devrais faire, je n'ai pas demandé une solution différente pour ce que vous pensez que je fais, je n'ai pas demandé de bonnes pratiques. Vous n'avez aucune idée de ce dans quoi je travaille, où mon code va être implémenté et dans quelles conditions. Certains devraient apprendre à lire, à comprendre les questions et à répondre à ce qui est réellement demandé. Pas besoin de se montrer ici.
151

Plus de détails ici , et ici mais vous pouvez utiliser

string str = "some string" ;
char *cstr = &str[0];
bobobobo
la source
15
FWIW, dans mon livre, c'est la seule bonne réponse à la question qui a été réellement posée. Une chaîne std :: est intrinsèquement mutable: les gens qui la font sonner comme modifier le contenu de la chaîne sont en quelque sorte le travail du diable semble manquer ce fait.
Jay Freeman -saurik-
2
oui, c'est la bonne réponse. il est stupide que, compte tenu de la fréquence d'utilisation, il n'y ait pas de méthode standard pour le faire, comme le lockbuffer de msoft.
Erik Aronesty
1
J'ai changé 0 en 0u. Parce que certains compilateurs / bibliothèques se plaindront (croyez-le ou non) d'une certaine ambiguïté lorsque les avertissements sont activés complètement pour la construction & str [0]
Erik Aronesty
Je doute que lorsque le str est supprimé, le char cstr soit rendu nul. Donc, je suppose que seul un pointeur de la chaîne est affecté au pointeur char. La conversion de chaîne en char n'est donc pas complètement terminée. La chaîne pointe uniquement sur char * mais sa valeur n'est pas convertie. Ai-je raison???
Samitha Chathuranga,
5
Notez qu'il s'agit uniquement de C ++ 11. Les versions précédentes peuvent ne pas avoir de stockage continu ou il manque le
zéro de
39

Si j'avais besoin d'une copie brute modifiable du contenu d'une chaîne c ++, alors je ferais ceci:

std::string str = "string";
char* chr = strdup(str.c_str());

et ensuite:

free(chr); 

Alors pourquoi ne pas jouer avec std :: vector ou new [] comme n'importe qui d'autre? Parce que quand j'ai besoin d'une chaîne char * brute de style C mutable, alors parce que je veux appeler le code C qui change la chaîne et le code C désalloue les choses avec free () et alloue avec malloc () (strdup utilise malloc) . Donc, si je passe ma chaîne brute à une fonction X écrite en C, cela pourrait avoir une contrainte sur son argument qu'elle doit allouer sur le tas (par exemple si la fonction peut vouloir appeler realloc sur le paramètre). Mais il est très peu probable qu'il attende un argument alloué avec (certains redéfinis par l'utilisateur) new []!

Mainframe nordique
la source
Vous devez expliquer d'où strdupvient.
LF
22

(Cette réponse s'applique uniquement à C ++ 98.)

S'il vous plaît, n'utilisez pas de brut char*.

std::string str = "string";
std::vector<char> chars(str.c_str(), str.c_str() + str.size() + 1u);
// use &chars[0] as a char*
ildjarn
la source
1
En fait, j'avais juste besoin de quelque chose comme ça plus tôt dans la journée. Je me demandais, est-ce correct de dire vector<char>(str.c_str(), str.c_str() + str.size() + 1), sans affecter le pointeur char à un temporaire?
Kerrek SB, le
1
@Kerrek: Oui, la valeur de retour de std::basic_string<>::c_str()est valide jusqu'à ce que le stringsoit modifié ou détruit. Cela implique également qu'il renvoie la même valeur lors des appels suivants tant que le stringn'est pas modifié.
ildjarn
2
@friendzis: Il n'y a pas de vitesse ou d'espace supplémentaire à utiliser vectorde cette façon. Et si l'on devait écrire du code sans exception sans mécanisme RAII (c'est-à-dire en utilisant des pointeurs bruts), la complexité du code serait beaucoup plus élevée que cette simple ligne unique.
ildjarn
7
C'est un mythe qui vectora une énorme quantité de frais généraux et de complexité. Si vous avez besoin d'un tableau de caractères mutable , alors en fait, un vecteur de caractères est à peu près le wrapper C ++ idéal. Si votre exigence appelle simplement un pointeur const-char, alors utilisez-le c_str()et vous avez terminé.
Kerrek SB
3
@ildjarn: En fait, c'était le cas .
Courses de légèreté en orbite du
14
  • Si vous voulez juste une chaîne de style C représentant le même contenu:

    char const* ca = str.c_str();
  • Si vous voulez une chaîne de style C avec un nouveau contenu, une façon (étant donné que vous ne connaissez pas la taille de la chaîne au moment de la compilation) est l'allocation dynamique:

    char* ca = new char[str.size()+1];
    std::copy(str.begin(), str.end(), ca);
    ca[str.size()] = '\0';

    N'oubliez pas delete[]plus tard.

  • Si vous souhaitez plutôt un tableau alloué statiquement et de longueur limitée:

    size_t const MAX = 80; // maximum number of chars
    char ca[MAX] = {};
    std::copy(str.begin(), (str.size() >= MAX ? str.begin() + MAX : str.end()), ca);

std::stringne se convertit pas implicitement à ces types pour la simple raison que le faire est généralement une odeur de conception. Assurez-vous d'en avoir vraiment besoin.

Si vous en avez vraiment besoin char*, la meilleure façon est probablement:

vector<char> v(str.begin(), str.end());
char* ca = &v[0]; // pointer to start of vector
Courses de légèreté en orbite
la source
1
Pourquoi &str.front(), &str.back()(qui ne sont pas présents dans C ++ 03) au lieu des plus courants str.begin()et str.end()?
Armen Tsirunyan
1
qu'en est-il str.begin(), ou même std::begin(str), comme un itérateur? Je ne crois pas stringavoir d'obligation d'être dans une mémoire contiguë comme vector, ou l'a-t-il?
xtofl
1
@xtofl: J'en ai déjà édité. Et oui, depuis C ++ 11 il y a une obligation; c'était implicite en C ++ 03.
Courses de légèreté en orbite le
@xtofl: pas en C ++ 03. En C ++ 11, nous avons cette garantie, ainsi que les fonctions avant () et arrière (), qui ont quand même été mal utilisées dans la réponse d'origine
Armen Tsirunyan
1
@Tomalak: Ils ont été mal utilisés en ce que vous en aviez besoin &back() + 1, pas&back()
Armen Tsirunyan
12

Ce serait mieux comme un commentaire sur la réponse de Bobobobo, mais je n'ai pas de représentant pour cela. Il accomplit la même chose mais avec de meilleures pratiques.

Bien que les autres réponses soient utiles, si vous avez besoin de convertir std::stringen char*explicit sans const, const_castc'est votre ami.

std::string str = "string";
char* chr = const_cast<char*>(str.c_str());

Notez que cela ne vous donnera pas une copie des données; il vous donnera un pointeur sur la chaîne. Ainsi, si vous modifiez un élément de chr, vous modifierez str.

sans poils
la source
6

En supposant que vous ayez juste besoin d'une chaîne de style C pour passer en entrée:

std::string str = "string";
const char* chr = str.c_str();
Mark B
la source
4

Pour être strictement pédant, vous ne pouvez pas "convertir une chaîne std :: en un type de données char * ou char []."

Comme les autres réponses l'ont montré, vous pouvez copier le contenu de la chaîne std :: dans un tableau char, ou créer un const char * dans le contenu de la chaîne std :: afin de pouvoir y accéder dans un "style C" .

Si vous essayez de changer le contenu de la chaîne std ::, le type std :: string possède toutes les méthodes pour faire tout ce dont vous pourriez avoir besoin.

Si vous essayez de le passer à une fonction qui prend un char *, il y a std :: string :: c_str ().

Rob K
la source
4

Voici une version plus robuste de Protocol Buffer

char* string_as_array(string* str)
{
    return str->empty() ? NULL : &*str->begin();
}

// test codes
std::string mystr("you are here");
char* pstr = string_as_array(&mystr);
cout << pstr << endl; // you are here
zangw
la source
+1 pour vérifier que la chaîne est vide. J'ajouterais également une vérification pour m'assurer que la chaîne est terminée par zéro: if (str[str.length()-1] != 0) str.push_back(0)
GaspardP
4

Conversion dans le style OOP

converter.hpp

class StringConverter {
    public: static char * strToChar(std::string str);
};

converter.cpp

char * StringConverter::strToChar(std::string str)
{
    return (char*)str.c_str();
}

usage

StringConverter::strToChar("converted string")
Patryk Merchelski
la source
J'aime la façon dont la conversion en char * élimine la const de la sortie de c_str, je pense que la raison pour laquelle c_str renvoie const char * est d'éliminer les problèmes potentiels d'accès à la mémoire, en fournissant une version en lecture seule de la chose. l'OP veut avoir un char *, mais je pense qu'il pourrait être mieux servi par const char * sortie de c_str à la place, car c'est plus sûr, et les fonctions qui prennent char * prennent généralement const char * également (en supposant que les fonctions ne le font pas faire quelque chose comme changer leurs entrées, ce qui en général, ils ne devraient pas).
Felipe Valdes
3

Pour être complet, n'oubliez pas std::string::copy().

std::string str = "string";
const size_t MAX = 80;
char chrs[MAX];

str.copy(chrs, MAX);

std::string::copy()NUL ne se termine pas. Si vous devez garantir un terminateur NUL pour une utilisation dans les fonctions de chaîne C:

std::string str = "string";
const size_t MAX = 80;
char chrs[MAX];

memset(chrs, '\0', MAX);
str.copy(chrs, MAX-1);
Jeffery Thomas
la source
3

Vous pouvez le faire en utilisant l'itérateur.

std::string str = "string";
std::string::iterator p=str.begin();
char* chr = &(*p);

Bonne chance.

TS.PARK
la source
3

Une version sûre de la réponse char * d'orlp en utilisant unique_ptr:

std::string str = "string";
auto cstr = std::make_unique<char[]>(str.length() + 1);
strcpy(cstr.get(), str.c_str());
Enhex
la source
3

Pour obtenir un à const char *partir d'une std::stringutilisation de la c_str()fonction membre:

std::string str = "string";
const char* chr = str.c_str();

Pour obtenir un non-const à char *partir d'un, std::stringvous pouvez utiliser la data()fonction membre qui retourne un pointeur non-const depuis C ++ 17:

std::string str = "string";
char* chr = str.data();

Pour les anciennes versions du langage, vous pouvez utiliser la construction de plages pour copier la chaîne dans un vecteur à partir duquel un pointeur non const peut être obtenu:

std::string str = "string";
std::vector<char> str_copy(str.c_str(), str.c_str() + str.size() + 1);
char* chr = str_copy.data();

Mais attention, cela ne vous permettra pas de modifier la chaîne contenue dans str, seules les données de la copie peuvent être modifiées de cette façon. Notez qu'il est particulièrement important dans les anciennes versions de la langue d'utiliser c_str()ici, car à l'époque, il std::stringn'était pas garanti que la terminaison null soit terminée jusqu'à ce qu'elle c_str()soit appelée.

François Andrieux
la source
2
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
cegprakash
la source
2

Alternativement, vous pouvez utiliser des vecteurs pour obtenir un caractère inscriptible * comme illustré ci-dessous;

//this handles memory manipulations and is more convenient
string str;
vector <char> writable (str.begin (), str.end) ;
writable .push_back ('\0'); 
char* cstring = &writable[0] //or &*writable.begin () 

//Goodluck  

la source
Cela allouera une nouvelle mémoire au vecteur, puis copiera chaque caractère. std :: string est déjà un conteneur, vous pourriez aussi bien pousser push_back (0) vers votre chaîne et faire & str [0]
GaspardP
1

Cela fonctionnera également

std::string s;
std::cout<<"Enter the String";
std::getline(std::cin, s);
char *a=new char[s.size()+1];
a[s.size()]=0;
memcpy(a,s.c_str(),s.size());
std::cout<<a;  
Genocide_Hoax
la source