将C++ .NET中的System::String转换为std::string

57

如何在C++ .NET中将System::String转换为std::string?


5
不要将内容转换为std::wstring。System.String是Unicode编码,不是ASCII编码。 - MSalters
6
你好,@MSalters。你似乎认为转换不包括翻译,或者每个人都可以随时选择他们要与之交互的API。 - user645280
或者说 std::string 必须是 ASCII 编码(提示:它应该是 UTF-8 编码!) - Lightness Races in Orbit
6个回答

71

如果您使用的是最新版本的 .net,那么语法更加简洁。

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

#include <msclr\marshal_cppstd.h>

using namespace System;

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

    msclr::interop::marshal_context context;
    std::string standardString = context.marshal_as<std::string>(managedString);

    return 0;
}

这也可以在异常处理方面提供更好的清理。

还有一个MSDN文章提供其他各种类型的转换。


这个功能也能处理从UTF-16(.Net默认)到UTF-8(std :: string)的编码转换吗? - zak
@zak,内部的代码在msclr::interop::marshal_context中使用了Win32 API WideCharToMultiByte,它最终转换为Windows-1252编码,至少根据我的经验。可能有方法可以避开这个问题,但我认为如果你想要UTF-8,那么你需要自己处理。例如,英镑符号'£'被转换为单字节0xA3,而拉丁字母Q 'ʠ' 被转换为单个“?”符号。无论C++/CLI项目的字符集设置是MBCS还是Unicode,它都会以这种方式运行。 - Mike E
我们从什么时候开始在 #include 标签内使用反斜杠(\)? - JvO

29

而对于C++/CLI的较新版本中关于更简单的方法,你可以不使用marshal_context来完成。我知道这在Visual Studio 2010中是可行的,但之前的情况就不太确定了。


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

#include <msclr\marshal_cppstd.h>

using namespace msclr::interop;

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

    std::string standardString = marshal_as<std::string>(managedString);

    return 0;
}


1
查看Collin提供的MSDN文章,以了解何时使用marshal_as和何时使用marshal_context。一般来说,在需要清理非托管资源时,需要使用marshal_context。 - rotti2
3
文章表示,只有当本地类型没有析构函数来进行自身清理时,才需要上下文。那么,在 std::string 的情况下,这是否需要呢? - Kristopher Johnson
2
std::string 不需要上下文。只有在从包装类型到未包装类型(即原始指针)进行编组时才需要上下文。正如概述C++中的编组中所列出的那样,只有三种情况需要上下文。 - Edward Brey
谢谢 @Mike Johnson,我差点就放弃C++了。现在又可以继续学习了... 嘿嘿 :D - gumuruh
1
我尝试使用它,但遇到了一个问题,因为我的System::String^是一个类变量,所以我不得不复制构造一个新的String^并将其传递给marshal_as。我讨厌Visual Studio。 - PfunnyGuy
问题不在于Visual Studio,而是.NET。 - Tom

7
C#使用UTF16格式来表示字符串。
因此,除了仅仅转换数据类型外,您还应该注意字符串的实际格式。
在编译 多字节字符集 时,Visual Studio和Win API假设使用UTF8(实际上是Windows编码,即Windows-28591)。 在编译 Unicode字符集 时,Visual Studio和Win API则假设使用UTF16。
因此,您必须将字符串从UTF16格式转换为UTF8格式,而不仅仅是转换为std :: string。
这将在处理一些非拉丁语言等多字符格式时变得必要。
思路是决定std :: wstring始终表示 UTF16
std :: string则始终表示 UTF8
这不是编译器强制执行的,而是一个很好的规则。
#include "stdafx.h"
#include <string>

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

或者使用更紧凑的语法:

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

据我所知,窄字符串 API 版本假定系统代码页,这可能是许多东西,但从不是 UTF-8。Win API 假定的是 Windows 编码,即 Windows-28591。 - heinrichj

6
stdString = toss(systemString);

  static std::string toss( System::String ^ s )
  {
    // convert .NET System::String to std::string
    const char* cstr = (const char*) (Marshal::StringToHGlobalAnsi(s)).ToPointer();
    std::string sstr = cstr;
    Marshal::FreeHGlobal(System::IntPtr((void*)cstr));
    return sstr;
  }

2
这是旧的语法。我更喜欢 Colin 上面建议的那个。 - orad
1
如果sstr()抛出异常会怎么样?这不会导致cstr成为一个泄漏吗? - user645280

4

我之前遇到了太多模糊的错误(是的,我是一个C++新手),以上回答对我来说都不适用。

这个方法对我在C#和C++ CLI之间传递字符串很有用。

C#

bool result;
result = mps.Import(mpsToolName);

C++ CLI

功能:

bool ManagedMPS::Import(System::String^ mpsToolNameTest)
std::string mpsToolName;
mpsToolName = toStandardString(mpsToolNameTest);

将String^转换为std::string的函数

static std::string toStandardString(System::String^ string)
{
 using System::Runtime::InteropServices::Marshal;
 System::IntPtr pointer = Marshal::StringToHGlobalAnsi(string);
 char* charPointer = reinterpret_cast<char*>(pointer.ToPointer());
 std::string returnString(charPointer, string->Length);
 Marshal::FreeHGlobal(pointer);
 return returnString;
}

进一步的研究表明,这种方法更加清洁和安全。我改用了这种方法。
std::string Utils::ToUnmanagedString(String^ stringIncoming)
{
   std::string unmanagedString = marshal_as<std::string>(stringIncoming);
   return unmanagedString;
}

1
那么,你是如何处理所有 IServiceProvider 不明确的错误的呢? - Arman Bimatov
我不记得遇到过那些错误。我当时刚接触C++,现在已经转到另一个公司的合同/项目了……抱歉,祝你好运。 - Tom Stickel

0

创建一个可供使用的 Windows 运行时组件:

String^ systemString = "Hello";
std::wstring ws1(systemString ->Data());
std::string standardString(ws1.begin(), ws1.end());

C++/CLI 字符串似乎没有 Data() 方法。 - demberto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接