STL std::string 的实用程序






4.21/5 (24投票s)
std::string 的实用函数。
STL std::string 的实用程序
很多程序员都熟悉各种字符串对象的操作,例如 length
、substring
、find
、charAt
、toLowerCase
、toUpperCase
、trim
、equalsIgnoreCase
、startsWith
、endsWith
、parseInt
、toString
、split
等等。
现在,如果您正在使用 STL 及其 string 类 std::string
,您将如何实现上述这些操作呢?
当然,std::string
提供了一些方法来实现上述部分操作。它们是:
length()
:获取字符串的长度。substr()
:获取字符串的子字符串。at()
/operator []
:获取字符串中指定位置的字符。find
/rfind()
:在字符串中向前/向后查找子字符串。find_first_of()
:查找第一个匹配指定字符集的字符。find_first_not_of()
:查找第一个不匹配指定字符集的字符。find_last_of()
:查找最后一个匹配指定字符集的字符。find_last_not_of()
:查找最后一个不匹配指定字符集的字符。
有关更多 std::string
方法,请参阅文档。
有些操作没有直接实现为 std::string
方法,但我们可以从 algorithm.h 中找到实现方法。当然,这些实现也使用了 std::string
的现有方法。
将字符串转换为大写/小写
std::transform(str.begin(), str.end(), str.begin(), tolower);
std::transform(str.begin(), str.end(), str.begin(), toupper);
有关 std::transform
函数的详细信息,请参阅文档。
修剪字符串两侧的空格
修剪左侧空格
string::iterator i;
for (i = str.begin(); i != str.end(); i++) {
if (!isspace(*i)) {
break;
}
}
if (i == str.end()) {
str.clear();
} else {
str.erase(str.begin(), i);
}
修剪右侧空格
string::iterator i;
for (i = str.end() - 1; ;i--) {
if (!isspace(*i)) {
str.erase(i + 1, str.end());
break;
}
if (i == str.begin()) {
str.clear();
break;
}
}
修剪两侧空格
先修剪左侧空格,再修剪右侧空格。从而实现两侧空格的修剪。
通过重复字符或子字符串创建字符串
如果您想通过重复子字符串来创建字符串,您必须使用循环来实现。
string repeat(const string& str, int n) {
string s;
for (int i = 0; i < n; i++) {
s += str;
}
return s;
}
但是,如果您只需要重复一个字符,std::string
有一个构造函数可以实现。
string repeat(char c, int n) {
return string(n, c);
}
忽略大小写比较
这很有趣。我们应该复制要比较的两个字符串。然后将它们全部转换为小写。最后,只需比较两个小写字符串。
Starts with 和 EndsWith
StartsWith
str.find(substr) == 0;
如果结果为 true
,则表示 str
以 substr
开头。
EndsWith
size_t i = str.rfind(substr);
return (i != string::npos) && (i == (str.length() - substr.length()));
如果结果为 true
,则表示 str
以 substr
结尾。
还有另一种方法可以做到。只需获取左侧或右侧的子字符串进行比较。因为我不想计算字符串长度是否足够,所以我使用了 find
和 rfind
来实现。
从字符串解析数字/布尔值
对于这些操作,atoi
、atol
以及其他一些 C 函数都可以。但我希望使用 C++ 的方式来实现。所以我选择了 std::istringstream
。该类在 sstream.h 中。
一个模板函数可以处理大部分情况,但不包括布尔值。
template<class T> parseString(const std::string& str) {
T value;
std::istringstream iss(str);
iss >> value;
return value;
}
模板函数可以将 0 解析为 false
,将其他数字解析为 true
。但它不能将 "false"
解析为 false
,也不能将 "true"
解析为 true
。所以我编写了一个特殊函数。
template<bool>
bool parseString(const std::string& str) {
bool value;
std::istringstream iss(str);
iss >> boolalpha >> value;
return value;
}
正如您所见,我将 std::boolalpha
标志传递给输入流,然后输入流就可以识别字面量 bool
值。
使用类似的方法解析十六进制字符串是可能的。这次我应该将 std::hex
标志传递给流。
template<class T> parseHexString(const std::string& str) {
T value;
std::istringstream iss(str);
iss >> hex >> value;
return value;
}
转换为字符串的例程
就像从字符串解析一样,我将使用 std::ostringstream
来从其他类型的值获取字符串。该类也在 sstream.h 中。这里展示了相关的三个函数。
template<class T> std::string toString(const T& value) {
std::ostringstream oss;
oss << value;
return oss.str();
}
string toString(const bool& value) {
ostringstream oss;
oss << boolalpha << value;
return oss.str();
}
template<class T> std::string toHexString(const T& value, int width) {
std::ostringstream oss;
oss << hex;
if (width > 0) {
oss << setw(width)
<< setfill('0');
}
oss << value;
return oss.str();
}
您注意到 setw
和 setfill
了吗?它们仍然是需要参数的标志。std::setw
允许流中的输出内容占据固定宽度。如果长度不够,默认情况下会用空格填充。std::setfill
用于更改填充字符。如果您想控制对齐,可以使用 std::left
和 std::right
标志。
哦,我忘了告诉您,setw
和 setfill
需要 iomanip.h 头文件。
分割和分词
我认为分割函数应该通过分词器来实现。所以我先编写了一个分词器。我们可以使用 find_first_of
和 find_first_not_of
方法来获取每个标记。下面是 Tokenizer
类的 nextToken
方法。
bool Tokenizer::nextToken(const std::string& delimiters) {
// find the start character of the next token.
size_t i = m_String.find_first_not_of(delimiters, m_Offset);
if (i == string::npos) {
m_Offset = m_String.length();
return false;
}
// find the end of the token.
size_t j = m_String.find_first_of(delimiters, i);
if (j == string::npos) {
m_Token = m_String.substr(i);
m_Offset = m_String.length();
return true;
}
// to intercept the token and save current position
m_Token = m_String.substr(i, j - i);
m_Offset = j;
return true;
}
完整的标记器可在源代码存档中找到。您可以从上面的链接下载。所有其他函数仍在源代码文件中。