std :: string to char *

335

Chcę przekonwertować std :: string na typ danych char * lub char [] .

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

Powoduje: „błąd: nie można przekonwertować 'std :: string' na 'char' ...” .

Jakie są dostępne metody, aby to zrobić?


źródło

Odpowiedzi:

672

Nie zamieni się automatycznie (dzięki Bogu). Musisz użyć tej metody, c_str()aby uzyskać wersję ciągu C.

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

Zauważ, że zwraca a const char *; nie mogą zmienić ciąg C-styl zwrócony przez c_str(). Jeśli chcesz go przetworzyć, musisz go najpierw skopiować:

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

Lub we współczesnym C ++:

std::vector<char> cstr(str.c_str(), str.c_str() + str.size() + 1);
orlp
źródło
4
@Kerrek SB: To był przykład, używałby inteligentnego wskaźnika w prawdziwym kodzie, a raczej c_strcałkowicie unikałby tego szaleństwa.
orlp
14
Odpowiedź jest nieporęczna, nieelegancka, nielokalna, wykorzystuje surowe tablice i wymaga zwrócenia uwagi na bezpieczeństwo wyjątkowe. vectorzostał wymyślony właśnie jako opakowanie dla tablic dynamicznych, więc nieużywanie go wydaje się w najlepszym razie straconą szansą, aw najgorszym naruszeniem ducha C ++.
Kerrek SB
21
Po pierwsze tak, odpowiedź jest nieporęczna. Najpierw wyjaśniam błąd OP (myśląc, std::stringże automatycznie się skonwertuje), a następnie wyjaśniam, czego powinien użyć, używając krótkiej próbki kodu. Myśląc dalej, wyjaśniam również niektóre skutki uboczne korzystania z tej funkcji, z których jedną jest to, że nie możesz edytować zwracanego ciągu c_str(). Moje błędne przykłady błędnie postrzegasz jako prawdziwy kod do rozwiązywania problemów, ale tak nie jest.
orlp
8
Głosowałem za odpowiedzią. Ta odpowiedź rzeczywiście nie jest przydatna, a fakt, że została zaakceptowana, jest najbardziej niefortunny. Ta odpowiedź całkowicie pomija dobre praktyki programowania w C ++ i bezpieczeństwo wyjątków, a istnieją o wiele lepsze rozwiązania, z których niektóre podano w innych odpowiedziach na to pytanie (np. Odpowiedź udzielona przez ildjarn, który używa std::vector<char>).
James McNellis,
114
@ james-mcnellis, wybrałem tę odpowiedź jako prawidłową, ponieważ odpowiedziała DOKŁADNIE na to, o co prosiłem ... Nie więcej, nie mniej. Nie prosiłem o to, co według ciebie powinienem zrobić, nie prosiłem o inne rozwiązanie tego, co według ciebie robię, nie prosiłem o dobre praktyki. Nie masz pojęcia, w czym pracuję, gdzie mój kod zostanie wdrożony i na jakich warunkach. Niektórzy powinni nauczyć się czytać, rozumieć pytania i odpowiadać na to, o co się pyta. Nie musisz się tutaj pokazywać.
150

Więcej szczegółów tutaj i tutaj, ale możesz użyć

string str = "some string" ;
char *cstr = &str[0];
Bobobobo
źródło
15
FWIW, w mojej książce jest to jedyna poprawna odpowiedź na faktycznie zadane pytanie. Std :: string jest z natury zmienny: ludzie, którzy sprawiają, że brzmi to jak modyfikowanie zawartości łańcucha, w jakiś sposób dzieło diabła wydaje się nie mieć tego faktu.
Jay Freeman -saurik-
2
tak, to poprawna odpowiedź. to głupie, że biorąc pod uwagę częstotliwość używania, nie ma standardowej metody, aby to zrobić, takiej jak blokada bufora msoft.
Erik Aronesty
1
Zmieniłem 0 na 0u. Ponieważ niektóre kompilatory / biblioteki będą (wierzcie lub nie) narzekają na pewną dwuznaczność, gdy ostrzeżenia są włączane do końca dla konstrukcji & str [0]
Erik Aronesty
Wątpię, kiedy str zostanie usunięty, char cstr ma wartość null. Sądzę więc, że tylko wskaźnik łańcucha jest przypisany do wskaźnika char. Tak więc konwersja łańcucha znaków na char nie jest dosłownie zakończona. Ciąg jest wskazywany tylko na char *, ale jego wartość nie jest konwertowana. Mam rację???
Samitha Chathuranga,
5
Zauważ, że jest to tylko C ++ 11. Poprzednie wersje mogły nie mieć ciągłego przechowywania lub brakowało
końcowego zerowania
40

