C++ 字符串视图及相关功能详解
1. 字符串视图操作
字符串视图(
basic_string_view
)是 C++ 中用于高效处理字符串的轻量级对象,它提供了一系列实用的操作函数。
1.1 交换操作
constexpr void swap(basic_string_view& s) noexcept;
此函数的作用是交换当前字符串视图对象和参数
s
的值。
1.2 复制操作
constexpr size_type copy(charT* s, size_type n, size_type pos = 0) const;
-
确定
rlen为n和size() - pos中的较小值。 -
若
pos > size(),则抛出out_of_range异常。 -
要求
[s, s + rlen)是一个有效的范围。 -
其效果等同于
traits::copy(s, data() + pos, rlen)。 -
返回
rlen。 - 复杂度为 $O(rlen)$。
1.3 子字符串操作
constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const;
-
确定
rlen为n和size() - pos中的较小值。 -
若
pos > size(),则抛出out_of_range异常。 -
确定要引用的字符串的有效长度
rlen。 -
返回一个新的字符串视图,其数据从
data() + pos开始,长度为rlen。
1.4 比较操作
constexpr int compare(basic_string_view str) const noexcept;
-
确定
rlen为当前字符串视图和str的长度中的较小值。 -
通过调用
traits::compare(data(), str.data(), rlen)来比较两个字符串。 - 复杂度为 $O(rlen)$。
-
返回值根据比较结果而定,具体如下表所示:
| 条件 | 返回值 |
| ---- | ---- |
|size() < str.size()|< 0|
|size() == str.size()|0|
|size() > str.size()|> 0|
此外,还有多种不同参数形式的
compare
函数,它们的效果通常是通过调用
substr
函数进行转换后再比较。
1.5 前缀和后缀检查
constexpr bool starts_with(basic_string_view 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 x) const noexcept;
constexpr bool ends_with(charT x) const noexcept;
constexpr bool ends_with(const charT* x) const;
这些函数用于检查字符串视图是否以指定的字符串、字符或字符指针开头或结尾。
2. 字符串视图搜索功能
字符串视图提供了多种搜索函数,如
find
、
rfind
、
find_first_of
等。
2.1 搜索函数的通用规则
-
对于形式为
constexpr return-type F (const charT* s, size_type pos) const;的成员函数,其效果等同于return F (basic_string_view(s), pos);。 -
对于形式为
constexpr return-type F (const charT* s, size_type pos, size_type n) const;的成员函数,其效果等同于return F (basic_string_view(s, n), pos);。 -
对于形式为
constexpr return-type F (charT c, size_type pos) const noexcept;的成员函数,其效果等同于return F (basic_string_view(addressof(c), 1), pos);。
2.2 具体搜索函数
constexpr size_type find(basic_string_view str, size_type pos = 0) const noexcept;
-
找到满足以下条件的最低位置
xpos: -
pos <= xpos -
xpos + str.size() <= size() -
对于
str引用的字符串的所有元素I,都有traits::eq(at(xpos + I), str.at(I))。 -
若能找到这样的
xpos,则返回该位置;否则返回npos。
constexpr size_type rfind(basic_string_view str, size_type pos = npos) const noexcept;
与
find
类似,但找到的是满足条件的最高位置。
constexpr size_type find_first_of(basic_string_view str, size_type pos = 0) const noexcept;
找到满足以下条件的最低位置
xpos
:
-
pos <= xpos
-
xpos < size()
- 对于
str
引用的字符串的某个元素
I
,有
traits::eq(at(xpos), str.at(I))
。
constexpr size_type find_last_of(basic_string_view str, size_type pos = npos) const noexcept;
与
find_first_of
类似,但找到的是满足条件的最高位置。
constexpr size_type find_first_not_of(basic_string_view str, size_type pos = 0) const noexcept;
找到满足以下条件的最低位置
xpos
:
-
pos <= xpos
-
xpos < size()
- 对于
str
引用的字符串的所有元素
I
,都有
traits::eq(at(xpos), str.at(I))
不成立。
constexpr size_type find_last_not_of(basic_string_view str, size_type pos = npos) const noexcept;
与
find_first_not_of
类似,但找到的是满足条件的最高位置。
3. 非成员比较函数
为了方便对字符串视图进行比较,提供了一系列非成员比较函数。
| 表达式 | 等效表达式 |
|---|---|
t == sv
|
S(t) == sv
|
sv == t
|
sv == S(t)
|
t != sv
|
S(t) != sv
|
sv != t
|
sv != S(t)
|
t < sv
|
S(t) < sv
|
sv < t
|
sv < S(t)
|
t > sv
|
S(t) > sv
|
sv > t
|
sv > S(t)
|
t <= sv
|
S(t) <= sv
|
sv <= t
|
sv <= S(t)
|
t >= sv
|
S(t) >= sv
|
sv >= t
|
sv >= S(t)
|
以下是
operator==
的示例实现:
template<class charT, class traits>
constexpr bool operator==(basic_string_view<charT, traits> lhs,
basic_string_view<charT, traits> rhs) noexcept {
return lhs.compare(rhs) == 0;
}
4. 插入器和提取器
template<class charT, class traits>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os, basic_string_view<charT, traits> str);
此函数的作用是将字符串视图输出到输出流中。它的行为类似于格式化输出函数,会根据输出流的宽度和字符串视图的大小进行填充,最后将字符串视图插入到输出流中,并将输出流的宽度设置为 0。
5. 哈希支持
为了支持哈希操作,提供了对不同类型字符串视图的哈希特化:
template<> struct hash<string_view>;
template<> struct hash<u8string_view>;
template<> struct hash<u16string_view>;
template<> struct hash<u32string_view>;
template<> struct hash<wstring_view>;
这些特化是启用的,并且字符串视图对象的哈希值与相应字符串对象的哈希值相等。
6. 字符串视图字面量后缀
为了方便创建字符串视图对象,提供了一系列字面量后缀操作符:
constexpr string_view operator""sv(const char* str, size_t len) noexcept;
constexpr u8string_view operator""sv(const char8_t* str, size_t len) noexcept;
constexpr u16string_view operator""sv(const char16_t* str, size_t len) noexcept;
constexpr u32string_view operator""sv(const char32_t* str, size_t len) noexcept;
constexpr wstring_view operator""sv(const wchar_t* str, size_t len) noexcept;
这些操作符返回相应类型的字符串视图对象。
7. 空终止序列实用工具
C++ 标准库还提供了一系列与空终止序列相关的头文件和函数,这些头文件的内容和意义与 C 标准库的相应头文件基本相同。
7.1
<cctype>
头文件
namespace std {
int isalnum(int c);
int isalpha(int c);
int isblank(int c);
int iscntrl(int c);
int isdigit(int c);
int isgraph(int c);
int islower(int c);
int isprint(int c);
int ispunct(int c);
int isspace(int c);
int isupper(int c);
int isxdigit(int c);
int tolower(int c);
int toupper(int c);
}
这些函数用于字符分类和大小写转换。
7.2
<cwctype>
头文件
namespace std {
using wint_t = see below ;
using wctrans_t = see below ;
using wctype_t = see below ;
int iswalnum(wint_t wc);
int iswalpha(wint_t wc);
int iswblank(wint_t wc);
int iswcntrl(wint_t wc);
int iswdigit(wint_t wc);
int iswgraph(wint_t wc);
int iswlower(wint_t wc);
int iswprint(wint_t wc);
int iswpunct(wint_t wc);
int iswspace(wint_t wc);
int iswupper(wint_t wc);
int iswxdigit(wint_t wc);
int iswctype(wint_t wc, wctype_t desc);
wctype_t wctype(const char* property);
wint_t towlower(wint_t wc);
wint_t towupper(wint_t wc);
wint_t towctrans(wint_t wc, wctrans_t desc);
wctrans_t wctrans(const char* property);
}
#define WEOF see below
这些函数用于宽字符的分类和转换。
7.3
<cstring>
头文件
namespace std {
using size_t = see 16.2.4;
void* memcpy(void* s1, const void* s2, size_t n);
void* memmove(void* s1, const void* s2, size_t n);
char* strcpy(char* s1, const char* s2);
char* strncpy(char* s1, const char* s2, size_t n);
char* strcat(char* s1, const char* s2);
char* strncat(char* s1, const char* s2, size_t n);
int memcmp(const void* s1, const void* s2, size_t n);
int strcmp(const char* s1, const char* s2);
int strcoll(const char* s1, const char* s2);
int strncmp(const char* s1, const char* s2, size_t n);
size_t strxfrm(char* s1, const char* s2, size_t n);
const void* memchr(const void* s, int c, size_t n);
void* memchr(void* s, int c, size_t n);
const char* strchr(const char* s, int c);
char* strchr(char* s, int c);
size_t strcspn(const char* s1, const char* s2);
const char* strpbrk(const char* s1, const char* s2);
char* strpbrk(char* s1, const char* s2);
const char* strrchr(const char* s, int c);
char* strrchr(char* s, int c);
size_t strspn(const char* s1, const char* s2);
const char* strstr(const char* s1, const char* s2);
char* strstr(char* s1, const char* s2);
char* strtok(char* s1, const char* s2);
void* memset(void* s, int c, size_t n);
char* strerror(int errnum);
size_t strlen(const char* s);
}
#define NULL see 16.2.3
这些函数用于字符串的复制、连接、比较、查找等操作。
7.4
<cwchar>
头文件
namespace std {
using size_t = see 16.2.4;
using mbstate_t = see below ;
using wint_t = see below ;
struct tm;
int fwprintf(FILE* stream, const wchar_t* format, ...);
int fwscanf(FILE* stream, const wchar_t* format, ...);
int swprintf(wchar_t* s, size_t n, const wchar_t* format, ...);
int swscanf(const wchar_t* s, const wchar_t* format, ...);
int vfwprintf(FILE* stream, const wchar_t* format, va_list arg);
int vfwscanf(FILE* stream, const wchar_t* format, va_list arg);
int vswprintf(wchar_t* s, size_t n, const wchar_t* format, va_list arg);
int vswscanf(const wchar_t* s, const wchar_t* format, va_list arg);
int vwprintf(const wchar_t* format, va_list arg);
int vwscanf(const wchar_t* format, va_list arg);
int wprintf(const wchar_t* format, ...);
int wscanf(const wchar_t* format, ...);
wint_t fgetwc(FILE* stream);
wchar_t* fgetws(wchar_t* s, int n, FILE* stream);
wint_t fputwc(wchar_t c, FILE* stream);
int fputws(const wchar_t* s, FILE* stream);
int fwide(FILE* stream, int mode);
wint_t getwc(FILE* stream);
wint_t getwchar();
wint_t putwc(wchar_t c, FILE* stream);
wint_t putwchar(wchar_t c);
wint_t ungetwc(wint_t c, FILE* stream);
double wcstod(const wchar_t* nptr, wchar_t** endptr);
float wcstof(const wchar_t* nptr, wchar_t** endptr);
long double wcstold(const wchar_t* nptr, wchar_t** endptr);
long int wcstol(const wchar_t* nptr, wchar_t** endptr, int base);
long long int wcstoll(const wchar_t* nptr, wchar_t** endptr, int base);
unsigned long int wcstoul(const wchar_t* nptr, wchar_t** endptr, int base);
unsigned long long int wcstoull(const wchar_t* nptr, wchar_t** endptr, int base);
wchar_t* wcscpy(wchar_t* s1, const wchar_t* s2);
wchar_t* wcsncpy(wchar_t* s1, const wchar_t* s2, size_t n);
wchar_t* wmemcpy(wchar_t* s1, const wchar_t* s2, size_t n);
wchar_t* wmemmove(wchar_t* s1, const wchar_t* s2, size_t n);
wchar_t* wcscat(wchar_t* s1, const wchar_t* s2);
wchar_t* wcsncat(wchar_t* s1, const wchar_t* s2, size_t n);
int wcscmp(const wchar_t* s1, const wchar_t* s2);
int wcscoll(const wchar_t* s1, const wchar_t* s2);
int wcsncmp(const wchar_t* s1, const wchar_t* s2, size_t n);
size_t wcsxfrm(wchar_t* s1, const wchar_t* s2, size_t n);
int wmemcmp(const wchar_t* s1, const wchar_t* s2, size_t n);
const wchar_t* wcschr(const wchar_t* s, wchar_t c);
wchar_t* wcschr(wchar_t* s, wchar_t c);
size_t wcscspn(const wchar_t* s1, const wchar_t* s2);
const wchar_t* wcspbrk(const wchar_t* s1, const wchar_t* s2);
wchar_t* wcspbrk(wchar_t* s1, const wchar_t* s2);
const wchar_t* wcsrchr(const wchar_t* s, wchar_t c);
wchar_t* wcsrchr(wchar_t* s, wchar_t c);
size_t wcsspn(const wchar_t* s1, const wchar_t* s2);
const wchar_t* wcsstr(const wchar_t* s1, const wchar_t* s2);
wchar_t* wcsstr(wchar_t* s1, const wchar_t* s2);
wchar_t* wcstok(wchar_t* s1, const wchar_t* s2, wchar_t** ptr);
const wchar_t* wmemchr(const wchar_t* s, wchar_t c, size_t n);
wchar_t* wmemchr(wchar_t* s, wchar_t c, size_t n);
size_t wcslen(const wchar_t* s);
wchar_t* wmemset(wchar_t* s, wchar_t c, size_t n);
size_t wcsftime(wchar_t* s, size_t maxsize, const wchar_t* format, const struct tm* timeptr);
wint_t btowc(int c);
int wctob(wint_t c);
int mbsinit(const mbstate_t* ps);
size_t mbrlen(const char* s, size_t n, mbstate_t* ps);
size_t mbrtowc(wchar_t* pwc, const char* s, size_t n, mbstate_t* ps);
size_t wcrtomb(char* s, wchar_t wc, mbstate_t* ps);
size_t mbsrtowcs(wchar_t* dst, const char** src, size_t len, mbstate_t* ps);
size_t wcsrtombs(char* dst, const wchar_t** src, size_t len, mbstate_t* ps);
}
#define NULL see 16.2.3
#define WCHAR_MAX see below
#define WCHAR_MIN see below
#define WEOF see below
这些函数用于宽字符的输入输出、字符串操作以及多字节字符与宽字符的转换。
7.5
<cuchar>
头文件
namespace std {
using mbstate_t = see below ;
using size_t = see 16.2.4;
size_t mbrtoc8(char8_t* pc8, const char* s, size_t n, mbstate_t* ps);
size_t c8rtomb(char* s, char8_t c8, mbstate_t* ps);
size_t mbrtoc16(char16_t* pc16, const char* s, size_t n, mbstate_t* ps);
size_t c16rtomb(char* s, char16_t c16, mbstate_t* ps);
size_t mbrtoc32(char32_t* pc32, const char* s, size_t n, mbstate_t* ps);
size_t c32rtomb(char* s, char32_t c32, mbstate_t* ps);
}
这些函数用于多字节字符与不同编码字符的转换。
8. 多字节 / 宽字符串和字符转换函数
这些函数主要用于多字节字符和宽字符之间的转换,部分函数在使用时需要注意数据竞争的问题。
int mbsinit(const mbstate_t* ps);
int mblen(const char* s, size_t n);
size_t mbstowcs(wchar_t* pwcs, const char* s, size_t n);
size_t wcstombs(char* s, const wchar_t* pwcs, size_t n);
int mbtowc(wchar_t* pwc, const char* s, size_t n);
int wctomb(char* s, wchar_t wchar);
size_t mbrlen(const char* s, size_t n, mbstate_t* ps);
size_t mbrtowc(wchar_t* pwc, const char* s, size_t n, mbstate_t* ps);
size_t wcrtomb(char* s, wchar_t wc, mbstate_t* ps);
size_t mbsrtowcs(wchar_t* dst, const char** src, size_t len, mbstate_t* ps);
size_t wcsrtombs(char* dst, const wchar_t** src, size_t len, mbstate_t* ps);
size_t mbrtoc8(char8_t* pc8, const char* s, size_t n, mbstate_t* ps);
size_t c8rtomb(char* s, char8_t c8, mbstate_t* ps);
其中,
mbrtoc8
函数用于将多字节字符转换为 UTF - 8 编码的字符,
c8rtomb
函数用于将 UTF - 8 编码的字符转换为多字节字符。
总结
本文详细介绍了 C++ 中字符串视图的各种操作、搜索功能、非成员比较函数、插入器和提取器、哈希支持以及相关的空终止序列实用工具和多字节 / 宽字符串转换函数。这些功能为 C++ 开发者提供了强大而高效的字符串处理能力,在实际应用中可以根据具体需求选择合适的函数进行使用。同时,在使用一些涉及多线程的函数时,需要注意数据竞争的问题,以确保程序的正确性和稳定性。
C++ 字符串视图及相关功能详解
9. 多字节/宽字符串和字符转换函数详解
9.1
mbrtoc8
函数
size_t mbrtoc8(char8_t* pc8, const char* s, size_t n, mbstate_t* ps);
- 功能 :该函数用于将多字节字符转换为 UTF - 8 编码的字符。
-
操作步骤
:
-
若
s为nullptr,则等效于mbrtoc8(nullptr, "", 1, ps)。 -
检查从
s指向的字节开始的最多n个字节,以确定完成下一个多字节字符(包括任何移位序列)所需的字节数。 -
如果确定下一个多字节字符完整且有效,则确定相应 UTF - 8 代码单元的值。若
pc8不为nullptr,则将第一个(或唯一)这样的代码单元的值存储在pc8指向的对象中。后续调用将存储连续的 UTF - 8 代码单元,而无需消耗任何额外的输入,直到所有代码单元都被存储。 - 如果对应的 Unicode 字符是 U+0000,则得到的状态是初始转换状态。
-
若
-
返回值
:
-
若接下来的
n个或更少字节完成了对应于 U+0000 Unicode 字符的多字节字符(即存储的值),则返回 0。 -
若接下来的
n个或更少字节完成了一个有效的多字节字符(即存储的值),则返回完成该多字节字符的字节数,该值在 1 到n之间(包含 1 和n)。 -
若上一次调用产生的下一个字符已被存储(本次调用未消耗输入中的任何字节),则返回
(size_t)(-3)。 -
若接下来的
n个字节对一个不完整(但可能有效)的多字节字符有贡献,且所有n个字节都已处理(未存储任何值),则返回(size_t)(-2)。 -
若发生编码错误,即接下来的
n个或更少字节对一个完整且有效的多字节字符无贡献(未存储任何值),则将宏EILSEQ的值存储在errno中,返回(size_t)(-1),并且转换状态未指定。
-
若接下来的
9.2
c8rtomb
函数
size_t c8rtomb(char* s, char8_t c8, mbstate_t* ps);
- 功能 :该函数用于将 UTF - 8 编码的字符转换为多字节字符。
-
操作步骤
:
-
若
s为nullptr,则等效于c8rtomb(buf, u8'\0', ps),其中buf是一个内部缓冲区。 -
若
c8完成了一个有效的 UTF - 8 代码单元序列,则确定表示该多字节字符(包括任何移位序列)所需的字节数,并将多字节字符表示存储在以s指向的元素为首的数组中。最多存储MB_CUR_MAX个字节。 - 若多字节字符是一个空字符,则存储一个空字节,并在前面加上恢复初始移位状态所需的任何移位序列;得到的状态是初始转换状态。
-
若
-
返回值
:返回存储在数组对象中的字节数(包括任何移位序列)。若
c8对对应于有效多字节字符的char8_t序列无贡献,则将宏EILSEQ的值存储在errno中,返回(size_t)(-1),并且转换状态未指定。 -
注意事项
:对
c8rtomb函数使用s为nullptr的调用可能会与其他使用s为nullptr的c8rtomb调用引入数据竞争。
10. 函数调用关系与流程图
为了更清晰地理解这些函数之间的调用关系,下面给出一个简单的 mermaid 流程图:
graph TD;
A[mbsinit] -->|调用| B[mbrlen];
A -->|调用| C[mbrtowc];
A -->|调用| D[wcrtomb];
A -->|调用| E[mbsrtowcs];
A -->|调用| F[wcsrtombs];
C -->|调用| G[mbrtoc8];
D -->|调用| H[c8rtomb];
这个流程图展示了多字节/宽字符串和字符转换函数之间的一些基本调用关系。例如,
mbsinit
函数可能会作为其他函数的前置检查,而
mbrtowc
可能会调用
mbrtoc8
进行多字节到 UTF - 8 字符的转换。
11. 实际应用场景与示例
在实际的 C++ 开发中,这些字符串视图和相关转换函数有很多应用场景。以下是一些示例:
11.1 字符串搜索与比较
#include <iostream>
#include <string_view>
int main() {
std::string_view str = "Hello, World!";
std::string_view sub = "World";
// 查找子字符串
size_t pos = str.find(sub);
if (pos != std::string_view::npos) {
std::cout << "Found 'World' at position: " << pos << std::endl;
} else {
std::cout << "Did not find 'World'" << std::endl;
}
// 比较字符串
if (str.compare(0, 5, "Hello") == 0) {
std::cout << "The first 5 characters are 'Hello'" << std::endl;
}
return 0;
}
在这个示例中,我们使用了
std::string_view
的
find
和
compare
函数进行字符串的搜索和比较操作。
11.2 多字节字符转换
#include <iostream>
#include <cuchar>
#include <cwchar>
int main() {
char8_t utf8_char;
char multi_byte[] = "你";
mbstate_t state{};
// 多字节字符转换为 UTF - 8 字符
size_t result = mbrtoc8(&utf8_char, multi_byte, sizeof(multi_byte), &state);
if (result > 0) {
std::cout << "Converted multi - byte char to UTF - 8 successfully" << std::endl;
}
return 0;
}
此示例展示了如何使用
mbrtoc8
函数将多字节字符转换为 UTF - 8 字符。
12. 性能考虑
在使用这些字符串处理函数时,性能是一个重要的考虑因素。例如,字符串视图的搜索函数在最坏情况下的复杂度为 $O(size() * str.size())$,但实现应该尽量优化。在处理大规模字符串时,选择合适的函数可以显著提高性能。
- 避免不必要的复制 :使用字符串视图可以避免对字符串进行不必要的复制,因为它只是对现有字符串的一个视图。
- 选择合适的搜索算法 :对于搜索函数,一些实现可能会使用更高效的算法,如 KMP 算法或 Boyer - Moore 算法,以减少搜索时间。
13. 总结与建议
本文全面介绍了 C++ 中字符串视图及相关功能,包括字符串视图的操作、搜索功能、非成员比较函数、插入器和提取器、哈希支持,以及空终止序列实用工具和多字节/宽字符串转换函数。这些功能为 C++ 开发者提供了丰富的字符串处理能力。
在实际应用中,建议开发者:
-
合理使用字符串视图
:在需要处理字符串但不需要复制的场景中,优先使用字符串视图,以提高性能。
-
注意数据竞争
:在使用涉及多线程的函数时,如
c8rtomb
等,要注意数据竞争问题,确保程序的正确性和稳定性。
-
根据需求选择函数
:根据具体的应用场景,选择合适的字符串处理函数,以达到最佳的性能和效果。
通过深入理解和合理运用这些功能,开发者可以更高效地处理 C++ 中的字符串操作。
超级会员免费看

456

被折叠的 条评论
为什么被折叠?



