C ++ / CLI Konwersja z System :: String ^ do std :: string

91

Czy ktoś może opublikować prosty kod, który umożliwi konwersję,

System::String^

Do,

C ++ std::string

To znaczy, chcę tylko przypisać wartość,

String^ originalString;

Do,

std::string newString;
sivabudh
źródło

Odpowiedzi:

39

Sprawdź System::Runtime::InteropServices::Marshal::StringToCoTaskMemUni()i jego przyjaciół.

Przepraszamy, nie mogę teraz wysłać kodu; Nie mam programu VS na tym komputerze, aby sprawdzić, czy się kompiluje przed wysłaniem.

Jaskółka oknówka
źródło
164

Nie twórz własnego, użyj tych przydatnych (i rozszerzalnych) opakowań dostarczonych przez firmę Microsoft.

Na przykład:

#include <msclr\marshal_cppstd.h>

System::String^ managed = "test";
std::string unmanaged = msclr::interop::marshal_as<std::string>(managed);
tragomaskhalos
źródło
2
dzięki za ten przydatny link, ta wskazówka zaoszczędziła mi dużo czasu na kodowaniu. na marginesie: szablony / klasy znajdują się w #include <msclr \ *. h> (np. #include <msclr \ marshal.h>) oraz w przestrzeni nazw msclr :: interop, zobacz przykład na msdn.microsoft.com /de-de/library/vstudio/bb384859(v=vs.90).aspx )
Beachwalker
4
Chociaż jest to wygodne, całkowicie brakuje odpowiedniej obsługi kodowania. Zobacz także moje pytanie dotyczące SO: stackoverflow.com/questions/18894551/… . Moje założenie jest takie, że marshal_as konwertuje ciągi Unicode na ACP w std :: string.
Mike Lischke
Rekomendacją MS jest użycie marshal_context i usunięcie go po zakończeniu konwersji. Link: msdn.microsoft.com/en-us/library/bb384856.aspx
Ruslan Makrenko
40

Możesz to łatwo zrobić w następujący sposób

#include <msclr/marshal_cppstd.h>

System::String^ xyz="Hi boys"; 

std::string converted_xyz=msclr::interop::marshal_as< std::string >( xyz);
Sriwantha Attanayake
źródło
+1 dla krótkiego i prostego rozwiązania oraz prostego przykładu działania (chociaż na końcu twojego kodu jest dodatkowy nawias)
Simon Forsberg
To jedyne rozwiązanie, które bezpośrednio odpowiada na to pytanie.
Jiminion
8
hmm ... 33 głosy za odpowiedzią, która została udzielona już ponad 2 lata wcześniej z prawie tymi samymi liniami kodu. szacunek dla zdobycia za to tylu punktów. ;-)
Beachwalker
20

To zadziałało dla mnie:

#include <stdlib.h>
#include <string.h>
#include <msclr\marshal_cppstd.h>
//..
using namespace msclr::interop;
//..
System::String^ clrString = (TextoDeBoton);
std::string stdString = marshal_as<std::string>(clrString); //String^ to std
//System::String^ myString = marshal_as<System::String^>(MyBasicStirng); //std to String^
prueba.CopyInfo(stdString); //MyMethod
//..
//Where: String^ = TextoDeBoton;
//and stdString is a "normal" string;
Alejandro Perea
źródło
3
Tłumaczenie angielskie: „Odpowiem również na ten post: p. To jest moja funkcja”.
sivabudh
9

Oto kilka procedur konwersji, które napisałem wiele lat temu dla projektu c ++ / cli, powinny nadal działać.

void StringToStlWString ( System::String const^ s, std::wstring& os)
    {
        String^ string = const_cast<String^>(s);
        const wchar_t* chars = reinterpret_cast<const wchar_t*>((Marshal::StringToHGlobalUni(string)).ToPointer());
        os = chars;
        Marshal::FreeHGlobal(IntPtr((void*)chars));

    }
    System::String^ StlWStringToString (std::wstring const& os) {
        String^ str = gcnew String(os.c_str());
        //String^ str = gcnew String("");
        return str;
    }

    System::String^ WPtrToString(wchar_t const* pData, int length) {
        if (length == 0) {
            //use null termination
            length = wcslen(pData);
            if (length == 0) {
                System::String^ ret = "";
                return ret;
            }
        }

        System::IntPtr bfr = System::IntPtr(const_cast<wchar_t*>(pData));
        System::String^ ret = System::Runtime::InteropServices::Marshal::PtrToStringUni(bfr, length);
        return ret;
    }

    void Utf8ToStlWString(char const* pUtfString, std::wstring& stlString) {
        //wchar_t* pString;
        MAKE_WIDEPTR_FROMUTF8(pString, pUtfString);
        stlString = pString;
    }

    void Utf8ToStlWStringN(char const* pUtfString, std::wstring& stlString, ULONG length) {
        //wchar_t* pString;
        MAKE_WIDEPTR_FROMUTF8N(pString, pUtfString, length);
        stlString = pString;
    }
Ben Schwehn
źródło
@alap, Use System :: Runtime :: InteropServices :: Marshal lub pisz używając przestrzeni nazw System :: Runtime :: InteropServices; .
neo
6

Spędziłem wiele godzin próbując przekonwertować wartość ToString formularza listbox systemu Windows na standardowy ciąg, aby móc go użyć z fstream do wyjścia do pliku txt. Mój program Visual Studio nie zawierał plików nagłówkowych Marshal, których używam w kilku odpowiedziach. Po tylu próbach i błędach w końcu znalazłem rozwiązanie problemu, które używa tylko System :: Runtime :: InteropServices:

