C++/WinRT 中的字符串处理String handling in C++/WinRT

利用 C++/WinRT,你可以使用 C++ 标准库宽字符串类型(如 std::wstring )调用 Windows 运行时 API(注:不要使用窄字符串类型,例如 std::string )。With C++/WinRT, you can call Windows Runtime APIs using C++ Standard Library wide string types such as std::wstring (note: not with narrow string types such as std::string). C++/WinRT 确实有名为 winrt::hstring 的自定义字符串类型(在 C++/WinRT 基础库 %WindowsSdkDir%Include\<WindowsTargetPlatformVersion>\cppwinrt\winrt\base.h 中定义)。C++/WinRT does have a custom string type called winrt::hstring (defined in the C++/WinRT base library, which is %WindowsSdkDir%Include\<WindowsTargetPlatformVersion>\cppwinrt\winrt\base.h). 这是 Windows 运行时构造函数、函数和属性实际上采用并返回的字符串类型。And that's the string type that Windows Runtime constructors, functions, and properties actually take and return. 但在很多情况下(由于 hstring 的转换构造函数和转换运算符),你可以选择是否要注意客户端代码中的 hstring 。But in many cases—thanks to hstring's conversion constructors and conversion operators—you can choose whether or not to be aware of hstring in your client code. 如果你要创作 API,则很可能需要了解 hstring 。If you're authoring APIs, then you're more likely to need to know about hstring.

C++ 中有很多字符串类型。There are many string types in C++. 除了 C++ 标准库中的 std::basic_string 之外,变体还存在于很多库中。Variants exist in many libraries in addition to std::basic_string from the C++ Standard Library. C++17 具有字符串转换实用程序和 std::basic_string_view ,用来消除所有字符串类型之间的差别。C++17 has string conversion utilities, and std::basic_string_view, to bridge the gaps between all of the string types. winrt::hstring 利用 std::wstring_view 提供了可转换性,以实现 std::basic_string_view 应有的互操作性。winrt::hstring provides convertibility with std::wstring_view to provide the interoperability that std::basic_string_view was designed for.

将 std::wstring (也可以选择 winrt::hstring )与 Uri 结合使用Using std::wstring (and optionally winrt::hstring) with Uri

Windows::Foundation::Uri winrt::hstring 构建。Windows::Foundation::Uri is constructed from a winrt::hstring.

public:
    Uri(winrt::hstring uri) const;

但 hstring 具有可让你使用它而无需注意它的转换构造函数But hstring has conversion constructors that let you work with it without needing to be aware of it. 下面是一个代码示例,展示了如何从宽字符串参数、从宽字符串视图和从 std::wstring 创建 Uri 。Here's a code example showing how to make a Uri from a wide string literal, from a wide string view, and from a std::wstring.

#include <winrt/Windows.Foundation.h>
#include <string_view>

using namespace winrt;
using namespace Windows::Foundation;

int main()
{
    using namespace std::literals;

    winrt::init_apartment();

    // You can make a Uri from a wide string literal.
    Uri contosoUri{ L"http://www.contoso.com" };

    // Or from a wide string view.
    Uri contosoSVUri{ L"http://www.contoso.com"sv };

    // Or from a std::wstring.
    std::wstring wideString{ L"http://www.adventure-works.com" };
    Uri awUri{ wideString };
}

属性访问器 Uri::Domain 属于类型 hstring 。The property accessor Uri::Domain is of type hstring.

public:
    winrt::hstring Domain();

但重复一下,由于 hstring 的 std::wstring_view 的转换运算符,注意该细节是一个可选操作。But, again, being aware of that detail is optional thanks to hstring's conversion operator to std::wstring_view.

// Access a property of type hstring, via a conversion operator to a standard type.
std::wstring domainWstring{ contosoUri.Domain() }; // L"contoso.com"
domainWstring = awUri.Domain(); // L"adventure-works.com"

// Or, you can choose to keep the hstring unconverted.
hstring domainHstring{ contosoUri.Domain() }; // L"contoso.com"
domainHstring = awUri.Domain(); // L"adventure-works.com"

同样,IStringable::ToString 将返回 hstring。Similarly, IStringable::ToString returns hstring.

public:
    hstring ToString() const;

Uri 将实现 IStringable 接口。Uri implements the IStringable interface.

// Access hstring's IStringable::ToString, via a conversion operator to a standard type.
std::wstring tostringWstring{ contosoUri.ToString() }; // L"http://www.contoso.com/"
tostringWstring = awUri.ToString(); // L"http://www.adventure-works.com/"

// Or you can choose to keep the hstring unconverted.
hstring tostringHstring{ contosoUri.ToString() }; // L"http://www.contoso.com/"
tostringHstring = awUri.ToString(); // L"http://www.adventure-works.com/"

你可以使用 hstring::c_str function 函数从 hstring 获取标准宽字符串(正如你可以从 std::wstring 获取一样)。You can use the hstring::c_str function to get a standard wide string from an hstring (just as you can from a std::wstring).

#include <iostream>
std::wcout << tostringHstring.c_str() << std::endl;

如果你有 hstring ,则可以通过它创建 Uri 。If you have an hstring then you can make a Uri from it.

Uri awUriFromHstring{ tostringHstring };

考虑一个采用 hstring 的方法。Consider a method that takes an hstring.

public:
    Uri CombineUri(winrt::hstring relativeUri) const;

你刚刚看到的所有选项在这类情况下也适用。All of the options you've just seen also apply in such cases.

std::wstring contact{ L"contact" };
contosoUri = contosoUri.CombineUri(contact);
    
