字符串的索引计数从零开始(不是一)。字符串范围是一个来自字符串字面的连续字符序列。整个字符串就是一个大的范围。较小的范围可以从一个字符串中获得。字符串视图的想法是从一个字符串中获得一个决定性的范围,这样的范围就像一个独立的字符串,它自己的第一个字符的索引是0。尽管一个字符串视图对象可以独立存在,但它正式成为一个范围视图,而且通常不能被改变。
字符串视图是它自己的一个类库,应按如下方式包含在C++程序中。
#include <string_view>
它的成员函数被归为以下几类:构造、容量、元素访问、迭代器支持、修改器、字符串操作和搜索。奇怪的是,string_view有修改器,这与它的定义有冲突。这一点将在本教程中得到澄清。本教程中所有的字符串视图代码都在C++ main()函数中。
文章内容
结构
basic_string_view(const charT* str)
这是一个字符串视图,由一个完整的字符串字头组成,由常量指针到字符组成。下面的例子说明了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str);
cout <<strV <<endl;
return 0;
}
输出的结果是
one two three four five
请注意,构造函数的名称使用了string_view,而不是basic_string_view。
basic_string_view(const charT* str, size_type len)
这个成员函数窗口了字符串参数的前len个字符。窗口化不是复制。没有字符串视图构造函数会复制。它们只是引用。下面的程序显示了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str, 13);
cout <<strV <<endl;
return 0;
}
输出的结果是
one two three
basic_string_view(const basic_string_view&)
这个构造函数与上面的第一个构造函数相同,但它的参数是一个string_view对象,它已经有了自己的视图。下面的程序说明了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str, 13);
string_view strVV(strV);
cout <<strVV <<endl;
return 0;
}
输出的结果是
one two three
basic_string_view& operator=(const basic_string_view&)
这个成员构造函数与上面类似,但string_view是被分配的,而不是作为参数传递的。下面的程序说明了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str, 13);
string_view strVV = strV;
cout <<strVV <<endl;
return 0;
}
输出的结果是
one two three
容量
size_type size() const
记住,string_view是某个原始字符串的一个范围。它不是该范围的副本。它有对原始字符串中该范围的字符元素的引用。这个string_view(窗口视图)的大小仍然可以确定,因为string_view是一个从string_view类实例化的对象。该大小由size()成员函数返回。下面的程序演示了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str, 13);
int sz = strV.size();
cout <<sz <<endl;
return 0;
}
输出结果是
13
bool empty() const
一个string_view对象可以被创建为空,如以下程序所示。当为空时,empty()成员函数将返回真,否则将返回假。该程序为
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
string_view strV;
bool bl = strV.empty();
cout <<bl <<endl;
return 0;
}
输出是1,表示真。注意,创建string_view对象时没有括号,也没有任何参数。
元素访问
const_reference operator[](size_type pos) const
方括号操作符可以用来读取string_view中的一个值(字符)。下面的程序使用一个for-loop来说明这个问题。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str, 13);
for (int i=0; i <strV.size(); i++)
cout <<strV[i];
cout <<endl;
return 0;
}
输出的结果是
one two three
注意:string_view对象中的一个字符不能被改变。任何可能的改变都应该在原始字符串中进行。
迭代器支持
const_iterator begin() const
这个成员函数返回一个常数迭代器,它指向string_view对象的第一个字符元素。这里,const_iterator意味着字符不能被改变。然而,迭代器可以被递增以指向下一个字符元素;或者类似于在索引中添加一个整数以指向前面的另一个字符元素。下面的程序说明了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str, 13);
string_view::const_iterator it = strV.begin();
it++;
it = it + 4;
cout <<*it <<endl;
return 0;
}
输出是'w'。请注意,迭代器的构造方式。
const_iterator end() const
这个成员函数返回一个恒定的迭代器,它正好指向string_view对象的最后一个字符元素之后。该迭代器可以被递减以指向最后一个字符元素,或者被减去一个整数,类似于从索引中减去一个整数,以指向之前的另一个字符元素。这里,const_iterator意味着字符不能被改变。下面的程序说明了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str, 13);
string_view::const_iterator it = strV.end();
it--;
it = it - 4;
cout <<*it <<endl;
return 0;
}
输出是't'。
修改器
void remove_prefix(size_type n)
这将从string_view中删除第一个子字符串,但是它不会从原始字符串中删除任何字符。下面的程序说明了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str, 13);
cout <<strV <<endl;
strV.remove_prefix(4);
cout <<strV <<endl;
cout <<str <<endl;
return 0;
}
输出结果是
one two three
two three
one two three four five
void remove_suffix(size_type n)
这将从string_view中删除最后一个子串,但是它并没有从原始字符串中删除任何字符。下面的程序说明了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str, 13);
cout <<strV <<endl;
strV.remove_suffix(6);
cout <<strV <<endl;
cout <<str <<endl;
return 0;
}
输出结果是
one two three
one two
one two three four five
字符串操作
size_type copy(charT* s, size_type n, size_type pos = 0) const
这个函数从string_view对象(而不是原来的字符串)中复制一个字符范围,以替换另一个字符串的第一个子字符串。下面的程序说明了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
char strB[] = "aaa bbb ccc ddd eee";
const char* str = "one two three four five";
string_view strV(str, 13);
cout <<strV <<endl;
strV.copy(strB, 3, 4);
cout <<strB <<endl;
cout <<str <<endl;
return 0;
}
输出结果是
one two three
two bbb ccc ddd eee
one two three four five
这里有两个独立的字符串。string_view不是一个独立的字符串。它是其中一个独立字符串的范围窗口。请注意,成员函数的第一个参数 "charT* s "是指一个字符数组,而不是一个常量的字符*。
int compare(basic_string_view s) const
两个string_view的字符数在这里被比较。如果使用成员函数compare()的那个数字更高,那么就会返回一个正数。如果数字相等,则返回0。如果采用成员函数的string_view对象的数字较低,则返回一个负数。下面的程序说明了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* strA = "one two three four five";
string_view strVA(strA, 13);
cout <<strVA <<endl;
const char* strB = "aaa bbb ccc ddd eee";
string_view strVB(strB, 11);
cout <<strVB <<endl;
int ret = strVB.compare(strVA);
cout <<ret <<endl;
return 0;
}
输出结果是
one two three
aaa bbb ccc
-14, at the author’s computer.
搜索
size_type find(const charT* s, size_type pos = 0) const
这个成员函数寻找第一个子串,s在相关的string_view中的出现。它返回找到的子串的第一个字符的索引。如果没有找到,则返回-1。第二个参数告诉我们搜索的开始位置(默认索引为0)。下面的程序说明了这一点。
#include <iostream>
#include <string_view>
using namespace std;
int main()
{
const char* str = "one two three four five";
string_view strV(str, 13);
cout <<strV <<endl;
const char* cs = "two";
int ret = strV.find(cs, 0);
cout <<ret <<endl;
return 0;
}
输出结果是
one two three
4
结论
string_view是原始字符串的一个范围的窗口视图。它不是一个字符的拷贝。字符是被引用的。有一个字符串视图类,字符串视图对象可以从该类中实例化出来。String-View有许多成员函数。上面已经解释了不同类别的基本成员函数。