Gdybym potrzebował modyfikowalnej, surowej kopii zawartości łańcucha c ++, zrobiłbym to:

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

i później:

free(chr); 

Dlaczego więc nie będę majstrować przy standardzie std :: vector lub new [] jak ktokolwiek inny? Ponieważ gdy potrzebuję zmiennego surowego łańcucha char * w stylu C, to dlatego, że chcę wywołać kod C, który zmienia łańcuch, a kod C zwalnia rzeczy za pomocą free () i przydziela za pomocą malloc () (strdup używa malloc) . Więc jeśli przekażę mój nieprzetworzony ciąg do funkcji X napisanej w C , może on mieć ograniczenie w argumencie, który musi przypisać na stercie (na przykład, jeśli funkcja może chcieć wywołać realloc dla parametru). Ale jest bardzo mało prawdopodobne, że spodziewałby się argumentu przypisanego (niektórym przedefiniowanym przez użytkownika) nowemu []!

Nordic Mainframe
źródło
Powinieneś wyjaśnić, skąd strduppochodzi.
LF,
22

(Ta odpowiedź dotyczy tylko C ++ 98).

Proszę nie używać surowego 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
źródło
1
Tak naprawdę potrzebowałem dziś czegoś takiego wcześniej. Zastanawiałem się, czy można powiedzieć vector<char>(str.c_str(), str.c_str() + str.size() + 1)bez przypisywania wskaźnika char tymczasowego?
Kerrek SB,
1
@Kerrek: Tak, zwracana wartość std::basic_string<>::c_str()jest ważna do momentu stringzmiany lub zniszczenia. Oznacza to również, że zwraca tę samą wartość przy kolejnych połączeniach, o ile stringnie jest modyfikowany.
ildjarn
2
@friendzis: W ten sposób nie ma narzutu prędkości ani przestrzeni vector. A gdyby napisać kod bezpieczny dla wyjątków bez mechanizmu RAII (tj. Używając surowych wskaźników), złożoność kodu byłaby znacznie wyższa niż tego prostego jednowierszowego.
ildjarn
7
To mit, który vectorma ogromne koszty ogólne i złożoność. Jeśli twoim wymaganiem jest, abyś miał zmienną tablicę char, to w rzeczywistości wektor znaków jest właściwie idealnym opakowaniem w C ++. Jeśli twoje wymaganie wymaga tylko wskaźnika const-char, po prostu użyj c_str()i gotowe.
Kerrek SB
3
@ildjarn: Właściwie tak było .
Wyścigi lekkości na orbicie
15
  • Jeśli chcesz tylko ciąg w stylu C reprezentujący tę samą treść:

    char const* ca = str.c_str();
  • Jeśli chcesz łańcucha w stylu C z nową zawartością, jednym ze sposobów (biorąc pod uwagę, że nie znasz rozmiaru łańcucha w czasie kompilacji) jest alokacja dynamiczna:

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

    Nie zapomnij o delete[]tym później.

  • Jeśli zamiast tego chcesz mieć statycznie przydzieloną tablicę o ograniczonej długości:

    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::stringnie dokonuje domyślnej konwersji na te typy z tego prostego powodu, że konieczność wykonania tego jest zwykle zapachem projektowym. Upewnij się, że naprawdę potrzebujesz.

Jeśli zdecydowanie potrzebujesz char*, najlepszym sposobem jest prawdopodobnie:

vector<char> v(str.begin(), str.end());
char* ca = &v[0]; // pointer to start of vector
Lekkość Wyścigi na orbicie
źródło
1
Dlaczego &str.front(), &str.back()(które nie są obecne w C ++ 03) zamiast bardziej powszechnego str.begin()i str.end()?
Armen Tsirunyan,
1
co str.begin(), lub nawet std::begin(str), iterator-jak? Nie wierzę, że stringma jakiś obowiązek bycia w ciągłej pamięci vector, czy ma to?
xtofl,
1
@xtofl: Już je edytowałem. I tak, od C ++ 11 istnieje obowiązek; było to dorozumiane w C ++ 03.
Wyścigi lekkości na orbicie
@xtofl: Nie w C ++ 03. W C ++ 11 mamy tę gwarancję, a także funkcje front () i back (), które i tak zostały niewłaściwie użyte w oryginalnej odpowiedzi
Armen Tsirunyan
1
@Tomalak: Były wykorzystywane w niewłaściwy sposób &back() + 1, a nie&back()
Armen Tsirunyan,
12