std::wcout << contosoUri.ToString().c_str() << std::endl;

hstring 具有成员 std::wstring_view 转换运算符,转换是免费实现的。hstring has a member std::wstring_view conversion operator, and the conversion is achieved at no cost.

void legacy_print(std::wstring_view view);

void Print(winrt::hstring const& hstring)
{
    legacy_print(hstring);
}

winrt::hstring 函数和运算符winrt::hstring functions and operators

winrt::hstring 实现了大量构造函数、运算符、函数和迭代程序。A host of constructors, operators, functions, and iterators are implemented for winrt::hstring.

hstring 是一个范围,因此你可以将其与基于范围的 for 或与 std::for_each 一起使用。An hstring is a range, so you can use it with range-based for, or with std::for_each. 它还提供了一个比较运算符,用于自然、高效地与它在 C++ 标准库中的对应项进行比较。It also provides comparison operators for naturally and efficiently comparing against its counterparts in the C++ Standard Library. 它还包含将 hstring 用作关联容器的键所需的一切。And it includes everything you need to use hstring as a key for associative containers.

我们发现很多 C++ 库使用了 std::string ,并且仅与 UTF-8 文本配合。We recognize that many C++ libraries use std::string, and work exclusively with UTF-8 text. 为方便起见,我们提供了 winrt::to_string winrt::to_hstring 等用于来回转换的帮助程序。As a convenience, we provide helpers, such as winrt::to_string and winrt::to_hstring, for converting back and forth.

WINRT_ASSERT 是宏定义,并且扩展到 _ASSERTEWINRT_ASSERT is a macro definition, and it expands to _ASSERTE.

winrt::hstring w{ L"Hello, World!" };

std::string c = winrt::to_string(w);
WINRT_ASSERT(c == "Hello, World!");

w = winrt::to_hstring(c);
WINRT_ASSERT(w == L"Hello, World!");

有关 hstring 函数和运算符的更多示例和信息,请参阅 winrt::hstring API 参考主题。For more examples and info about hstring functions and operators, see the winrt::hstring API reference topic.

winrt::hstring 和 winrt::param::hstring 的原理The rationale for winrt::hstring and winrt::param::hstring

Windows 运行时根据 wchar_t 字符实现,但 Windows 运行时的应用程序二进制接口 (ABI) 不是 std::wstring 或 std::wstring_view 提供的内容的一部分。The Windows Runtime is implemented in terms of wchar_t characters, but the Windows Runtime's Application Binary Interface (ABI) is not a subset of what either std::wstring or std::wstring_view provide. 使用这些将导致效率显著降低。Using those would lead to significant inefficiency. 相反,C++/WinRT 提供了 winrt::hstring ,它表示与基础 HSTRING 一致的不可变字符串,在与 std::wstring 的接口相似的接口后面实现。Instead, C++/WinRT provides winrt::hstring, which represents an immutable string consistent with the underlying HSTRING, and implemented behind an interface similar to that of std::wstring.

你可能会注意到在逻辑上应该接受 winrt::hstring 的 C++/WinRT 输入参数实际上需要 winrt::param::hstring 。You may notice that C++/WinRT input parameters that should logically accept winrt::hstring actually expect winrt::param::hstring. param 命名空间包含一组类型,专用于优化输入参数以自然地绑定到 C++ 标准库类型,以及避免副本和其他低效率现象。The param namespace contains a set of types used exclusively to optimize input parameters to naturally bind to C++ Standard Library types and avoid copies and other inefficiencies. 你不应直接使用这些类型。You shouldn't use these types directly. 如果你要对自己的函数使用优化,则应使用 std::wstring_view 。If you want to use an optimization for your own functions then use std::wstring_view. 另请参阅将参数传递到 ABI 边界Also see Passing parameters into the ABI boundary.

这样,你便可以在很大程度上忽略 Windows 运行时字符串管理的细节,并使用你了解的资源高效地工作。The upshot is that you can largely ignore the specifics of Windows Runtime string management, and just work with efficiency with what you know. 考虑到在 Windows 运行时中使用字符串的频率,这一点很重要。And that's important, given how heavily strings are used in the Windows Runtime.

格式化字符串Formatting strings

用于字符串格式化的一个选择是 std::wostringstreamOne option for string-formatting is std::wostringstream. 下面是格式化和显示简单调试跟踪消息的示例。Here's an example that formats and displays a simple debug trace message.

#include <sstream>
#include <winrt/Windows.UI.Input.h>
#include <winrt/Windows.UI.Xaml.Input.h>
...
void MainPage::OnPointerPressed(winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e)
{
    winrt::Windows::Foundation::Point const point{ e.GetCurrentPoint(nullptr).Position() };
    std::wostringstream wostringstream;
    wostringstream << L"Pointer pressed at (" << point.X << L"," << point.Y << L")" << std::endl;
    ::OutputDebugString(wostringstream.str().c_str());
}

设置属性的正确方式The correct way to set a property

可以将值传递给 setter 函数,以这种方式设置属性。You set a property by passing a value to a setter function. 下面是一个示例。Here's an example.

// The right way to set the Text property.
myTextBlock.Text(L"Hello!");

以下代码不正确。The code below is incorrect. 它可以编译,但只是修改 Text() 访问器函数返回的临时 winrt::hstring,然后就会将结果抛开。It compiles, but all it does is to modify the temporary winrt::hstring returned by the Text() accessor function, and then to throw the result away.

// *Not* the right way to set the Text property.
myTextBlock.Text() = L"Hello!";

重要的 APIImportant APIs