void MarshalString ( String ^ s, string& os ) {
   using namespace Runtime::InteropServices;
   const char* chars = 
      (const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
   os = chars;
   Marshal::FreeHGlobal(IntPtr((void*)chars));
}

//this is the code to use the function:
scheduleBox->SetSelected(0,true);
string a = "test";
String ^ c = gcnew String(scheduleBox->SelectedItem->ToString());
MarshalString(c, a);
filestream << a;

A oto strona MSDN z przykładem: http://msdn.microsoft.com/en-us/library/1b4az623(v=vs.80).aspx

Wiem, że to całkiem proste rozwiązanie, ale zajęło mi to GODZINY rozwiązywania problemów i odwiedzenia kilku forów, aby w końcu znaleźć coś, co działało.

Joe
źródło
6

Znalazłem prostym sposobem na uzyskanie std :: string z String ^ jest użycie sprintf ().

char cStr[50] = { 0 };
String^ clrString = "Hello";
if (clrString->Length < sizeof(cStr))
  sprintf(cStr, "%s", clrString);
std::string stlString(cStr);

Nie ma potrzeby wywoływania funkcji Marshal!

AKTUALIZACJA Dzięki Ericowi zmodyfikowałem przykładowy kod, aby sprawdzić rozmiar ciągu wejściowego, aby zapobiec przepełnieniu bufora.

Ionian316
źródło
1
To ciekawa decyzja o wprowadzeniu w kodzie luki przepełnienia bufora tylko po to, aby uniknąć wywoływania funkcji specjalnie zaprojektowanych do kierowania ciągami.
Eric
Po prostu przedstawiam inne podejście, jeśli ktoś nie chce używać funkcji marszałka. Dodałem sprawdzenie rozmiaru, aby zapobiec przepełnieniu.
Ionian316,
@Eric Wewnętrznie to krosowanie dla Ciebie. Zobacz tę odpowiedź SO, aby uzyskać szczegółowe informacje. Jeśli wcześniej sprawdzisz rozmiar, nie będziesz mieć żadnych problemów z przepełnieniem, a kod jest znacznie czystszy.
Ionian316
4

C # używa formatu UTF16 dla swoich ciągów.
Tak więc, oprócz zwykłej konwersji typów, powinieneś być również świadomy faktycznego formatu ciągu.

Podczas kompilowania dla zestawu znaków wielobajtowych program Visual Studio i interfejs API Win zakłada UTF8 (w rzeczywistości kodowanie systemu Windows, czyli Windows-28591 ).
Podczas kompilowania zestawu znaków Unicode Visual studio i interfejs API Win zakładają UTF16.

Dlatego też musisz przekonwertować łańcuch z formatu UTF16 na UTF8, a nie tylko przekonwertować go na std :: string.
Będzie to konieczne podczas pracy z formatami wieloznakowymi, takimi jak niektóre języki inne niż łacińskie.

Chodzi o to, aby zdecydować, że std::wstring zawsze reprezentuje UTF16 .
I std::string zawsze reprezentuje UTF8 .

Nie jest to wymuszane przez kompilator, jest to raczej dobra polityka.

#include "stdafx.h"
#include <string>
#include <codecvt>
#include <msclr\marshal_cppstd.h>

using namespace System;

int main(array<System::String ^> ^args)
{
    System::String^ managedString = "test";

    msclr::interop::marshal_context context;

    //Actual format is UTF16, so represent as wstring
    std::wstring utf16NativeString = context.marshal_as<std::wstring>(managedString); 

    //C++11 format converter
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;

    //convert to UTF8 and std::string
    std::string utf8NativeString = convert.to_bytes(utf16NativeString);

    return 0;
}

Lub utwórz bardziej zwartą składnię:

int main(array<System::String ^> ^args)
{
    System::String^ managedString = "test";

    msclr::interop::marshal_context context;
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;

    std::string utf8NativeString = convert.to_bytes(context.marshal_as<std::wstring>(managedString));

    return 0;
}
Yochai Timmer
źródło
1
Chcę tylko podkreślić znaczenie konwersji do UTF8 w moim przypadku użycia: potrzebowałem przekazać ścieżkę do pliku otrzymaną z Win32 OpenFileDialog (gdzie możliwe są nazwy plików ze znakami wielobajtowymi, np. Nazwy plików zawierające znaki azjatyckie) do kodu silnika przez std :: string, więc konwersja do UTF8 była niezbędna. Dzięki za doskonałą odpowiedź!
Jason McClinsey,
0

Lubię trzymać się z dala od marszałka.

Using CString newString(originalString);

Wydaje mi się dużo czystszy i szybszy. Nie musisz się martwić o tworzenie i usuwanie kontekstu.

LL.
źródło
0

// Użyłem VS2012 do napisania poniższego kodu - convert_system_string do Standard_Sting

        #include "stdafx.h"
        #include <iostream>
        #include <string> 

        using namespace System;
        using namespace Runtime::InteropServices; 


        void MarshalString ( String^ s, std::string& outputstring )
        {  
           const char* kPtoC =  (const char*) (Marshal::StringToHGlobalAnsi(s)).ToPointer();                                                        
           outputstring = kPtoC;  
           Marshal::FreeHGlobal(IntPtr((void*)kPtoC));  
        }   

        int _tmain(int argc, _TCHAR* argv[])
        {
             std::string strNativeString;  
             String ^ strManagedString = "Temp";

             MarshalString(strManagedString, strNativeString);  
             std::cout << strNativeString << std::endl; 

             return 0;
        }
Praveer Kumar
źródło