Byłoby to lepsze jako komentarz do odpowiedzi bobobobo, ale nie mam za to przedstawiciela. Osiąga to samo, ale dzięki lepszym praktykom.

Chociaż inne odpowiedzi są przydatne, jeśli kiedykolwiek będziesz musiał przejść std::stringna char*jawnie bez const, const_castto twój przyjaciel.

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

Pamiętaj, że nie da ci to kopii danych; da ci wskaźnik do łańcucha. Zatem jeśli zmodyfikujesz element chr, zmodyfikujesz str.

bez włosów
źródło
7

Zakładając, że potrzebujesz tylko łańcucha w stylu C, aby przekazać jako dane wejściowe:

std::string str = "string";
const char* chr = str.c_str();
Mark B
źródło
5

Aby być ściśle pedantycznym, nie można „konwertować parametru std :: string na typ danych char * lub char [].”

Jak pokazały inne odpowiedzi, możesz skopiować zawartość std :: string do tablicy char lub utworzyć const char * do zawartości std :: string, abyś mógł uzyskać do niej dostęp w stylu „C” .

Jeśli próbujesz zmienić zawartość std :: string, typ std :: string ma wszystkie metody, aby zrobić wszystko, co może być potrzebne.

Jeśli próbujesz przekazać go do jakiejś funkcji, która przyjmuje char *, istnieje std :: string :: c_str ().

Rob K.
źródło
4

Dla kompletności nie zapomnij std::string::copy().

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

str.copy(chrs, MAX);

std::string::copy()nie kończy się NUL. Jeśli musisz zapewnić terminator NUL do użycia w funkcjach ciągów C:

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

memset(chrs, '\0', MAX);
str.copy(chrs, MAX-1);
Jeffery Thomas
źródło
4

Oto jeszcze jedna solidna wersja z 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
źródło
+1 za sprawdzenie, czy łańcuch jest pusty. Dodałbym również sprawdzenie, aby upewnić się, że łańcuch jest zakończony na zero: if (str[str.length()-1] != 0) str.push_back(0)
GaspardP
4

Konwersja w stylu OOP

konwerter.hpp

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

konwerter.cpp

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

stosowanie

StringConverter::strToChar("converted string")
Patryk Merchelski
źródło
Podoba mi się, jak rzutowanie na char * eliminuje const z wyjścia c_str, myślę, że powodem, dla którego c_str zwraca const char *, jest wyeliminowanie potencjalnych problemów z dostępem do pamięci, poprzez zapewnienie wersji tylko do odczytu. OP chce mieć char *, ale myślę, że lepiej by go obsłużył wynik const char * c_str, ponieważ jest to bezpieczniejsze, a funkcje, które przyjmują char *, zwykle również przyjmują const char * (zakładając, że funkcje nie zrobić coś takiego, jak zmienić swoje dane wejściowe, co ogólnie nie powinno).
Felipe Valdes
3
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
cegprakash
źródło
3

Alternatywnie można użyć wektorów, aby uzyskać zapisywalny znak *, jak pokazano poniżej;

//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  

źródło
Spowoduje to przydzielenie nowej pamięci dla wektora, a następnie skopiowanie każdego znaku. std :: string jest już kontenerem, możesz równie dobrze push_back (0) do swojego łańcucha i zrobić & str [0]
GaspardP
3

Możesz to zrobić za pomocą iteratora.

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

Powodzenia.

TS.PARK
źródło
3

Bezpieczna wersja odpowiedzi char * orlp przy użyciu unikalnego_ptr:

std::string str = "string";
auto cstr = std::make_unique<char[]>(str.length() + 1);
strcpy(cstr.get(), str.c_str());
Enhex
źródło
3

Aby uzyskać const char *z std::stringużycia c_str()funkcji członka:

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

Aby uzyskać non-const char *z an std::string, możesz użyć data()funkcji członka, która zwraca wskaźnik non-const od C ++ 17:

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

W starszych wersjach języka można użyć konstrukcji zakresu, aby skopiować ciąg do wektora, z którego można uzyskać wskaźnik niezmienny:

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

Ale uwaga: nie pozwoli to na modyfikację zawartego w nim ciągu str, tylko dane kopii można zmienić w ten sposób. Zauważ, że jest to szczególnie ważne w starszych wersjach tego języka c_str(), ponieważ w tamtych czasach std::stringnie było gwarantowane, że zostanie zakończone zerowo, dopóki nie c_str()zostanie wywołane.

François Andrieux
źródło
2

To też zadziała

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;  
Ludobójstwo
źródło