C++ 编程/代码/IO/流/字符串
字符串类是 C++ 标准库的一部分,用于方便地操作字符序列,以取代处理字符串的静态、不安全的 C 方法。要在程序中使用字符串类,必须包含 <string> 头文件。标准库字符串类可以通过 std 命名空间 访问。
基本模板类是 basic_string<>
,其标准特化是 string
和 wstring
。
声明一个 std 字符串可以通过以下两种方法之一完成
using namespace std;
string std_string;
or
std::string std_string;
本节只处理键盘和文本输入。还有许多其他输入可以读取(鼠标移动和按钮点击等),但这些内容在本节中不会涉及,即使是读取键盘上的特殊键也会被排除在外。
也许字符串类最基本的使用方式是读取用户的文本并将其写入屏幕。在头文件iostream中,C++ 定义了一个名为 cin 的对象,它处理输入的方式与 cout 处理输出的方式非常相似。
// snipped designed to get an integer value from the user
int x;
std::cin >> x;
>> 运算符将导致执行停止并等待用户输入。如果用户输入有效的整数,它将被转换为整数值并存储在 x 中。
如果用户输入的不是整数,编译器不会报错。相反,它会在x中保留旧内容(一个“随机”的无意义值)并继续执行。
然后可以将其扩展到以下程序
#include <iostream>
#include <string>
int main(){
std::string name;
std::cout << "Please enter your first name: ";
std::cin >> name;
std::cout << "Welcome " << name << "!" << std::endl;
return 0;
}
虽然字符串可以包含任何字符的序列,包括空格和空字符,但在使用 cin 和提取运算符(>>)将内容读入字符串时,只会存储第一个空格之前的字符。或者,如果需要整行文本,可以使用 getline 函数
std::getline(std::cin, name);
幸运的是,有一种方法可以检查输入语句是否成功。我们可以调用 cin 上的 good 函数来检查所谓的流状态。good 返回一个 bool 值:如果为 true,则上次输入语句成功。如果不是,我们知道之前的某个操作失败了,而且下一个操作也会失败。
因此,从用户获取输入可能看起来像这样
#include <iostream>
using namespace std;
int main ()
{
int x;
// prompt the user for input
cout << "Enter an integer: ";
// get input
cin >> x;
// check and see if the input statement succeeded
if (cin.good() == false) {
cout << "That was not an integer." << endl;
return -1;
}
// print the value we got from the user
cout << x << endl;
return 0;
}
cin 也可以用来输入字符串
string name;
cout << "What is your name? ";
cin >> name;
cout << name << endl;
与标准 C 库中的 scanf() 函数一样,此语句只获取输入的第一个单词,并将剩余的单词留给下一个输入语句。因此,如果您运行此程序并输入您的全名,它只会输出您的名字。
您可能还会注意到 >> 运算符没有按预期处理错误(例如,如果您在数字提示中不小心输入了您的姓名)。由于这些问题,读取一行文本并使用该行作为输入可能更合适 - 这是使用名为 getline 的函数来执行的。
string name;
cout << "What is your name? ";
getline (cin, name);
cout << name << endl;
getline 的第一个参数是 cin,它表示输入来自哪里。第二个参数是您要存储结果的字符串变量的名称。
getline 会读取整行,直到用户按下回车键或 Enter 键。这对于输入包含空格的字符串很有用。
事实上,getline 通常用于获取任何类型的输入。例如,如果您想让用户输入一个整数,您可以输入一个字符串,然后检查它是否是一个有效的整数。如果是,则可以将其转换为整数值。如果不是,您可以打印一条错误消息并要求用户重试。
要将字符串转换为整数,可以使用在头文件 cstdlib 中定义的 strtol 函数。(请注意,与 strtol 相比,旧函数 atoi 并不安全,而且功能也不如 strtol。)
如果您仍然需要 >> 运算符的功能,则需要创建字符串流,如<sstream>中所述。此流的使用将在后面的章节中讨论。
我们将使用这个虚拟字符串作为我们一些示例。
string str("Hello World!");
这调用带有 const char*
参数的默认构造函数。默认构造函数创建一个不包含任何内容的字符串,即没有字符,甚至没有 '\0'
(但是 std::string 不是以空字符结尾的)。
string str2(str);
将触发复制构造函数。std::string
了解如何对它存储的字符进行深度复制。
string str2 = str;
这将使用赋值运算符复制字符串。此代码的效果与上面的示例中使用复制构造函数相同。
string::size_type string::size() const;
string::size_type string::length() const;
例如,您可以执行以下操作
string::size_type strSize = str.size();
string::size_type strSize2 = str2.length();
size()
和 length()
方法都返回字符串对象的大小。两者之间没有明显的区别。请记住,字符串中的最后一个字符是 size() - 1
,而不是 size()
。就像在 C 风格的字符串和数组中一样,std::string
从 0 开始计数。
ostream& operator<<(ostream &out, string &str);
istream& operator>>(istream &in, string &str);
移位运算符(>>
和 <<
)已被重载,因此您可以对 istream
和 ostream
对象执行 I/O 操作,最值得注意的是 cout
、cin
和文件流。因此,您可以像这样执行控制台 I/O
std::cout << str << endl;
std::cin >> str;
istream& getline (istream& in, string& str, char delim = '\n');
或者,如果您想一次读取整行,请使用 getline()
。请注意,这不是一个成员函数。getline()
将从输入流 in
中检索字符并将其分配给 str
,直到遇到 EOF
或 delim
。getline
将在追加数据之前重置输入字符串。delim
可以设置为任何 char
值,并用作通用分隔符。以下是一些示例用法
#include <fstream>
//open a file
std::ifstream file("somefile.cpp");
std::string data, temp;
while( getline(file, temp, '#')) //while data left in file
{
//append data
data += temp;
}
std::cout << data;
由于 getline
的工作方式(即它返回输入流),因此您可以嵌套多个 getline()
调用来获取多个字符串;但是,这可能会显著降低可读性。
char& string::operator[](string::size_type pos);
string
中的 Chars
可以使用重载的下标 ([]
) 运算符直接访问,就像在 char
数组中一样。
std::cout << str[0] << str[2];
打印 "Hl"。
std::string
支持从旧的 C 字符串类型 const char*
转换。你也可以将一个简单的 char
分配或追加到一个字符串中。将 char*
分配给 string
就像这样
str = "Hello World!";
如果你想逐个字符地进行,你也可以使用
str = 'H';
毫不奇怪,operator+
和 operator+=
也被定义了!你可以将另一个 string
、一个 const char*
或一个 char
追加到任何字符串中。
比较运算符 >, <, ==, >=, <=, !=
都对字符串执行比较操作,类似于 C 的 strcmp() 函数。这些返回一个真/假值。
if(str == "Hello World!")
{
std::cout << "Strings are equal!";
}
string::size_type string::find(string needle, string::size_type pos = 0) const;
你可以使用 find()
成员函数在另一个字符串中查找字符串的第一个出现位置。find()
将从位置 pos
开始在 this
中查找 needle
,并返回 needle
第一次出现的位置。例如
std::string haystack = "Hello World!";
std::string needle = "o";
std::cout << haystack.find(needle);
将简单地打印 "4",这是 str
中 "o" 第一次出现的位置索引。如果我们想要 "World" 中的 "o",我们需要修改 pos
指向第一个出现位置之后。str.find(find, 4)
将返回 4,而 str.find(find, 5)
将返回 7。如果子字符串未找到,find()
将返回 std::string::npos
。这个简单的代码在一个字符串中搜索所有 "wiki" 的出现位置,并打印它们的位置
std::string wikistr = "wikipedia is full of wikis (wiki-wiki means fast)";
for(string::size_type i = 0, tfind; (tfind = wikistr.find("wiki", i)) != string::npos; i = tfind + 1)
{
std::cout << "Found occurrence of 'wiki' at position " << tfind << std::endl;
}
string::size_type string::rfind(string needle, string::size_type pos = string::npos) const;
函数 rfind()
的工作原理类似,只是它返回传递字符串的最后出现位置。
string& string::insert(size_type pos, const string& str);
你可以使用 insert()
成员函数将另一个字符串插入到一个字符串中。例如
string newstr = " Human";
str.insert (5,newstr);
将返回 Hello Human World!
string& string::erase(size_type pos, size_type n);
你可以使用 erase()
从字符串中删除子字符串。例如
str.erase (5,6);
将返回 Hello!
string& string::substr(size_type pos, size_type n);
你可以使用 substr()
从字符串中提取子字符串。例如
string str = "Hello World!";
string part = str.substr(6,5);
将返回 World。
const char* string::c_str() const;
const char* string::data() const;
为了与只接受 char*
参数的 C/C++ 函数向后兼容,你可以使用成员函数 string::c_str()
和 string::data()
返回一个临时的 const char*
字符串,你可以将其传递给函数。这两个函数之间的区别是,c_str()
返回一个以 null 结尾的字符串,而 data()
不一定返回一个以 null 结尾的字符串。因此,如果你的旧函数需要一个以 null 结尾的字符串,请使用 c_str()
,否则使用 data()
(并且可能还要将字符串的长度作为参数传递)。
字符串可以通过简单地使用 + 运算符连接(追加)在一起。
string firstString = "Hello";
string secondString = " World!";
string finalString = firstString + secondString;
cout << finalString << endl;
这里的输出将是 "Hello World"
需要注意的是,除了 + 运算符或连接之外,还可以使用 .append(str2)
类成员函数将一个字符串连接到另一个字符串。str2
对象可以是字符串对象或 C 字符串。这将把括号中的字符串添加到调用 append
的字符串中。
还应该注意,append 函数可以用于在字符串中的特定字符位置追加字符串。如果程序员输入 str.append(str2, p, n)
,则从字符串 str2
中位置 p
开始的 n
个字符将被追加到 str
的末尾。例如,在以下代码中,有两个字符串。从第二个字符串中位置 8 开始的 5 个字符将被追加到第一个字符串 str
的末尾。
string str("Watch out for ");
string str2("Llamas, Bears, and Telemarketers!");
str.append(str2, 8, 5);
cout << str << endl;
上面的代码将在第一个字符串的末尾追加单词 Bears
,然后在屏幕上打印 Watch out for Bears
。
有时我们想将字符串转换为数字。为此,我们可以使用 stoi() 函数,它接受一个字符串作为参数并返回该值。
string exString1 = "12023";
string exString2 = "1.23249";
string exString3 = "1232 test";
要将这些字符串转换为数字,我们将字符串变量的 stoi 保存到一个整数变量中。
int exInt1 = stoi(exString1);
int exInt2 = stoi(exString2);
int exInt3 = stoi(exString3);
cout << "Before stoi string:" << exString1 << " and after stoi int:" << exInt1 << endl;
cout << "Before stoi string:" << exString2 << " and after stoi int: " << exInt2 << endl;
cout << "Before stoi string:" << exString3 << " and after stoi int:" << exInt3 << endl;
输出将是:Before stoi string :12023 and after stoi int: 12023 Before stoi string :1 and after stoi int: 1 Before stoi string :1232 and after stoi int: 1232
如果我们想做相反的事情,将一个整数转换为一个字符串,我们可以使用 to_string() 函数,它接受一个整数作为参数,并返回该整数作为字符串。
int exInt = 12023;
要将这个整数转换为字符串,我们调用 to_string() 函数。整数变量作为参数传递给函数。然后该函数将返回该整数作为字符串,然后可以将其分配给一个字符串变量。
string exString = to_string(exInt);
cout << "Before to_string int:" << exInt << " and after to_string string:" << exString << endl;
输出将是:Before to_string int:12023 and after to_string string:12023
字符串只能追加到其他字符串,但不能追加到数字或其他数据类型,因此类似 std::string("Foo") + 5
这样的代码将不会产生一个内容为 "Foo5"
的字符串。要将其他数据类型转换为字符串,存在类 std::ostringstream
,它位于头文件 <sstream>
中。std::ostringstream
的行为与 std::cout
完全相同,唯一的区别是输出不会像操作系统提供的当前标准输出那样,而是输出到一个内部缓冲区中,该缓冲区可以通过 std::ostringstream::str()
方法转换为 std::string
。
#include <iostream>
#include <sstream>
int main()
{
std::ostringstream buffer;
// Use the std::ostringstream just like std::cout or other iostreams
buffer << "You have: " << 5 << " Helloworlds in your inbox";
// Convert the std::ostringstream to a normal string
std::string text = buffer.str();
std::cout << text << std::endl;
return 0;
}