目录
1.2 字符串字面值及字符串容器类型basic_string
2.5 只读basic_string--std::basic_string_view
一、字符串本质
1.1 字符类型char及字符字面值
C++ 的内置类型与其在计算机的存储器中的表示方式紧密相关。计算机以位序列存储数据,每一位存储 0 或 1。一段内存可能存储着0001101101110001 ...,在位这一级上,存储器是没有结构和意义的。
char是c/c++标准基本内置类型,最小存储空间为8bits,数值表示0~255。字符、整数和布尔值的算术类型合称为整型。字符类型有两种:char 和 wchar_t。char 类型保证了有足够的空间,能够存储机器基本字符集中任何字符相应的数值,因此,char 类型通常是单个机器字节(byte)。wchar_t 类型用于扩展字符集,比如汉字和日语,这些字符集中的一些字符不能用单个 char 表示。对于字符来说,编译器会将char类型的数值通过字符映射表(ASCII 码表)转换为对于的可视字符。
ASCII 码表一共定义了 128 个字符,例字母 a 是 97 (0110 0001)。这 128 个字符只使用了 8 位二进制数中的后面 7 位,最前面的一位统一规定为 0。ASCII 码止共定义了128个字符,其中33个字符无法显示。0010 0000 ~ 0111 1110 (32-126)是可以显示的 ,基本都能使用键盘打出来, 具体参见对照表: ASCII编码对照表。ASCII 额外扩展的版本 EASCII,这里就可以使用一个完整子节的 8 个 bit 位表示共 256 个字符,EASCII版本前0-127个字符是和ASCII是一样的,不一样的是128-255这一部分,其中就又包括了一些衍生的拉丁字母。
像 42 这样的值,在程序中被当作字面值常量。称之为字面值是因为只能用它的值称呼它,称之为常量是因为它的值不能修改。每个字面值都有相应的类型,例如:0 是 int 型,3.14159 是 double 型。只有内置类型存在字面值,没有类类型的字面值。因此,也没有任何标准库类型的字面值。字符型字面值通常用一对单引号来定义:'a'、 '2'、 ',' 、' ' // blank。这些字面值都是 char 类型的。在字符字面值前加 L 就能够得到 wchar_t类型的宽字符字面值。如:L'a'。
有些字符是不可打印的。不可打印字符实际上是不可显示的字符,比如退格或者控制符。还有一些在语言中有特殊意义的字符,例如单引号、双引号和反斜线符号。不可打印字符和特殊字符都用转义字符书写。转义字符都以反斜线符号开始,C++ 语言中定义了如下转义字符:
换行符 \n
水平制表符 \t
纵向制表符 \v
退格符 \b
回车符 \r
进纸符 \f
报警(响铃)符 \a
反斜线 \\
疑问号 \?
单引号 \'
双引号 \"
......
可以将任何字符表示为以下形式的通用转义字符:\ooo。这 ooo 表示三个八进制数字,这三个数字表示字符的数字值。下面的例子是用 ASCII 码字符集表示字面值常量:
\7 (bell) \12 (newline) \40 (blank)
\0 (null) \062 ('2') \115 ('M')
字符’\0’通常表示“空字符(null character)”,它有着非常特殊的意义。同样也可以用十六进制转义字符来定义字符:\xddd。它由一个反斜线符、一个 x 和一个或者多个十六进制数字组成。
1.2 字符串字面值及字符串容器类型basic_string
字符串字面值是一串常量字符。字符串字面值常量用双引号括起来的零个或者多个字符表示。不可打印字符表示成相应的转义字符。
"Hello World!" "您好!" "①Ⅺ"
为了兼容 C 语言,C++ 中所有的字符串字面值都由编译器自动在末尾添加一个空字符。字符字面值'A' ,表示单个字符 A,而双引号的"A" 表示包含字母 A 和空字符两个字符的字符串。又如宽字符字面值,如L'a',也存在宽字符串字面值,一样在前面加“L”,如L"hi",宽字符串字面值是一串常量宽字符,同样以一个宽空字符结束。
std::string 类型支持长度可变的字符串,C++ 标准库将负责管理与存储字符相关的内存,以及提供各种有用的操作。标准库 string 类型的目的就是满足对字符串的一般应用。而std::string类型的本质是std::basic_string类模板的特化std::basic_string<char>。
标准std::basic_string类模板定义于头文件 <string>中:
//
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
> class basic_string;
// (C++17 起)
namespace pmr {
template <class CharT, class Traits = std::char_traits<CharT>>
using basic_string = std::basic_string< CharT, Traits,
std::polymorphic_allocator<CharT>>;
}
类模板 basic_string 存储并操纵作为非数组平凡标准布局类型的仿 char 对象序列。该类既不依赖字符类型,亦不依赖该类型上的原生操作。操作的定义通过 Traits 模板形参--std::char_traits 的特化或兼容特性类提供。 Traits::char_type 和 CharT 必须指名同一类型;否则程序非良构。
#include <iostream>
#include <cstring>
void basestr_test(void)
{
std::basic_string<char> str1{"hello"};
std::basic_string<char> str2(" ");
std::basic_string<char> str3 = "world";
std::basic_string<char> str4(2,'!');
std::basic_string<char> str5 = str1 + str2 + str3 + str4;
std::cout << "str5 = " << str5 << "\n";
}
basic_string 是相继存储的,即对于 basic_string str ,对任何 [0, str.size()) 中的 n 有 &*(str.begin() + n) == &*str.begin() + n ,或等价地,指向 s[0] 的指针能传递给期待指向空终止 (C++11 起) CharT[] 数组首元素指针的函数。
二、字符数组--basic_string
2.1 字符数组--basic_string
类模板 std::basic_string 存储并操纵作为非数组平凡标准布局类型的仿 char 对象序列。因此它和std::vector很类似:
//string
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
> class basic_string;
//vector
template<
class T,
class Allocator = std::allocator<T>
> class vector;
只是basic_string的操作通过 std::char_traits模板来提供。char_traits 类是一种特性类模板,对给定的字符类型抽象基础字符和字符串比较操作。有定义操作集是几乎始终可实现于其项的通用算法。从而可以将这些算法用于几乎任何可能的字符或字符串类型,只需提供自定义的 char_traits 类。
//定义于头文件 <string>
template<class CharT> class char_traits;
char_traits 类模板表现为显式实例化的基础。用户可以对任何自定义字符类型提供特化。标准字符类型上已定义了数种特化,例如std::char_traits<char>、std::char_traits<wchar_t>等。
2.2 字符操作--char_traits
标准库中std::char_traits类模板已经针对字符处理提供了特定化类模板。
std::char_traits<char>
std::char_traits<wchar_t>
std::char_traits<char16_t> //(C++11)
std::char_traits<char32_t> //(C++11)
std::char_traits<char8_t> //(C++20)
这些模板特化和std::char_traits类模板本身都提供了这字符处理的常规操作:
成员类型 定义
char_type CharT
int_type 能保有所有 char_type 值加 EOF 的整数类型
off_type 实现定义
pos_type 实现定义
state_type 实现定义
成员函数
assign [静态] 赋值一个字符(公开静态成员函数)
eqlt [静态] 比较二个字符(公开静态成员函数)
move [静态] 移动一个字符序列到另一个上(公开静态成员函数)
copy [静态] 复制一个字符序列(公开静态成员函数)
compare [静态] 以字典序比较二个字符序列(公开静态成员函数)
length [静态] 返回一个字符序列的长度(公开静态成员函数)
find [静态] 在字符序列中查找一个字符(公开静态成员函数)
to_char_type [静态] 转换 int_type 到等效的 char_type(公开静态成员函数)
to_int_type [静态] 转换 char_type 到等效的 int_type(公开静态成员函数)
eq_int_type [静态] 比较二个 int_type 值(公开静态成员函数)
eof [静态] 返回一个 eof 值 (公开静态成员函数)
not_eof [静态] 检查字符是否为 eof 值 (公开静态成员函数)
//char 特化的char_traits模板定义
template<> struct char_traits<char> {
using char_type = char;
using int_type = int;
using off_type = streamoff;
using pos_type = streampos;
using state_type = mbstate_t;
using comparison_category = strong_ordering;
static constexpr void assign(char_type& c1, const char_type& c2) noexcept;
static constexpr bool eq(char_type c1, char_type c2) noexcept;
static constexpr bool lt(char_type c1, char_type c2) noexcept;
static constexpr int compare(const char_type* s1, const char_type* s2, size_t n);
static constexpr size_t length(const char_type* s);
static constexpr const char_type* find(const char_type* s, size_t n,
const char_type& a);
static constexpr char_type* move(char_type* s1, const char_type* s2, size_t n);
static constexpr char_type* copy(char_type* s1, const char_type* s2, size_t n);
static constexpr char_type* assign(char_type* s, size_t n, char_type a);
static constexpr int_type not_eof(int_type c) noexcept;
static constexpr char_type to_char_type(int_type c) noexcept;
static constexpr int_type to_int_type(char_type c) noexcept;
static constexpr bool eq_int_type(int_type c1, int_type c2) noexcept;
static constexpr int_type eof() noexcept;
};
如果觉得std::char_traits提供的操作不能满足到字符串处理的需要,也可以通过继承该类模板,定义更多的操作功能,然后将该类模板作为basic_string类模板的Traits 模板形参传递进入,实现更多可能性。
#include <iostream>
#include <cstring>
#include <string>
//用户定义的字符特性可以用于提供无关大小写的比较
struct ci_char_traits : public std::char_traits<char> {
static char to_upper(char ch) {
return std::toupper((unsigned char) ch);
}
static bool eq(char c1, char c2) {
return to_upper(c1) == to_upper(c2);
}
static bool lt(char c1, char c2) {
return to_upper(c1) < to_upper(c2);
}
static int compare(const char* s1, const char* s2, std::size_t n) {
while ( n-- != 0 ) {
if ( to_upper(*s1) < to_upper(*s2) ) return -1;
if ( to_upper(*s1) > to_upper(*s2) ) return 1;
++s1; ++s2;
}
return 0;
}
static const char* find(const char* s, std::size_t n, char a) {
auto const ua (to_upper(a));
while ( n-- != 0 )
{
if (to_upper(*s) == ua)
return s;
s++;
}
return nullptr;
}
};
template<class DstTraits, class CharT, class SrcTraits>
constexpr std::basic_string_view<CharT, DstTraits>
traits_cast(const std::basic_string_view<CharT, SrcTraits> src) noexcept
{
return {src.data(), src.size()};
}
void char_traits_test(void)
{
using namespace std::literals;
constexpr auto s1 = "Hello"sv;
constexpr auto s2 = "heLLo"sv;
if (traits_cast<ci_char_traits>(s1) == traits_cast<ci_char_traits>(s2))
std::cout << s1 << " and " << s2 << " are equal\n";
}
2.3 basic_string构造函数
从各种数据源构造新 string ,可选地使用用户提供的分配器 alloc和
操作集。basic_string 满足知分配器容器 (AllocatorAwareContainer) (除了不用定制的 construct/destroy 构造/析构元素)、序列容器 (SequenceContainer) 及连续容器 (ContiguousContainer) (C++17 起)的要求。
std::basic_string<CharT,Traits,Allocator>::basic_string
{
public:
/*默认构造函数。构造空 string (拥有零大小和未指定的容量)。
*若不提供分配器,则从默认构造的实例获得分配器。*/
basic_string();
explicit basic_string( const Allocator& alloc );// (C++17 前)
basic_string() noexcept(noexcept( Allocator() ))
: basic_string( Allocator() ) {}
explicit basic_string( const Allocator& alloc ) noexcept; //(C++17 起)(C++20 前)
constexpr basic_string() noexcept(noexcept( Allocator() ))
: basic_string( Allocator() ) {}
explicit constexpr basic_string( const Allocator& alloc ) noexcept; //(C++20 起)
/*构造拥有字符 ch 的 count 个副本的 string 。若会推导出不足以作为分配器的 Allocator 类型,
*则此构造函数不用于类模板实参推导。 (C++17 起)*/
basic_string( size_type count,CharT ch,const Allocator& alloc = Allocator() );// (C++20 前)
constexpr basic_string( size_type count, CharT ch,
const Allocator& alloc = Allocator() );//(C++20 起)
basic_string( const basic_string& other, size_type pos,
size_type count = std::basic_string::npos,
const Allocator& alloc = Allocator() );//(C++17 前)
/*以 other 的子串 [pos, pos+count) 构造 string 。若 count == npos 或未指定 count ,
*或若请求的子串越过字符串的结尾,则产生的子串为 [pos, other.size()) */
basic_string( const basic_string& other, size_type pos,
const Allocator& alloc = Allocator() );//(C++17 起)(C++20 前)
constexpr basic_string( const basic_string& other, size_type pos,
const Allocator& alloc = Allocator() );//(C++20 起)
basic_string( const basic_string& other,size_type pos,size_type count,
const Allocator& alloc = Allocator() );//(C++17 起)(C++20 前)
constexpr basic_string( const basic_string& other,size_type pos,size_type count, const Allocator& alloc = Allocator() );//(C++20 起)
/*以 s 所指向的字符串的首 count 个字符构造 string 。 s 能包含空字符。
*string 的长度为 count 。若 [s, s + count) 不是合法范围则行为未定义。*/
basic_string( const CharT* s,size_type count,
const Allocator& alloc = Allocator() );//(C++20 前)
constexpr basic_string( const CharT* s,size_type count,
const Allocator& alloc = Allocator() );//(C++20 起)
/*以s所指向的空终止字符串的副本所初始化的内容构造string。以首个空字符确定字符串的长度。*/
basic_string( const CharT* s,const Allocator& alloc = Allocator() );//(C++20 前)
constexpr basic_string( const CharT* s,
const Allocator& alloc = Allocator() );// (C++20 起)
/*构造拥有范围 [first, last) 内容的 string */
template< class InputIt >
basic_string( InputIt first, InputIt last,
const Allocator& alloc = Allocator() );//(C++20 前)
template< class InputIt >
constexpr basic_string( InputIt first, InputIt last,
const Allocator& alloc = Allocator() );//(C++20 起)
/*复制构造函数。构造拥有 other 内容副本的 string 。*/
basic_string( const basic_string& other );//(C++20 前)
constexpr basic_string( const basic_string& other );//(C++20 起)
basic_string( const basic_string& other, const Allocator& alloc );//(C++11 起)(C++20 前)
constexpr basic_string( const basic_string& other, const Allocator& alloc );//(C++20 起)
/*移动构造函数。用移动语义构造拥有other内容的 string 。将other留在合法但未指定的状态。*/
basic_string( basic_string&& other ) noexcept;//(C++11 起)(C++20 前)
constexpr basic_string( basic_string&& other ) noexcept;//(C++20 起)
basic_string( basic_string&& other, const Allocator& alloc );//(C++11 起)(C++20 前)
constexpr basic_string( basic_string&& other, const Allocator& alloc );//(C++20 起)
/*构造拥有 initializer_list ilist 内容的 string 。*/
basic_string( std::initializer_list<CharT> ilist,
const Allocator& alloc = Allocator() );//(C++11 起)(C++20 前)
constexpr basic_string( std::initializer_list<CharT> ilist,
const Allocator& alloc = Allocator() );//(C++20 起)
/*如同用std::basic_string_view<CharT, Traits> sv = t; 隐式转换t为 string_view sv ,
*然后如同用basic_string(sv.data(), sv.size(), alloc) ,以sv的内容初始化 string 。*/
template < class T > explicit basic_string( const T& t,
const Allocator& alloc = Allocator() );//(C++17 起)(C++20 前)
template < class T > explicit constexpr basic_string( const T& t,
const Allocator& alloc = Allocator() );//(C++20 起)
/*如同用std::basic_string_view<CharT, Traits> sv = t; 隐式转换t为string_view sv ,
*然后如同用basic_string(sv.substr(pos, n), a) ,以sv的子范围[pos, pos + n)初始化string*/
template < class T >
basic_string( const T& t, size_type pos, size_type n,
const Allocator& alloc = Allocator() );//(C++17 起)(C++20 前)
template < class T >
constexpr basic_string( const T& t, size_type pos, size_type n,
const Allocator& alloc = Allocator() );//(C++20 起)
/*不能从 nullptr 构造 basic_string 。*/
constexpr basic_string( std::nullptr_t ) = delete;//(C++23 起)
}
/*
alloc - 用于此 string 所有内存分配的分配器
count - 产生的 string 大小
ch - 初始化 string 所用的值
pos - 要包含的首字符位置
first, last - 复制字符的来源范围
s - 指向用作源初始化 string 的字符数组的指针
other - 用作源初始化 string 的另一 string
ilist - 初始化 string 所用的 std::initializer_list
t - 初始化 string 所用的对象(可转换为 std::basic_string_view )
*/
下面例子展示std::basic_string从各个数据源构造新对象:
void basestr_construction_test(void)
{
std::allocator<char> alloc_char;
std::basic_string<char> str1;
std::basic_string<char> str2{};
std::basic_string<char> str3(alloc_char);
//
std::basic_string<char> str4(4,'a');
std::basic_string<char> str5{5,'b'};
std::cout << "str5 = " << str5 << "\n";
//
std::basic_string<char> str6(str4,0);
std::basic_string<char> str7{str5,0};
std::cout << "str7 = " << str7 << "\n";
//若构造的字符串长度会超出 max_size() ,若 count > max_size() )则抛出 std::length_error 。调用 Allocator::allocate 可能抛出。
std::basic_string<char> str8(str4,0,3);
std::basic_string<char> str9{str5,0,4};
std::cout << "str9 = " << str9 << "\n";
//
char cvec[] = "hello";
std::basic_string<char> str10(cvec,3);
std::basic_string<char> str11{cvec,4};
std::cout << "str11 = " << str11 << "\n";
//
std::basic_string<char> str12(cvec);
std::basic_string<char> str13{cvec};
std::cout << "str13 = " << str13 << "\n";
//
std::basic_string<char> str14(str10); //复制构造
std::basic_string<char> str15{str11};
std::cout << "str15 = " << str15 << "\n";
//
std::basic_string<char> const cstr("world");
std::basic_string<char> str16(cstr); //移动构造
std::basic_string<char> str17{cstr};
std::cout << "str17 = " << str17 << "\n";
//
std::basic_string<char> str18(std::begin(cvec),std::end(cvec)-1);
std::basic_string<char> str19{std::begin(cvec),std::end(cvec)-1};
std::cout << "str19 = " << str19 << "\n";
//basic_string(std::initializer_list<charT> ilist)
std::basic_string<char> str20({ 'b', 'a', 's', 'i', 'c', '_', 's', 't', 'r', 'i', 'n', 'g' });
std::cout << "str20 = " << str20 << "\n";
}
2.4 basic_string的功能操作
std::char_traits为basic_string提供了复制、移动、比较、赋值、查找等基本操作,basic_string利用这基本操作针对字符数据进行了组合运用,实现了元素访问、元素修改、容量计算、数据查找、数值转换等操作,同时也类似vector容器一样,提供了迭代器访问操作。总得来说可以把basic_string看做是做一个针对字符类型的顺序容器,类似于vector<char>的实现,只是相比vector,多了一些字符类型层面特有的功能而已。
//basic_string扼要及全面的成员
模板形参
CharT - 字符类型
Traits - 指定字符类型上操作的特性类
Allocator - 用于分配内部存储的分配器 (Allocator) 类型
成员类型 定义
traits_type Traits
value_type CharT
allocator_type Allocator
size_type Allocator::size_type (C++11 前)
,std::allocator_traits<Allocator>::size_type (C++11 起)
difference_type Allocator::difference_type (C++11 前)
,std::allocator_traits<Allocator>::difference_type (C++11 起)
reference value_type&
const_reference const value_type&
pointer Allocator::pointer (C++11 前)
,std::allocator_traits<Allocator>::pointer (C++11 起)
const_pointer Allocator::const_pointer (C++11 前)
,std::allocator_traits<Allocator>::const_pointer (C++11 起)
iterator 指向 value_type 的老式随机访问迭代器 (LegacyRandomAccessIterator) 及老式连续迭代器 (LegacyContiguousIterator)(C++20 前)
,指向 value_type 的老式随机访问迭代器 (LegacyRandomAccessIterator) 、contiguous_iterator 及常量表达式迭代器(ConstexprIterator) (C++20 起)
const_iterator 指向 const value_type 的老式随机访问迭代器 (LegacyRandomAccessIterator) 及老式连续迭代器 (LegacyContiguousIterator) (C++20 前)
,指向 const value_type 的老式随机访问迭代器 (LegacyRandomAccessIterator) 、contiguous_iterator 及常量表达式迭代器 (ConstexprIterator) (C++20 起)
reverse_iterator std::reverse_iterator<iterator>
const_reverse_iterator std::reverse_iterator<const_iterator>
基本成员函数
(构造函数) 构造 basic_string (公开成员函数)
(析构函数) 销毁字符串,若使用内部存储则解分配它 (公开成员函数)
operator= 为字符串赋值 (公开成员函数)
assign 赋值字符给字符串 (公开成员函数)
get_allocator 返回关联的分配器 (公开成员函数)
元素访问
at 访问指定字符,有边界检查 (公开成员函数)
operator[] 访问指定字符 (公开成员函数)
front (C++11)访问首字符 (公开成员函数)
back (C++11)访问最后的字符 (公开成员函数)
data 返回指向字符串首字符的指针 (公开成员函数)
c_str 返回字符串的不可修改的 C 字符数组版本 (公开成员函数)
operator basic_string_view (C++17)返回到整个字符串的不可修改的 basic_string_view (公开成员函数)
迭代器
begin cbegin (C++11) 返回指向起始的迭代器(公开成员函数)
end cend (C++11) 返回指向末尾的迭代器(公开成员函数)
rbegin crbegin (C++11) 返回指向起始的逆向迭代器(公开成员函数)
rend crend (C++11) 返回指向末尾的逆向迭代器(公开成员函数)
容量
empty 检查字符串是否为空 (公开成员函数)
size 返回字符数 (公开成员函数)
length 返回字符数 (公开成员函数)
max_size 返回字符数的最大值 (公开成员函数)
reserve 保留存储 (公开成员函数)
capacity 返回当前对象分配的存储空间能保存的字符数量 (公开成员函数)
shrink_to_fit (C++11) 通过释放不使用内存减少内存使用(公开成员函数)
操作
clear 清除内容 (公开成员函数)
insert 插入字符 (公开成员函数)
erase 移除字符 (公开成员函数)
push_back 后附字符到结尾 (公开成员函数)
pop_back (C++11) 移除末尾字符 (公开成员函数)
append 后附字符到结尾 (公开成员函数)
operator+= 后附字符到结尾 (公开成员函数)
compare 比较二个字符串 (公开成员函数)
starts_with (C++20) 检查 string 是否始于给定前缀(公开成员函数)
ends_with (C++20) 检查 string 是否终于给定后缀(公开成员函数)
contains (C++23) 检查字符串是否含有给定的子串或字符(公开成员函数)
replace 替换字符串的指定部分 (公开成员函数)
substr 返回子串 (公开成员函数)
copy 复制字符 (公开成员函数)
resize 更改存储的字符数 (公开成员函数)
resize_and_overwrite (C++23) 更改存储的字符数并可能经由用户提供的操作重写不确定的内容(公开成员函数)
swap 交换内容(公开成员函数)
查找
find 于字符串中寻找字符 (公开成员函数)
rfind 寻找子串的最后一次出现 (公开成员函数)
find_first_of 寻找字符的首次出现 (公开成员函数)
find_first_not_of 寻找字符的首次缺失 (公开成员函数)
find_last_of 寻找字符的最后一次出现(公开成员函数)
find_last_not_of 寻找字符的最后一次缺失(公开成员函数)
常量
npos [静态] 特殊值。准确含义依赖语境(公开静态成员常量)
非成员函数
operator+ 连接两个字符串或者一个字符串和一个字符(函数模板)
operator== 以字典序比较两个字符串(函数模板)
operator!= (C++20 中移除)
operator< (C++20 中移除)
operator> (C++20 中移除)
operator<= (C++20 中移除)
operator>= (C++20 中移除)
operator<=> 三路比较运算(函数模板)
std::swap(std::basic_string) 特化 std::swap 算法 (函数模板)
erase(std::basic_string) (C++20)擦除所有满足特定判别标准的元素(函数模板)
erase_if(std::basic_string)
输入/输出
operator<< 执行字符串的流输出(函数模板)
operator>> 执行字符串的流输入(函数模板)
getline 从 I/O 流读取数据到字符串(函数模板)
数值转换
stoi (C++11)转换字符串为int整数(函数)
stol (C++11)转换字符串为long整数(函数)
stoll (C++11)转换字符串为long long整数(函数)
stoul (C++11)转换字符串为unsigned long整数(函数)
stoull (C++11)转换字符串为unsigned long long整数(函数)
stof (C++11)转换字符串为浮点值float(函数)
stod (C++11)转换字符串为浮点值double(函数)
stold (C++11)转换字符串为浮点值long double(函数)
to_string (C++11)转换整数或浮点值为 string(函数)
to_wstring (C++11)转换整数或浮点值为 wstring(函数)
字面量,定义于内联命名空间 std::literals::string_literals
operator""s (C++14)转换字符数组字面量为 basic_string(函数)
辅助类
//string 的散列支持(类模板特化)
std::hash<std::string> (C++11)
std::hash<std::u8string> (C++20)
std::hash<std::u16string> (C++11)
std::hash<std::u32string> (C++11)
std::hash<std::wstring> (C++11)
std::hash<std::pmr::string> (C++17)
std::hash<std::pmr::u8string> (C++20)
std::hash<std::pmr::u16string> (C++17)
std::hash<std::pmr::u32string> (C++17)
std::hash<std::pmr::wstring> (C++17)
关于std::basic_string具体声明见附录最后页面。
std::srting本质上就是std::basic_string类模板的特化std::basic_string<char>,通过定义std::basic_string<char>一样实现std::string的功能("具体见2.4 basic_string的功能操作")。
void basestr_func_test(void)
{
std::basic_string<char> str1{"hello"};
std::basic_string<char> str2(" ");
std::basic_string<char> str3 = "world!";
std::basic_string<char> str = str1 + str2 + str3;//+ , =
std::cout << "str = " << str << "\n";
//find
std::basic_string<char>::size_type pos = str.find('o');
std::cout << "pos = " << pos << "\n";
std::basic_string<char>::size_type pos_s = str.find("o");
std::cout << "pos_s = " << pos_s << "\n";
std::basic_string<char>::size_type rpos = str.rfind('o');
std::cout << "rpos = " << rpos << "\n";
std::basic_string<char>::size_type rpos_s = str.rfind("o");
std::cout << "rpos_s = " << rpos_s << "\n";
//find of
std::basic_string<char>::size_type fpos = str.find_first_of("l");
std::cout << "fpos = " << fpos << "\n";
std::basic_string<char>::size_type fpos_n = str.find_first_not_of("l");
std::cout << "fpos_n = " << fpos_n << "\n";
std::basic_string<char>::size_type lpos = str.find_last_of("l");
std::cout << "lpos = " << lpos << "\n";
std::basic_string<char>::size_type lpos_n = str.find_last_not_of("l");
std::cout << "lpos_n = " << lpos_n << "\n";
//访问元素
std::cout << "str.at(2) = " << str.at(2) << "\n";
std::cout << "str[2] = " << str[2] << "\n";
std::cout << "str.front() = " << str.front() << "\n";
std::cout << "str.back() = " << str.back() << "\n";
std::cout << "str.c_str() = " << std::string(str.c_str()) << "\n";
//
std::basic_string<char>::iterator iter = str.begin();
std::cout << "str for iterator = " ;
for(; iter!=str.end(); ++iter){
std::cout << *iter;
}
std::cout << "\n";
std::basic_string<char>::reverse_iterator riter = str.rbegin();
std::cout << "str for reverse_iterator = " ;
for(; riter!=str.rend(); ++riter){
std::cout << *riter;
}
std::cout << "\n";
//size
std::cout << "str.empty() = " << str.empty() << "\n";
std::cout << "str.size() = " << str.size() << "\n";
std::cout << "str.length() = " << str.length() << "\n";
std::cout << "str.max_size() = " << str.max_size() << "\n";
std::cout << "str.capacity() = " << str.capacity() << "\n";
str.reserve(std::size_t(10));
std::cout << "str.size() = " << str.size() << "\n";
//
std::basic_string<char> int_str{"1000"};
std::cout << "std::stoi(int_str) = " << std::stoi(int_str) << "\n";
std::cout << "std::stol(int_str) = " << std::stol(int_str) << "\n";
std::cout << "std::stoll(int_str) = " << std::stoll(int_str) << "\n";
std::cout << "std::stoul(int_str) = " << std::stoul(int_str) << "\n";
std::cout << "std::stoull(int_str) = " << std::stoull(int_str) << "\n";
std::basic_string<char> double_str{"345.678"};
std::cout << "std::stof(double_str) = " << std::stof(double_str) << "\n";
std::cout << "std::stod(double_str) = " << std::stod(double_str) << "\n";
std::cout << "std::stold(double_str) = " << std::stold(double_str) << "\n";
double dval = 12345.6789;
std::cout << "std::to_string(dval) = " << std::to_string(dval) << "\n";
//
str.insert(2,"g");
std::cout << "str = " << str << "\n";
str.push_back('a');
std::cout << "str = " << str << "\n";
str.append(2,'b');
std::cout << "str = " << str << "\n";
str.append("cd");
std::cout << "str = " << str << "\n";
//......
}
//out log
str = hello world!
pos = 4
pos_s = 4
rpos = 7
rpos_s = 7
fpos = 2
fpos_n = 0
lpos = 9
lpos_n = 11
str.at(2) = l
str[2] = l
str.front() = h
str.back() = !
str.c_str() = hello world!
str for iterator = hello world!
str for reverse_iterator = !dlrow olleh
str.empty() = 0
str.size() = 12
str.length() = 12
str.max_size() = 4611686018427387903
str.capacity() = 15
str.size() = 12
std::stoi(int_str) = 1000
std::stol(int_str) = 1000
std::stoll(int_str) = 1000
std::stoul(int_str) = 1000
std::stoull(int_str) = 1000
std::stof(double_str) = 345.678
std::stod(double_str) = 345.678
std::stold(double_str) = 345.678
std::to_string(dval) = 12345.678900
str = hegllo world!
str = hegllo world!a
str = hegllo world!abb
str = hegllo world!abbcd
2.5 只读basic_string--std::basic_string_view
在C++17版本后,标准加入了只读的std::basic_string,即std::basic_string_view类模板,该模板是std::basic_string的只读版本,提供了和std::basic_string一致的功能操作,除了可修改部分的功能不去掉(插入、删除、清空等)。
//定义于头文件 <string_view>,(C++17 起)
template< class CharT, class Traits = std::char_traits<CharT> >
class basic_string_view;
/*
*CharT - 字符类型
*Traits - 指定字符类型上操作的字符特征 (CharTraits) 类。
*同 basic_string , Traits::char_type 必须指名同 CharT 的类型,否则程序为谬构。
*/
类模板 basic_string_view 描述一个能指代常量连续仿 char 对象序列的对象,序列首元素在零位置。典型的实现仅保有二个成员:指向常 CharT
的指针和大小。std::basic_string_view 的每个特化均为可平凡复制 (TriviallyCopyable) 类型。 (C++23 起)
类模板 basic_string_view提供数种特化类,针对常用字符类型的 typedef :
// basic_string_view typedef 名
using string_view = basic_string_view<char>;
using u8string_view = basic_string_view<char8_t>;
using u16string_view = basic_string_view<char16_t>;
using u32string_view = basic_string_view<char32_t>;
using wstring_view = basic_string_view<wchar_t>;
类模板 basic_string_view 提供了类似basic_string类模板的同样功能操作,除了可修改部分(写部分):
成员类型 定义
traits_type Traits
value_type CharT
pointer CharT*
const_pointer const CharT*
reference CharT&
const_reference const CharT&
const_iterator 实现定义的常老式随机访问迭代器 (LegacyRandomAccessIterator) 、常量表达式迭代器 (ConstexprIterator) (C++20 起)兼老式连续迭代器 (LegacyContiguousIterator) ,其 value_type 为 CharT
iterator const_iterator
const_reverse_iterator std::reverse_iterator<const_iterator>
reverse_iterator const_reverse_iterator
size_type std::size_t
difference_type std::ptrdiff_t
//注解: iterator 与 const_iterator 是同一类型,因为 string_view 是到常字符序列中的视图。
//容器 (Container) 的迭代器类型上的所有要求亦应用于 basic_string_view 的 iterator 和 const_iterator 类型。
成员函数
(构造函数) 构造 basic_string_view (公开成员函数)
operator= 对视图赋值 (公开成员函数)
迭代器
begin cbegin 返回指向起始位置的迭代器(公开成员函数)
end cend 返回指向结尾的迭代器(公开成员函数)
rbegin crbegin 返回指向起始的反向迭代器(公开成员函数)
rend crend 返回指向结尾的反向迭代器(公开成员函数)
元素访问
operator[] 访问指定字符(公开成员函数)
at 访问指定字符,带有边界检查(公开成员函数)
front 访问首个字符(公开成员函数)
back 访问最末字符(公开成员函数)
data 返回指向视图首字符的指针(公开成员函数)
容量
size length 返回字符数(公开成员函数)
max_size 返回最大字符数(公开成员函数)
empty 检查视图是否为空(公开成员函数)
修改器
remove_prefix 以后移起点收缩视图(公开成员函数)
remove_suffix 以前移终点收缩视图(公开成员函数)
swap 交换内容(公开成员函数)
操作
copy 复制字符(公开成员函数)
substr 返回子串(公开成员函数)
compare 比较二个视图(公开成员函数)
starts_with (C++20) 检查 string_view 是否始于给定前缀(公开成员函数)
ends_with (C++20) 检查 string_view 是否终于给定后缀(公开成员函数)
contains (C++23) 检查字符串视图是否含有给定的子串或字符(公开成员函数)
find 在视图中查找字符(公开成员函数)
rfind 寻找子串的最后一次出现(公开成员函数)
find_first_of 查找字符的首次出现 (公开成员函数)
find_last_of 查找字符的最后一次出现(公开成员函数)
find_first_not_of 查找字符的首次不出现(公开成员函数)
find_last_not_of 查找字符的最后一次不出现(公开成员函数)
常量
npos [静态]特殊值。准确含义依赖于语境。(公开静态成员常量)
非成员函数
operator== 以字典序比较两个字符串视图(函数模板)
operator!= (C++20 中移除)
operator< (C++20 中移除)
operator> (C++20 中移除)
operator<= (C++20 中移除)
operator>= (C++20 中移除)
operator<=> (函数模板)
输入/输出
operator<< 进行字符串视图的流输出(函数模板)
字面量,定义于内联命名空间 std::literals::string_view_literals
operator""sv (C++17) 创建一个字符数组字面量的字符串视图(函数)
辅助类
//string_view 的散列支持(类模板特化)
std::hash<std::string_view> (C++17)
std::hash<std::wstring_view> (C++17)
std::hash<std::u8string_view> (C++20)
std::hash<std::u16string_view> (C++17)
std::hash<std::u32string_view> (C++17)
在新C++17版本以后,如果遇到只读字符串的业务实现,建议采用std::string_view(std::basic_string_view<char>)来替换 std::string( std::basic_string<char>)以区分其的只读特性。
#include <string_view>
#include <array>
void string_view_test(void)
{
//
std::basic_string_view<wchar_t> wcstr_v = L"xyzzy";
std::cout << std::wcslen(wcstr_v.data()) << '\n';
// OK :底层字符数组为空终止
char array[3] = {'B', 'a', 'r'};
std::basic_string_view<char> array_v(array, sizeof array);
// std::cout << std::strlen(array_v.data()) << '\n';
// 错误:底层字符数组非空终止
//
std::basic_string<char> str(array_v.data(), array_v.size()); // OK
std::cout << std::strlen(str.data()) << '\n';
// OK : std::string 的底层字符数组始终为空终止
std::basic_string<char> cppstr = "Foo";
std::basic_string_view<char> cppstr_v(cppstr);
std::array ar = {'P', 'u', 'b'};
std::basic_string_view<char> ar_v(ar.begin(), ar.end()); // C++20
std::cout << cppstr_v << ' '
<< array_v << ' '
<< ar_v << ' '
<< wcstr_v.size() << '\n';
//
std::ostream_iterator<char> out_it(std::cout);
std::basic_string_view<char> str_view("abcdef");
std::copy(str_view.rbegin(), std::next(str_view.rbegin(), 3), out_it);
*out_it = '\n';
std::copy(str_view.crbegin(), std::next(str_view.crbegin(), 3), out_it);
*out_it = '\n';
//
auto begin = str_view.begin();
auto cbegin = str_view.cbegin();
std::cout << *begin << '\n';
std::cout << *cbegin << '\n';
std::cout << std::boolalpha << (begin == cbegin) << '\n';
std::cout << std::boolalpha << (*begin == *cbegin) << '\n';
//
for (std::size_t i = 0; i<str_view.length(); ++i)
std::cout << i << ": " << str_view.at(i) << '\n';
//
using namespace std::literals;
//c++23,contains
std::cout
<< std::boolalpha
// bool contains(basic_string_view x) const noexcept;
<< "https://cppreference.com"sv.contains("cpp"sv) << ' ' // true
<< "https://cppreference.com"sv.contains("java"sv) << ' ' // false
// bool contains(CharT x) const noexcept;
<< "C++23"sv.contains('+') << ' ' // true
<< "C++23"sv.contains('-') << ' ' // false
// bool contains(const CharT* x) const;
<< std::basic_string_view<char>("basic_string_view").contains("string") << ' ' // true
<< std::basic_string_view<char>("basic_string_view").contains("String") << ' ' // false
<< '\n';
}
三、字符串string
3.1 std::string
C++兼容C对字符串的处理方式,与此同时还在标准库(STL)中提供了string容器,而字符容器通过使用char和wchar_t对basic_string进行特化可以分别得到string和wstring。我们很容易的使用string来进行字符串处理,本质上就是使用了std::basic_string<char>。
//string头文件, 定义特化类型 typedef 名 basic_string<T>
namespace std {
using string = basic_string<char>;
using u8string = basic_string<char8_t>;
using u16string = basic_string<char16_t>;
using u32string = basic_string<char32_t>;
using wstring = basic_string<wchar_t>;
};
因此前面关于std::basic_string<char>的操作使用案例,采用std::string替换完全没有区别。
void string_func_test(void)
{
std::string str1{"hello"};
std::string str2(" ");
std::string str3 = "world!";
std::string str = str1 + str2 + str3;//+ , =
std::cout << "str = " << str << "\n";
//other重复...
}
string还能很好的与标准库中的泛型算法结合起来使用,非常的方便。使用标准库提供的string可以轻松的与原来的C API兼容,也可以很好的与系统底层的API兼容。比如说string所提供的c_str() const 成员函数将返回内部的const指针。提供了得到长度的两个成员函数size() const和length() const,实际上,这两个接口的接口是相同的。声明一个length完全是为了照顾以前C程序员的编程习惯。
std::string(即std::basic_string<char>)的每个成员函数都提过多个重载功能函数,编译开发者调用,尤其对于类构造、复制、赋值等基础操作,动辄上十种重载操作,满足不同应用场景需求。
3.2 std::wstring
wstring和string的方法差不多,只不过内部使用的是wchar_t。这可以让我们更好的在UNICODE环境下使用STL提供的字符串。
void wstring_func_test(void)
{
std::string str1{"hello"};
std::cout << "str1 = " << str1 << "\n";
std::wstring str2{L"hello"};
std::wcout << "str2 = " << str2 << "\n";
}
对于wstring对象,如果要使用STL输出到文件的话,需要使用wofstream;如果要输出到控制台需要wcout。使用ofstream和cout是无法做到的。
#include <string>
#include <fstream>
#include <vector>
#include <iterator>
#include <locale>
void wstring_func_test(void)
{
//
std::wcout.imbue(std::locale());
//
std::wstring wss( L"hi" );
std::wcout << "wss = "<< wss << "!\n";
//
std::wofstream wofs( L"output.txt" );
if( wofs.fail() )
{
std::wcout<<"error"<< "\n";
return ;
}
wofs.imbue( std::locale() );
wofs.write( wss.c_str(), wss.size() );
typedef std::vector< std::wstring > WSVEC;
WSVEC wsv;
wsv.push_back( wss );
wsv.push_back( L"hello!" );
std::copy( wsv.begin(),wsv.end()
, std::ostream_iterator< std::wstring,std::wstring::value_type >( std::wcout,L"" ) );
std::copy( wsv.begin(),wsv.end()
, std::ostream_iterator< std::wstring,std::wstring::value_type >( wofs,L"" ) );
wofs.close();
}
3.3 空终止字节字符串
空终止字节字符串与很多字符相关的操作有密切关系,例如字符数值转换、字符拷贝、字符校验、字符比较、字符移动等等。空终止字节字符串( null-terminated byte string, NTBS )是后随拥有零值的字节(空终止字符)的非零字节序列。字节字符串的每个字节编码某字符集的一个字符。例如,字符数组 {'\x61', '\x62', '\x63', '\0'} 是持有以 ASCII 编码的字符串 "abc" 的 NTBS 。
标准库提供了一系列字符校验检测的函数集:
//字符分类函数,定义于头文件 <cctype>
isalnum 检查字符是否为字母或数字
isalpha 检查字符是否为字母
islower 检查字符是否为小写
isupper 检查字符是否为大写字符
isdigit 检查字符是否为数字
isxdigit 检查字符是为十六进制字符
iscntrl 检查字符是否为控制字符
isgraph 检查字符是否为图形字符
isspace 检查字符是否为空白间隔字符
isblank (C++11) 检查字符是否为空白字符
isprint 检查字符是否为打印字符
ispunct 检查字符是否为标点符
//字符操作函数
tolower 转换字符为小写
toupper 转换字符为大写
//转换为数值格式,定义于头文件 <cstdlib>
atof 转换字节字符串为浮点值
atoi atol atoll (C++11)转换字节字符串为整数值
strtol strtoll (C++11)转换字节字符串为整数值
strtoul strtoull (C++11)转换字节字符串为无符号整数值
strtof strtod strtold 转换字节字符串为浮点值
//定义于头文件 <cinttypes>
strtoimax strtoumax (C++11)转换字节字符串为 std::intmax_t 或 std::uintmax_t
//字符串操作,定义于头文件 <cstring>
strcpy 复制一个字符串给另一个
strncpy 复制来自一个字符串的一定量字符给另一个
strcat 连接两个字符串
strncat 连接两个字符串的一定量字符
strxfrm 变换字符串,使得 strcmp 会返回与 strcoll 相同的结果
//字符串检验,定义于头文件 <cstring>
strlen 返回给定字符串的长度
strcmp 比较两个字符串
strncmp 比较两个字符串的一定量字符
strcoll 按照当前本地环境比较两个字符串
strchr 寻找字符的首次出现
strrchr 寻找字符的最后出现
strspn 返回仅由另一字节字符串中找到的字符组成的最大起始段的长度
strcspn 返回仅由另一字节字符串中找不到的字符组成的最大起始段的长度
strpbrk 寻找任何来自分隔符集合的字符的首个位置
strstr 寻找字符子串的首次出现
strtok 寻找字节字符串中的下个记号
//字符数组操作,定义于头文件 <cstring>
memchr 在数组中搜索字符的首次出现
memcmp 比较两个缓冲区
memset 以一个字符填充缓冲区
memcpy 复制一个缓冲区到另一个
memmove 移动一个缓冲区到另一个
尽量按函数操作组的方式记住标准为空终止字节字符串提供的函数功能集,在业务实现时可以快速串联起来,然后在标准库查找到具体函数用法,在项目中快速运用起来。
void NTBS_str_test(void)
{
unsigned char c = '\xe5'; // ISO-8859-1 中的字母 å
std::setlocale(LC_ALL, "en_US.utf8");
std::cout << "islower(\'\\xe5\', default C locale) returned "
<< std::boolalpha << (bool)std::islower(c) << '\n';
std::setlocale(LC_ALL, "en_GB.iso88591");
std::cout << "islower(\'\\xe5\', ISO-8859-1 locale) returned "
<< std::boolalpha << (bool)std::islower(c) << '\n';
c = '\xc6'; // ISO-8859-1 中的字母 Æ
std::setlocale(LC_ALL, "en_US.utf8");
std::cout << "isupper(\'\\xc6\', default C locale) returned "
<< std::boolalpha << (bool)std::isupper(c) << '\n';
std::setlocale(LC_ALL, "en_GB.iso88591");
std::cout << "isupper(\'\\xc6\', ISO-8859-1 locale) returned "
<< std::boolalpha << (bool)std::isupper(c) << '\n';
}
//out log
islower('\xe5', default C locale) returned false
islower('\xe5', ISO-8859-1 locale) returned false
isupper('\xc6', default C locale) returned false
isupper('\xc6', ISO-8859-1 locale) returned false
3.4 空终止宽字符串
空终止宽字符串是以空字符结束的合法宽字符序列,标准库为其提供的功能函数与空终止字节字符串几乎是类似的。
//字符分类,定义于头文件 <cwctype>
iswalnum 检查宽字符是否为字母数字
iswalpha 检查宽字符是否为字母
......
//字符操作,定义于头文件 <cwctype>
towlower 转换宽字符为小写
towupper 转换宽字符为大写
......
3.5 空终止多字节字符串
空终止多字节字符串( null-terminated multibyte string, NTMBS ),或“多字节字符串”,是后随拥有零值的字节(空终止字符)的非零字节序列。存储于该字符串中的每个字符可能占用多于一个字节。用于表示字符的编码是本地环境限定的:它可以是 UTF-8 、 GB18030 、 EUC-JP 、 Shift-JIS 等。例如,字符数组 {'\xe4','\xbd','\xa0','\xe5','\xa5','\xbd','\0'} 是一个以UTF8编码包含 "你好" 的 NTMBS :首三个字节编码字符 "你" ,接下来三个字节编码字符 "好" 。编码于 GB18030 的相同字符串是字符数组 {'\xc4', '\xe3', '\xba', '\xc3', '\0'} ,其中二个字符各编码为双字节序列。
void empty_str_test(void)
{
std::setlocale(LC_ALL, "en_US.utf8");
char evec1[] = {'\xe4','\xbd','\xa0','\xe5','\xa5','\xbd','\0'} ;
std::cout << "evec2[] = "<< std::string(evec1) << '\n';
std::setlocale(LC_ALL, "chs");
char evec2[] = {'\xc4', '\xe3', '\xba', '\xc3', '\0'} ;
std::cout << "evec2[] = "<< std::string(evec2) << '\n';
}
一些多字节编码中,任何给定的多字节字符序列可以根据之前的字节序列,称之为“迁移序列”,表示不同的字符。这种编码被称为状态依赖的:要求知晓当前迁移状态以转译每个字符。 NTMBS 仅若以初始迁移状态开始及结束才合法:若使用迁移序列,则对应的无迁移序列必须在空终止字符前存在。这种编码的例子是 7-bit JIS 、 BOCU-1 及 SCSU 。
多字节字符串与空终止字节字符串( NTBS )布局兼容,即能用相同的设施存储、复制并检验,除了计算字符数。若当前本地环境生效,则 I/O 函数亦处理多字节字符串。多字节字符串可用 std::codecvt 成员函数、 std::wstring_convert :
//定义于头文件 <locale>
//类模板 std::codecvt 封装字符串的转换,包括宽和多字节,从一种编码到另一种。
template<class InternT,class ExternT,class State> class codecvt;
//c++11起,c++17弃用。类模板 std::wstring_convert 用单独的编码转换平面 Codecvt
//,进行字节字符串 std::string 和宽字符串 std::basic_string<Elem> 间的转换。
template< class Codecvt,class Elem = wchar_t,
class Wide_alloc = std::allocator<Elem>,class Byte_alloc = std::allocator<char> >
class wstring_convert;
或下列依赖本地环境的转换函数与宽字符串相互转换:
//多字节/宽字符串转换函数
//【1】定义于头文件 <cstdlib>
mblen 返回下一个多字节字符中的字节数
mbtowc 将下一个多字节字符转换成宽字符
wctomb 转换宽字符为其多字节表示
mbstowcs 转换窄多字节字符串为宽字符串
wcstombs 转换宽字符串为窄多字节字符串
//【2】定义于头文件 <cwchar>
mbsinit 检查 mbstate_t 对象是否表示初始迁移状态
btowc 若可能,则加宽单字节窄字符为宽字符
wctob 若可能,则窄化宽字符为单字节窄字符
mbrlen 给定状态,返回下一个多字节字符中的字节数
mbrtowc 给定状态,转换下个多字节字符为宽字符
wcrtomb 给定状态,转换宽字符到其多字节表示
mbsrtowcs 给定状态,转换窄多字节字符串到宽字符串
wcsrtombs 给定状态,转换宽字符串为窄多字节字符串
//【3】定义于头文件 <cuchar>
mbrtoc8 (C++20) 转换窄多字节字符为 UTF-8 编码
c8rtomb (C++20) 转换 UTF-8 字符串为窄多字节编码
mbrtoc16 (C++11) 转换窄多字节字符为 UTF-16 编码
c16rtomb (C++11) 转换 16 位宽字符为窄多字节字符串
mbrtoc32 (C++11) 转换窄多字节字符为 UTF-32 编码
c32rtomb (C++11) 转换 32 位宽字符为窄多字节字符串
在针对一些特殊字符处理时,多字节字符串的各种转换函数可以快捷实现需要的业务功能。
void ctype_char_test(void)
{
// 允许 mblen() 以 UTF-8 多字节编码工作
std::setlocale(LC_ALL, "en_US.utf8");
const std::string_view s{"z\u00df\u6c34\U0001f34c"};//u8"zß水🍌"
/*int mblen( const char* s, std::size_t n );
*s - 指向多字节字符的指针
*n - s中能被检验的字节数限制
*若 s 不是空指针,则返回多字节字符所含的字节数,或若 s 所指的首字节不组成合法多字节字符则返回 -1 ,或若 s 指向空字符 '\0' 则返回 0 。
*若 s 是空指针,则重置内部转换状态为初始迁移状态,并若当前多字节编码非状态依赖(不使用迁移序列)则返回 0 ,或者若当前多字节编码为状态依赖(使用迁移序列)则返回非零。
*/
std::size_t result = 0;
const char* ptr = s.data();
const char* end = ptr + s.size();
std::mblen(NULL, 0); // 重置转换状态
while (ptr < end) {
int next = std::mblen(ptr, end-ptr);
if (next == -1) {
throw std::runtime_error("strlen_mb(): conversion error");
}
ptr += next;
++result;
}
std::cout << "s.size() = "<< s.size() << '\n'; //4
std::cout << "result = "<< result << '\n'; //10
}
四、演示代码补充
编译指令:g++ main.cpp test*.cpp -o test.exe -std=c++23
main.cpp
#include "test1.h"
int main(int argc, char* argv[])
{
char_traits_test();
basestr_test_first();
basestr_construction_test();
basestr_func_test();
string_func_test();
wstring_func_test();
string_view_test();
NTBS_str_test();
ctype_char_test();
empty_str_test();
return 0;
}
test1.h
#ifndef _TEST_1_H_
#define _TEST_1_H_
void char_traits_test(void);
void basestr_test_first(void);
void basestr_construction_test(void);
void basestr_func_test(void);
void string_func_test(void);
void wstring_func_test(void);
void string_view_test(void);
void NTBS_str_test(void);
void ctype_char_test(void);
void empty_str_test(void);
#endif //_TEST_1_H_
test1.cpp
#include "test1.h"
#include <iostream>
#include <cstring>
#include <string>
struct ci_char_traits : public std::char_traits<char> {
static char to_upper(char ch) {
return std::toupper((unsigned char) ch);
}
static bool eq(char c1, char c2) {
return to_upper(c1) == to_upper(c2);
}
static bool lt(char c1, char c2) {
return to_upper(c1) < to_upper(c2);
}
static int compare(const char* s1, const char* s2, std::size_t n) {
while ( n-- != 0 ) {
if ( to_upper(*s1) < to_upper(*s2) ) return -1;
if ( to_upper(*s1) > to_upper(*s2) ) return 1;
++s1; ++s2;
}
return 0;
}
static const char* find(const char* s, std::size_t n, char a) {
auto const ua (to_upper(a));
while ( n-- != 0 )
{
if (to_upper(*s) == ua)
return s;
s++;
}
return nullptr;
}
};
template<class DstTraits, class CharT, class SrcTraits>
constexpr std::basic_string_view<CharT, DstTraits>
traits_cast(const std::basic_string_view<CharT, SrcTraits> src) noexcept
{
return {src.data(), src.size()};
}
void char_traits_test(void)
{
using namespace std::literals;
constexpr auto s1 = "Hello"sv;
constexpr auto s2 = "heLLo"sv;
if (traits_cast<ci_char_traits>(s1) == traits_cast<ci_char_traits>(s2))
std::cout << s1 << " and " << s2 << " are equal\n";
}
void basestr_test_first(void)
{
std::basic_string<char> str1{"hello"};
std::basic_string<char> str2(" ");
std::basic_string<char> str3 = "world";
std::basic_string<char> str4(2,'!');
std::basic_string<char> str5 = str1 + str2 + str3 + str4;
std::cout << "str5 = " << str5 << "\n";
}
void basestr_construction_test(void)
{
std::allocator<char> alloc_char;
std::basic_string<char> str1;
std::basic_string<char> str2{};
std::basic_string<char> str3(alloc_char);
//
std::basic_string<char> str4(4,'a');
std::basic_string<char> str5{5,'b'};
std::cout << "str5 = " << str5 << "\n";
//
std::basic_string<char> str6(str4,0);
std::basic_string<char> str7{str5,0};
std::cout << "str7 = " << str7 << "\n";
//
std::basic_string<char> str8(str4,0,3);
std::basic_string<char> str9{str5,0,4};
std::cout << "str9 = " << str9 << "\n";
//
char cvec[] = "hello";
std::basic_string<char> str10(cvec,3);
std::basic_string<char> str11{cvec,4};
std::cout << "str11 = " << str11 << "\n";
//
std::basic_string<char> str12(cvec);
std::basic_string<char> str13{cvec};
std::cout << "str13 = " << str13 << "\n";
//
std::basic_string<char> str14(str10); //复制构�?
std::basic_string<char> str15{str11};
std::cout << "str15 = " << str15 << "\n";
//
std::basic_string<char> const cstr("world");
std::basic_string<char> str16(cstr); //移动构�?
std::basic_string<char> str17{cstr};
std::cout << "str17 = " << str17 << "\n";
//
std::basic_string<char> str18(std::begin(cvec),std::end(cvec)-1);
std::basic_string<char> str19{std::begin(cvec),std::end(cvec)-1};
std::cout << "str19 = " << str19 << "\n";
//basic_string(std::initializer_list<charT> ilist)
std::basic_string<char> str20({ 'b', 'a', 's', 'i', 'c', '_', 's', 't', 'r', 'i', 'n', 'g' });
std::cout << "str20 = " << str20 << "\n";
}
void basestr_func_test(void)
{
std::basic_string<char> str1{"hello"};
std::basic_string<char> str2(" ");
std::basic_string<char> str3 = "world!";
std::basic_string<char> str = str1 + str2 + str3;//+ , =
std::cout << "str = " << str << "\n";
//find
std::basic_string<char>::size_type pos = str.find('o');
std::cout << "pos = " << pos << "\n";
std::basic_string<char>::size_type pos_s = str.find("o");
std::cout << "pos_s = " << pos_s << "\n";
std::basic_string<char>::size_type rpos = str.rfind('o');
std::cout << "rpos = " << rpos << "\n";
std::basic_string<char>::size_type rpos_s = str.rfind("o");
std::cout << "rpos_s = " << rpos_s << "\n";
//find of
std::basic_string<char>::size_type fpos = str.find_first_of("l");
std::cout << "fpos = " << fpos << "\n";
std::basic_string<char>::size_type fpos_n = str.find_first_not_of("l");
std::cout << "fpos_n = " << fpos_n << "\n";
std::basic_string<char>::size_type lpos = str.find_last_of("l");
std::cout << "lpos = " << lpos << "\n";
std::basic_string<char>::size_type lpos_n = str.find_last_not_of("l");
std::cout << "lpos_n = " << lpos_n << "\n";
//访问元素
std::cout << "str.at(2) = " << str.at(2) << "\n";
std::cout << "str[2] = " << str[2] << "\n";
std::cout << "str.front() = " << str.front() << "\n";
std::cout << "str.back() = " << str.back() << "\n";
std::cout << "str.c_str() = " << std::string(str.c_str()) << "\n";
//
std::basic_string<char>::iterator iter = str.begin();
std::cout << "str for iterator = " ;
for(; iter!=str.end(); ++iter){
std::cout << *iter;
}
std::cout << "\n";
std::basic_string<char>::reverse_iterator riter = str.rbegin();
std::cout << "str for reverse_iterator = " ;
for(; riter!=str.rend(); ++riter){
std::cout << *riter;
}
std::cout << "\n";
//size
std::cout << "str.empty() = " << str.empty() << "\n";
std::cout << "str.size() = " << str.size() << "\n";
std::cout << "str.length() = " << str.length() << "\n";
std::cout << "str.max_size() = " << str.max_size() << "\n";
std::cout << "str.capacity() = " << str.capacity() << "\n";
str.reserve(std::size_t(10));
std::cout << "str.size() = " << str.size() << "\n";
//
std::basic_string<char> int_str{"1000"};
std::cout << "std::stoi(int_str) = " << std::stoi(int_str) << "\n";
std::cout << "std::stol(int_str) = " << std::stol(int_str) << "\n";
std::cout << "std::stoll(int_str) = " << std::stoll(int_str) << "\n";
std::cout << "std::stoul(int_str) = " << std::stoul(int_str) << "\n";
std::cout << "std::stoull(int_str) = " << std::stoull(int_str) << "\n";
std::basic_string<char> double_str{"345.678"};
std::cout << "std::stof(double_str) = " << std::stof(double_str) << "\n";
std::cout << "std::stod(double_str) = " << std::stod(double_str) << "\n";
std::cout << "std::stold(double_str) = " << std::stold(double_str) << "\n";
double dval = 12345.6789;
std::cout << "std::to_string(dval) = " << std::to_string(dval) << "\n";
//
str.insert(2,"g");
std::cout << "str = " << str << "\n";
str.push_back('a');
std::cout << "str = " << str << "\n";
str.append(2,'b');
std::cout << "str = " << str << "\n";
str.append("cd");
std::cout << "str = " << str << "\n";
//...
}
void string_func_test(void)
{
std::string str1{"hello"};
std::string str2(" ");
std::string str3 = "world!";
std::string str = str1 + str2 + str3;//+ , =
std::cout << "str = " << str << "\n";
}
#include <string>
#include <fstream>
#include <vector>
#include <iterator>
#include <locale>
void wstring_func_test(void)
{
//
std::wcout.imbue(std::locale());
//
std::wstring wss( L"hi" );
std::wcout << "wss = "<< wss << "!\n";
//
std::wofstream wofs( L"output.txt" );
if( wofs.fail() )
{
std::wcout<<"error"<< "\n";
return ;
}
wofs.imbue( std::locale() );
wofs.write( wss.c_str(), wss.size() );
typedef std::vector< std::wstring > WSVEC;
WSVEC wsv;
wsv.push_back( wss );
wsv.push_back( L"hello!" );
std::copy( wsv.begin(),wsv.end()
, std::ostream_iterator< std::wstring,std::wstring::value_type >( std::wcout,L"" ) );
std::copy( wsv.begin(),wsv.end()
, std::ostream_iterator< std::wstring,std::wstring::value_type >( wofs,L"" ) );
wofs.close();
}
#include <string_view>
#include <array>
void string_view_test(void)
{
//
std::basic_string_view<wchar_t> wcstr_v = L"xyzzy";
std::cout << std::wcslen(wcstr_v.data()) << '\n';
// OK :底层字符数组为空终止
char array[3] = {'B', 'a', 'r'};
std::basic_string_view<char> array_v(array, sizeof array);
// std::cout << std::strlen(array_v.data()) << '\n';
// 错误:底层字符数组非空终止
//
std::basic_string<char> str(array_v.data(), array_v.size()); // OK
std::cout << std::strlen(str.data()) << '\n';
// OK : std::string 的底层字符数组始终为空终止
std::basic_string<char> cppstr = "Foo";
std::basic_string_view<char> cppstr_v(cppstr);
std::array ar = {'P', 'u', 'b'};
std::basic_string_view<char> ar_v(ar.begin(), ar.end()); // C++20
std::cout << cppstr_v << ' '
<< array_v << ' '
<< ar_v << ' '
<< wcstr_v.size() << '\n';
//
std::ostream_iterator<char> out_it(std::cout);
std::basic_string_view<char> str_view("abcdef");
std::copy(str_view.rbegin(), std::next(str_view.rbegin(), 3), out_it);
*out_it = '\n';
std::copy(str_view.crbegin(), std::next(str_view.crbegin(), 3), out_it);
*out_it = '\n';
//
auto begin = str_view.begin();
auto cbegin = str_view.cbegin();
std::cout << *begin << '\n';
std::cout << *cbegin << '\n';
std::cout << std::boolalpha << (begin == cbegin) << '\n';
std::cout << std::boolalpha << (*begin == *cbegin) << '\n';
//
for (std::size_t i = 0; i<str_view.length(); ++i)
std::cout << i << ": " << str_view.at(i) << '\n';
//
using namespace std::literals;
//c++23,contains
std::cout
<< std::boolalpha
// bool contains(basic_string_view x) const noexcept;
<< "https://cppreference.com"sv.contains("cpp"sv) << ' ' // true
<< "https://cppreference.com"sv.contains("java"sv) << ' ' // false
// bool contains(CharT x) const noexcept;
<< "C++23"sv.contains('+') << ' ' // true
<< "C++23"sv.contains('-') << ' ' // false
// bool contains(const CharT* x) const;
<< std::basic_string_view<char>("basic_string_view").contains("string") << ' ' // true
<< std::basic_string_view<char>("basic_string_view").contains("String") << ' ' // false
<< '\n';
}
void NTBS_str_test(void)
{
unsigned char c = '\xe5'; // ISO-8859-1 中的字母 å
std::setlocale(LC_ALL, "en_US.utf8");
std::cout << "islower(\'\\xe5\', default C locale) returned "
<< std::boolalpha << (bool)std::islower(c) << '\n';
std::setlocale(LC_ALL, "en_GB.iso88591");
std::cout << "islower(\'\\xe5\', ISO-8859-1 locale) returned "
<< std::boolalpha << (bool)std::islower(c) << '\n';
c = '\xc6'; // ISO-8859-1 中的字母 Æ
std::setlocale(LC_ALL, "en_US.utf8");
std::cout << "isupper(\'\\xc6\', default C locale) returned "
<< std::boolalpha << (bool)std::isupper(c) << '\n';
std::setlocale(LC_ALL, "en_GB.iso88591");
std::cout << "isupper(\'\\xc6\', ISO-8859-1 locale) returned "
<< std::boolalpha << (bool)std::isupper(c) << '\n';
}
void ctype_char_test(void)
{
// 允许 mblen() 以 UTF-8 多字节编码工作
std::setlocale(LC_ALL, "en_US.utf8");
const std::string_view s{"z\u00df\u6c34\U0001f34c"};//u8"zß水🍌"
/*int mblen( const char* s, std::size_t n );
*s - 指向多字节字符的指针
*n - s中能被检验的字节数限制
*若 s 不是空指针,则返回多字节字符所含的字节数,或若 s 所指的首字节不组成合法多字节字符则返回 -1 ,或若 s 指向空字符 '\0' 则返回 0 。
*若 s 是空指针,则重置内部转换状态为初始迁移状态,并若当前多字节编码非状态依赖(不使用迁移序列)则返回 0 ,或者若当前多字节编码为状态依赖(使用迁移序列)则返回非零。
*/
std::size_t result = 0;
const char* ptr = s.data();
const char* end = ptr + s.size();
std::mblen(NULL, 0); // 重置转换状态
while (ptr < end) {
int next = std::mblen(ptr, end-ptr);
if (next == -1) {
throw std::runtime_error("strlen_mb(): conversion error");
}
ptr += next;
++result;
}
std::cout << "s.size() = "<< s.size() << '\n'; //1
std::cout << "result = "<< result << '\n'; //1
}
void empty_str_test(void)
{
std::setlocale(LC_ALL, "en_US.utf8");
char evec1[] = {'\xe4','\xbd','\xa0','\xe5','\xa5','\xbd','\0'} ;
std::cout << "evec2[] = "<< std::string(evec1) << '\n';
std::setlocale(LC_ALL, "chs");
char evec2[] = {'\xc4', '\xe3', '\xba', '\xc3', '\0'} ;
std::cout << "evec2[] = "<< std::string(evec2) << '\n';
}
std::basic_string标准库声明:
namespace std {
template<class CharT, class Traits = char_traits<CharT>,
class Allocator = allocator<CharT>>
class basic_string {
public:
// 类型
using Traits_type = Traits;
using value_type = CharT;
using allocator_type = Allocator;
using size_type = typename allocator_traits<Allocator>::size_type;
using difference_type = typename allocator_traits<Allocator>::difference_type;
using pointer = typename allocator_traits<Allocator>::pointer;
using const_pointer = typename allocator_traits<Allocator>::const_pointer;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = /* 由实现定义 */;
using const_iterator = /* 由实现定义 */;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static const size_type npos = -1;
// 构造/复制/销毁
constexpr basic_string() noexcept(noexcept(Allocator()))
: basic_string(Allocator()) { }
constexpr explicit basic_string(const Allocator& a) noexcept;
constexpr basic_string(const basic_string& str);
constexpr basic_string(basic_string&& str) noexcept;
constexpr basic_string(const basic_string& str, size_type pos,
const Allocator& a = Allocator());
constexpr basic_string(const basic_string& str, size_type pos, size_type n,
const Allocator& a = Allocator());
template<class T>
constexpr basic_string(const T& t, size_type pos, size_type n,
const Allocator& a = Allocator());
template<class T>
constexpr explicit basic_string(const T& t, const Allocator& a = Allocator());
constexpr basic_string(const CharT* s, size_type n, const Allocator& a = Allocator());
constexpr basic_string(const CharT* s, const Allocator& a = Allocator());
constexpr basic_string(nullptr_t) = delete;
constexpr basic_string(size_type n, CharT c, const Allocator& a = Allocator());
template<class InputIt>
constexpr basic_string(InputIt begin, InputIt end,
const Allocator& a = Allocator());
constexpr basic_string(initializer_list<CharT>, const Allocator& = Allocator());
constexpr basic_string(const basic_string&, const Allocator&);
constexpr basic_string(basic_string&&, const Allocator&);
constexpr ~basic_string();
constexpr basic_string& operator=(const basic_string& str);
constexpr basic_string& operator=(basic_string&& str)
noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
allocator_traits<Allocator>::is_always_equal::value);
template<class T>
constexpr basic_string& operator=(const T& t);
constexpr basic_string& operator=(const CharT* s);
constexpr basic_string& operator=(nullptr_t) = delete;
constexpr basic_string& operator=(CharT c);
constexpr basic_string& operator=(initializer_list<CharT>);
// 迭代器
constexpr iterator begin() noexcept;
constexpr const_iterator begin() const noexcept;
constexpr iterator end() noexcept;
constexpr const_iterator end() const noexcept;
constexpr reverse_iterator rbegin() noexcept;
constexpr const_reverse_iterator rbegin() const noexcept;
constexpr reverse_iterator rend() noexcept;
constexpr const_reverse_iterator rend() const noexcept;
constexpr const_iterator cbegin() const noexcept;
constexpr const_iterator cend() const noexcept;
constexpr const_reverse_iterator crbegin() const noexcept;
constexpr const_reverse_iterator crend() const noexcept;
// 容量
constexpr size_type size() const noexcept;
constexpr size_type length() const noexcept;
constexpr size_type max_size() const noexcept;
constexpr void resize(size_type n, CharT c);
constexpr void resize(size_type n);
constexpr size_type capacity() const noexcept;
constexpr void reserve(size_type res_arg);
void reserve(); // 弃用
constexpr void shrink_to_fit();
constexpr void clear() noexcept;
[[nodiscard]] constexpr bool empty() const noexcept;
// 元素访问
constexpr const_reference operator[](size_type pos) const;
constexpr reference operator[](size_type pos);
constexpr const_reference at(size_type n) const;
constexpr reference at(size_type n);
constexpr const CharT& front() const;
constexpr CharT& front();
constexpr const CharT& back() const;
constexpr CharT& back();
// 修改器
constexpr basic_string& operator+=(const basic_string& str);
template<class T>
constexpr basic_string& operator+=(const T& t);
constexpr basic_string& operator+=(const CharT* s);
constexpr basic_string& operator+=(CharT c);
constexpr basic_string& operator+=(initializer_list<CharT>);
constexpr basic_string& append(const basic_string& str);
constexpr basic_string& append(const basic_string& str,
size_type pos, size_type n = npos);
template<class T>
constexpr basic_string& append(const T& t);
template<class T>
constexpr basic_string& append(const T& t, size_type pos, size_type n = npos);
constexpr basic_string& append(const CharT* s, size_type n);
constexpr basic_string& append(const CharT* s);
constexpr basic_string& append(size_type n, CharT c);
template<class InputIt>
constexpr basic_string& append(InputIt first, InputIt last);
constexpr basic_string& append(initializer_list<CharT>);
constexpr void push_back(CharT c);
constexpr basic_string& assign(const basic_string& str);
constexpr basic_string& assign(basic_string&& str)
noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
allocator_traits<Allocator>::is_always_equal::value);
constexpr basic_string& assign(const basic_string& str,
size_type pos, size_type n = npos);
template<class T>
constexpr basic_string& assign(const T& t);
template<class T>
constexpr basic_string& assign(const T& t, size_type pos, size_type n = npos);
constexpr basic_string& assign(const CharT* s, size_type n);
constexpr basic_string& assign(const CharT* s);
constexpr basic_string& assign(size_type n, CharT c);
template<class InputIt>
constexpr basic_string& assign(InputIt first, InputIt last);
constexpr basic_string& assign(initializer_list<CharT>);
constexpr basic_string& insert(size_type pos, const basic_string& str);
constexpr basic_string& insert(size_type pos1, const basic_string& str,
size_type pos2, size_type n = npos);
template<class T>
constexpr basic_string& insert(size_type pos, const T& t);
template<class T>
constexpr basic_string& insert(size_type pos1, const T& t,
size_type pos2, size_type n = npos);
constexpr basic_string& insert(size_type pos, const CharT* s, size_type n);
constexpr basic_string& insert(size_type pos, const CharT* s);
constexpr basic_string& insert(size_type pos, size_type n, CharT c);
constexpr iterator insert(const_iterator p, CharT c);
constexpr iterator insert(const_iterator p, size_type n, CharT c);
template<class InputIt>
constexpr iterator insert(const_iterator p,
InputIt first, InputIt last);
constexpr iterator insert(const_iterator p, initializer_list<CharT>);
constexpr basic_string& erase(size_type pos = 0, size_type n = npos);
constexpr iterator erase(const_iterator p);
constexpr iterator erase(const_iterator first, const_iterator last);
constexpr void pop_back();
constexpr basic_string& replace(size_type pos1, size_type n1, const basic_string& str);
constexpr basic_string& replace(size_type pos1, size_type n1, const basic_string& str,
size_type pos2, size_type n2 = npos);
template<class T>
constexpr basic_string& replace(size_type pos1, size_type n1, const T& t);
template<class T>
constexpr basic_string& replace(size_type pos1, size_type n1, const T& t,
size_type pos2, size_type n2 = npos);
constexpr basic_string& replace(size_type pos, size_type n1,
const CharT* s, size_type n2);
constexpr basic_string& replace(size_type pos, size_type n1, const CharT* s);
constexpr basic_string& replace(size_type pos, size_type n1, size_type n2, CharT c);
constexpr basic_string& replace(const_iterator i1, const_iterator i2,
const basic_string& str);
template<class T>
constexpr basic_string& replace(const_iterator i1, const_iterator i2, const T& t);
constexpr basic_string& replace(const_iterator i1, const_iterator i2, const CharT* s,
size_type n);
constexpr basic_string& replace(const_iterator i1, const_iterator i2, const CharT* s);
constexpr basic_string& replace(const_iterator i1, const_iterator i2,
size_type n, CharT c);
template<class InputIt>
constexpr basic_string& replace(const_iterator i1, const_iterator i2,
InputIt j1, InputIt j2);
constexpr basic_string& replace(const_iterator, const_iterator,
initializer_list<CharT>);
constexpr size_type copy(CharT* s, size_type n, size_type pos = 0) const;
constexpr void swap(basic_string& str)
noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
allocator_traits<Allocator>::is_always_equal::value);
// 字符串操作
constexpr const CharT* c_str() const noexcept;
constexpr const CharT* data() const noexcept;
constexpr CharT* data() noexcept;
constexpr operator basic_string_view<CharT, Traits>() const noexcept;
constexpr allocator_type get_allocator() const noexcept;
template<class T>
constexpr size_type find(const T& t, size_type pos = 0) const
noexcept(/* 见描述 */);
constexpr size_type find(const basic_string& str, size_type pos = 0) const noexcept;
constexpr size_type find(const CharT* s, size_type pos, size_type n) const;
constexpr size_type find(const CharT* s, size_type pos = 0) const;
constexpr size_type find(CharT c, size_type pos = 0) const noexcept;
template<class T>
constexpr size_type rfind(const T& t, size_type pos = npos) const
noexcept(/* 见描述 */);
constexpr size_type rfind(const basic_string& str,
size_type pos = npos) const noexcept;
constexpr size_type rfind(const CharT* s, size_type pos, size_type n) const;
constexpr size_type rfind(const CharT* s, size_type pos = npos) const;
constexpr size_type rfind(CharT c, size_type pos = npos) const noexcept;
template<class T>
constexpr size_type find_first_of(const T& t, size_type pos = 0) const
noexcept(/* 见描述 */);
constexpr size_type find_first_of(const basic_string& str,
size_type pos = 0) const noexcept;
constexpr size_type find_first_of(const CharT* s, size_type pos, size_type n) const;
constexpr size_type find_first_of(const CharT* s, size_type pos = 0) const;
constexpr size_type find_first_of(CharT c, size_type pos = 0) const noexcept;
template<class T>
constexpr size_type find_last_of(const T& t, size_type pos = npos) const
noexcept(/* 见描述 */);
constexpr size_type find_last_of(const basic_string& str,
size_type pos = npos) const noexcept;
constexpr size_type find_last_of(const CharT* s, size_type pos, size_type n) const;
constexpr size_type find_last_of(const CharT* s, size_type pos = npos) const;
constexpr size_type find_last_of(CharT c, size_type pos = npos) const noexcept;
template<class T>
constexpr size_type find_first_not_of(const T& t, size_type pos = 0) const
noexcept(/* 见描述 */);
constexpr size_type find_first_not_of(const basic_string& str,
size_type pos = 0) const noexcept;
constexpr size_type find_first_not_of(const CharT* s,
size_type pos, size_type n) const;
constexpr size_type find_first_not_of(const CharT* s, size_type pos = 0) const;
constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const noexcept;
template<class T>
constexpr size_type find_last_not_of(const T& t, size_type pos = npos) const
noexcept(/* 见描述 */);
constexpr size_type find_last_not_of(const basic_string& str,
size_type pos = npos) const noexcept;
constexpr size_type find_last_not_of(const CharT* s, size_type pos, size_type n) const;
constexpr size_type find_last_not_of(const CharT* s, size_type pos = npos) const;
constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const noexcept;
constexpr basic_string substr(size_type pos = 0, size_type n = npos) const;
template<class T>
constexpr int compare(const T& t) const noexcept(/* 见描述 */);
template<class T>
constexpr int compare(size_type pos1, size_type n1, const T& t) const;
template<class T>
constexpr int compare(size_type pos1, size_type n1, const T& t,
size_type pos2, size_type n2 = npos) const;
constexpr int compare(const basic_string& str) const noexcept;
constexpr int compare(size_type pos1, size_type n1, const basic_string& str) const;
constexpr int compare(size_type pos1, size_type n1, const basic_string& str,
size_type pos2, size_type n2 = npos) const;
constexpr int compare(const CharT* s) const;
constexpr int compare(size_type pos1, size_type n1, const CharT* s) const;
constexpr int compare(size_type pos1, size_type n1, const CharT* s, size_type n2) const;
constexpr bool starts_with(basic_string_view<CharT, Traits> x) const noexcept;
constexpr bool starts_with(CharT x) const noexcept;
constexpr bool starts_with(const CharT* x) const;
constexpr bool ends_with(basic_string_view<CharT, Traits> x) const noexcept;
constexpr bool ends_with(CharT x) const noexcept;
constexpr bool ends_with(const CharT* x) const;
constexpr bool contains(basic_string_view<CharT, Traits> x) const noexcept;
constexpr bool contains(CharT x) const noexcept;
constexpr bool contains(const CharT* x) const;
};
template<class InputIt,
class Allocator = allocator<typename iterator_traits<InputIt>::value_type>>
basic_string(InputIt, InputIt, Allocator = Allocator())
-> basic_string<typename iterator_traits<InputIt>::value_type,
char_traits<typename iterator_traits<InputIt>::value_type>,
Allocator>;
template<class CharT,
class Traits,
class Allocator = allocator<CharT>>
explicit basic_string(basic_string_view<CharT, Traits>, const Allocator& = Allocator())
-> basic_string<CharT, Traits, Allocator>;
template<class CharT,
class Traits,
class Allocator = allocator<CharT>>
basic_string(basic_string_view<CharT, Traits>,
typename /* 见描述 */::size_type,
typename /* 见描述 */::size_type,
const Allocator& = Allocator())
-> basic_string<CharT, Traits, Allocator>;
}