# 九、字符串

## 简介

字符串是 C 和 C++ 中最麻烦的事情之一。在早期的语言中，字符串都是字符数组，通常是 7 位 ASCII(尽管可能是 C 移植到的 IBM 大型机上的 EBCDIC)。然后出现了一大堆特定于操作系统的变通办法，比如代码页，以允许使用不在英语字母表中的字符的语言。经过一段时间的混乱，Unicode 出现了。然后是 Unicode。然后又是 Unicode。这里和那里还有一些 Unicodes，这是今天问题的根源。

Unicode 本质上是两件事。它是一系列定义好的代码点，其中有一个特定代码点到一个特定值的一对一映射，一些是图形的，另一些控制和操作格式或提供其他所需的信息。使用 Unicode 的每个人都同意所有这些，包括私有代码点，所有人都同意这些代码点是为符合 Unicode 的应用程序保留的。目前为止，一切顺利。

接下来是划分的编码方案。Unicode 中有 1，114，112 个代码点。你如何代表他们？答案是编码方案。UTF-16 是第一个。随后是 UTF-8 和 UTF-32。其中一些还存在字符顺序问题。

其他格式来来去去，其中一些甚至从来不是 Unicode 的一部分。

Windows 最终也像当初一样采用了 UTF-16。NET 和 Java。许多 GNU/Linux 和其他类似 UNIX 的系统都采用了 UTF-8。一些类似 UNIX 的系统使用 UTF-32。有些人可能会使用 UTF-16。网络在很大程度上使用 UTF-8，因为这种编码的有意设计主要是向后兼容 ASCII。只要你在一个系统上工作，一切都很好。当你试图变得跨平台时，事情会变得更加混乱。

## `char*`字符串

`char*`字符串(指向`char`数组的指针)最初是指 ASCII 字符串。现在它们*有时*指的是 ASCII，但更常见的是指 UTF-8。在 UNIX 世界中尤其如此。

在为 Windows 编程时，通常应该假设`char*`字符串是 ASCII 字符串或代码页字符串。代码页使用 7 位 ASCII 剩余的额外位来添加另外 128 个字符，因此创建了许多本地化文本，每个字符仍在一个字节内。

## `wchar_t*`字符串

`wchar_t*`字符串(指向`wchar_t`数组的指针，也称为*宽字符*)使用不同的、依赖于实现的字符集。在 Windows 上，这意味着 16 位值，用于 UTF-16。您应该始终使用`wchar_t`作为您的 Windows 原生字符类型，除非您必须支持非常非常旧的操作系统版本(即旧的 Windows 9X 系列)。

当您在代码中编写宽字符串常量时，您可以在双引号的前面加上一个 *L* 。例如:`const wchar_t* s = L"Hello World";`。如果你只需要一个字符，你可以再次使用 *L* ，但是要加上单引号:`wchar_t ch = L'A’;`。

## T0 和`std::wstring`弦

在`<string>`头文件中可以找到`std::string`和`std::wstring`类。如你所想，`std::string`对应`char*`，`std::wstring`对应`wchar_t*`。

这些类提供了一种存储可变长度字符串的方便方法，应该用于类成员变量，以代替它们相应的原始指针(`char*`和`wchar_t*`)。您应该只使用原始指针来传递字符串作为参数，然后只在字符串将按原样使用或本地复制到这些字符串类型之一时使用。

无论是哪种情况，函数都应该将字符串指针作为指向`const`(例如`const wchar_t* someStr`)的指针。毕竟，指针不会像`std::string`和`std::wstring`一样产生同样的建造和破坏费用。使用指向`const`的指针可以确保函数不会意外修改数据或试图释放指向的内存。

要获取其中一个的内容指向`const`的指针，请调用其`c_str`成员函数。注意，返回的指针指向`const`，因为数据不应该被修改，也不应该在指针上调用`delete`。内存仍然由底层的`std::string`或`std::wstring`实例拥有和管理。这也意味着，如果底层实例被破坏，`c_str`给你的指针就会失效，这就是为什么，如果你需要的字符串数据超出了它被传递到的函数的范围，你应该总是将字符串数据存储在这些类型中的一种，而不是直接存储指针。

要添加文本，使用`append`成员功能。

要查看字符串中是否出现特定的字符序列，请使用`find`成员函数或其更具体的变体之一，如`find_first_of`。如果序列不在字符串中，那么返回值将等于`std::npos`。否则，它将是序列的相关起点的索引。

要获取子字符串，请使用`substr`成员函数，向其传递从零开始的索引和要复制的元素数量(即`char`或`wchar_t`字符的数量)。它将返回一个`std::string`或一个`std::wstring`，不允许你通过传递一个不准确的计数或一个不正确的起始索引来溢出缓冲区。

还有其他有用的方法，所有这些都被记录为 [`basic_string`类](http://msdn.microsoft.com/en-us/library/syxtdd4f(VS.110).aspx)的一部分，这是一个模板类，`std::string`和`std::wstring`是其预定义的专门化。

## `std::wstringstream`字符串

`std::wstringstream`类(也有一个`std::stringstream`)类似于。NET `StringBuilder`类。它的使用方式与任何其他 C++ 标准库流非常相似。我发现这种类型对于在成员函数中构造字符串非常有用，该字符串将存储在 std::wstring 类成员中。

有关其用法的示例，请参见 ConstructorsSample\Toppings.h 文件中的`Toppings::GetString`成员函数。这里是它的代码，只是作为一个复习:

```cpp
                const wchar_t* GetString(void)
                {
                      if (m_toppings == None)
                      {
                            m_toppingsString = L"None";
                            return m_toppingsString.c_str();
                      }
                      bool addSpace = false;
                      std::wstringstream wstrstream;
                      if (m_toppings & HotFudge)
                      {
                            if (addSpace)
                            {
                                  wstrstream << L" ";
                            }
                            wstrstream << L"Hot Fudge";
                            addSpace = true;
                      }
                      if (m_toppings & RaspberrySyrup)
                      {
                            if (addSpace)
                            {
                                  wstrstream << L" ";
                            }
                            wstrstream << L"Raspberry Syrup";
                            addSpace = true;
                      }
                      if (m_toppings & CrushedWalnuts)
                      {
                            if (addSpace)
                            {
                                  wstrstream << L" ";
                            }
                            wstrstream << L"Crushed Walnuts";
                            addSpace = true;
                      }
                      if (m_toppings & WhippedCream)
                      {
                            if (addSpace)
                            {
                                  wstrstream << L" ";
                            }
                            wstrstream << L"Whipped Cream";
                            addSpace = true;
                      }
                      if (m_toppings & Cherry)
                      {
                            if (addSpace)
                            {
                                  wstrstream << L" ";
                            }
                            wstrstream << L"Cherry";
                            addSpace = true;
                      }
                      m_toppingsString = std::wstring(wstrstream.str());
                      return m_toppingsString.c_str();
                }

```