Conversión de C ++ / CLI de System :: String ^ a std :: string

90

¿Alguien puede publicar un código simple que convierta,

System::String^

A,

C ++ std::string

Es decir, solo quiero asignar el valor de,

String^ originalString;

A,

std::string newString;
sivabudh
fuente

Respuestas:

38

Echa un vistazo System::Runtime::InteropServices::Marshal::StringToCoTaskMemUni()y sus amigos.

Lo siento, no puedo publicar código ahora; No tengo VS en esta máquina para verificar que se compila antes de publicar.

Martín
fuente
160

No enrolle el suyo, use estos prácticos (y extensibles) contenedores proporcionados por Microsoft.

Por ejemplo:

#include <msclr\marshal_cppstd.h>

System::String^ managed = "test";
std::string unmanaged = msclr::interop::marshal_as<std::string>(managed);
tragomaskhalos
fuente
2
Gracias por este enlace útil, esta sugerencia me ahorró mucha codificación. como nota al margen: las plantillas / clases están en #include <msclr \ *. h> (por ejemplo, #include <msclr \ marshal.h>) y en el espacio de nombres msclr :: interop, vea un ejemplo en msdn.microsoft.com /de-de/library/vstudio/bb384859(v=vs.90).aspx )
Beachwalker
4
Si bien esto es conveniente, carece totalmente de soporte de codificación adecuado. Consulte también mi pregunta SO: stackoverflow.com/questions/18894551/… . Mi suposición es que marshal_as convierte cadenas Unicode al ACP en std :: string.
Mike Lischke
La recomendación de MS es usar marshal_context y eliminarlo después de que se haya realizado la conversión. El enlace: msdn.microsoft.com/en-us/library/bb384856.aspx
Ruslan Makrenko
40

Puede hacer esto fácilmente de la siguiente manera

#include <msclr/marshal_cppstd.h>

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

std::string converted_xyz=msclr::interop::marshal_as< std::string >( xyz);
Sriwantha Attanayake
fuente
+1 para una solución breve y simple y un ejemplo de trabajo simple (aunque hay un paréntesis adicional al final de su código)
Simon Forsberg
Esta es la única solución que responde directamente a la pregunta.
Jiminion
8
hmm ... 33 votos a favor para una respuesta que ya se dio más de 2 años antes con casi las mismas líneas de código. respeto por ganar tantos puntos por eso. ;-)
Beachwalker
20

Esto funcionó para mí:

#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
fuente
3
Traducción al español: "Voy a responder a esta publicación también: p. Esta es mi función".
sivabudh
9

Aquí hay algunas rutinas de conversión que escribí hace muchos años para un proyecto c ++ / cli, aún deberían funcionar.

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
fuente
@alap, use System :: Runtime :: InteropServices :: Marshal o escriba usando el espacio de nombres System :: Runtime :: InteropServices; .
neo
6

Pasé horas tratando de convertir un valor ToString de listbox de Windows Form a una cadena estándar para poder usarlo con fstream para generar un archivo txt. Mi Visual Studio no venía con archivos de encabezado marshal que varias respuestas que encontré decían usar. Después de tanto ensayo y error, finalmente encontré una solución al problema que solo usa 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;

Y aquí está la página de MSDN con el ejemplo: http://msdn.microsoft.com/en-us/library/1b4az623(v=vs.80).aspx

Sé que es una solución bastante simple, pero esto me tomó HORAS de resolución de problemas y visitar varios foros para finalmente encontrar algo que funcionó.

Joe
fuente
6

Encontré una manera fácil de obtener un std :: string de un String ^ es usar sprintf ().

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

¡No es necesario llamar a las funciones Marshal!

ACTUALIZACIÓN Gracias a Eric, modifiqué el código de muestra para verificar el tamaño de la cadena de entrada para evitar el desbordamiento del búfer.

Jónico316
fuente
1
Es una decisión curiosa introducir una vulnerabilidad de desbordamiento de búfer en su código solo para evitar llamar a funciones especialmente diseñadas para marshall strings.
Eric
Simplemente estoy presentando un enfoque diferente si alguien no quiere usar las funciones de mariscal. Agregué un cheque para el tamaño para evitar desbordes.
Ionian316
@Eric Internamente se está organizando para usted. Consulte esta respuesta SO para obtener más detalles. Si verifica el tamaño de antemano, no tendrá problemas de desbordamiento y el código es mucho más limpio.
Ionian316
4

C # usa el formato UTF16 para sus cadenas.
Por lo tanto, además de convertir los tipos, también debe ser consciente del formato real de la cadena.

Al compilar para el conjunto de caracteres de varios bytes, Visual Studio y la API de Win asumen UTF8 (en realidad, la codificación de Windows es Windows-28591 ).
Al compilar para el juego de caracteres Unicode, Visual Studio y la API de Win asumen UTF16.

Por lo tanto, también debe convertir la cadena de formato UTF16 a UTF8, y no solo convertirla a std :: string.
Esto será necesario cuando trabaje con formatos de varios caracteres como algunos idiomas no latinos.

La idea es decidir que std::wstring siempre representa UTF16 .
Y std::string siempre representa UTF8 .

Esto no es impuesto por el compilador, es más una buena política.

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

O tenerlo en una sintaxis más compacta:

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
fuente
1
Solo quiero enfatizar la importancia de convertir a UTF8 en mi caso de uso: necesitaba pasar una ruta de archivo recibida de Win32 OpenFileDialog (donde son posibles los nombres de archivo con caracteres multibyte, por ejemplo, nombres de archivo que contienen caracteres asiáticos) al código del motor a través de un std :: string, por lo que la conversión a UTF8 fue vital. ¡Gracias por la excelente respuesta!
Jason McClinsey
0

Me gusta alejarme del alguacil.

Using CString newString(originalString);

Me parece mucho más limpio y rápido. No hay necesidad de preocuparse por crear y eliminar un contexto.

LL.
fuente
0

// Usé VS2012 para escribir el siguiente código: convert_system_string a 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
fuente