按索引更改字符串
Change string by index
我是C++的初学者,目前正在使用字符串。
我的问题是为什么在编译我在下面提供的代码时,当我使用索引表示法时,我可以获取字符串的字符,但无法使用cout
获取字符串本身?
这是代码:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string original; // original message
string altered; // message with letter-shift
original = "abc";
cout << "Original : " << original << endl; // display the original message
for(int i = 0; i<original.size(); i++)
altered[i] = original[i] + 5;
// display altered message
cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;
cout << "altered : " << altered << endl;
return 0;
}
当我运行它时,字符串altered
中的字符通过以下行正确显示:
cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;
但是字符串本身没有显示以下行:
cout << "altered : " << altered << endl;
我想知道为什么会这样。
您尚未调整altered
字符串的大小以适应循环前original
字符串的长度,因此您的代码表现出未定义的行为:
altered[i] = original[i] + 5; // UB - altered is empty
要解决此问题,请在循环之前调整altered
大小:
altered.resize(original.size());
或使用std::string::operator+=
或类似方式附加到altered
:
altered += original[i] + 5;
这样,它可以在循环之前为空,它会自动调整自身大小以包含附加的字符。
<小时 />解释
UB 在这里发生的方式是,您成功地将数据写入静态数组,std::string
用于短字符串优化(std::string::operator[]
不检查您是否通过std::string::size()
访问此数组(,但std::string::size()
保持0
,以及std::string::begin() == std::string::end()
。
这就是为什么您可以单独访问数据(同样,使用 UB(:
cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;
但是cout << aligned
不会打印任何内容,考虑到简化operator<<
定义std::string
在功能上看起来像这样:
std::ostream &operator<<(std::ostream &os, std::string const& str)
{
for(auto it = str.begin(); it != str.end(); ++it) // this loop does not run
os << *it;
return os;
}
用一句话来说, std::string
不知道你对其底层数组做了什么,你的意思是字符串的长度会增加。
总而言之,<algoritm>
进行此转换的方法:
std::transform(original.begin(), original.end(),
std::back_inserter(altered), // or altered.begin() if altered was resized to original's length
[](char c)
{
return c + 5;
}
(必需的标头: <algorithm>
, <iterator>
(
在程序字符串中,altered
为空。它没有元素。因此,您不能像现在这样使用下标运算符来访问字符串中不存在的元素
altered[i] = original[i] + 5;
因此,您可以在字符串后附加新字符。有几种方法可以做到这一点。例如
altered.push_back( original[i] + 5 );
或
altered.append( 1, original[i] + 5 );
或
altered += original[i] + 5;
由于您可能不会对空字符串应用下标运算符来分配值,因此最好使用基于范围的 for 循环,因为实际上并未使用索引本身。例如
for ( char c : original ) altered += c + 5;
altered
的大小始终为零 - 通过使用索引,您尝试将值从original
复制到altered
altered
没有索引。正如 LogicStuff 所说,这是未定义的行为 - 它不会生成错误,因为当我们使用带有 std::string
的索引时,我们实际上是在std::string
上调用运算符来访问字符串的data
字段。使用[]
运算符在C++标准中定义为没有范围检查 - 这就是没有抛出错误的原因。访问索引的安全方法是使用 at(i)
方法:如果altered.size() <= i
,altered.at(i)
将抛出范围错误
但是,我将以此作为我的解决方案,因为它是一种"现代C++"方法(加上更短和完整的方法(。
这是我对上面给出的内容所做的替代方案:
string original = "abc";
string altered = original;
for (auto& c : altered) c += 5; // ranged for-loop - for each element in original, increase its value by 5
cout << altered << endl;
请注意代码的显着减少:-(
即使我按照LogicStuff的方式做,我仍然会这样做:
string original = "abc"
string altered = ""; // this is actually what an empty string should be initialised to.
for (auto c : original) altered += (c+5);
但是,我实际上不推荐这种方法,因为push_back()
和字符串追加/字符串连接的工作方式。在这个小例子中很好,但是如果original
是一个字符串,其中包含要解析的书籍的前 10 页怎么办?或者,如果它是一百万个字符的原始输入怎么办?然后,每次altered
的data
字段达到其限制时,都需要通过系统调用重新分配它,并复制altered
的内容并释放data
字段的先前分配。这是一个重要的性能障碍,相对于original
的大小而增长 - 这只是不好的做法。执行完整复制然后迭代,对复制的字符串进行必要的调整总是更有效。这同样适用于 std::vector
.
- 按字母顺序对C++问题中的子字符串索引进行分区
- C++ 中字符串向量的索引
- C++ - 成功打印超出范围的字符串索引
- 对于循环增量器不能用作字符串向量的索引
- 如何检索由带通配符的字符串索引的对象
- 访问包含P的有效索引时返回空格的C++字符串
- 在我的 for 循环中,如何将索引更改为字符串?
- 有效地从文本文件中读取带有字符串索引的大型二维数组(矩阵)
- 如何使用调整后的字符串索引第二次通过循环运行字符串
- C++中的字符串索引问题
- Qt5.5 Q奇数结果的字符串索引
- 是否有理由使用字符串 => 索引到向量的映射,而不是字符串 => 对象?
- 在数组 C++ 中搜索字符串索引
- C++:字符串索引和保留标点符号时出现问题
- C++中类似python的字符串索引
- 如何在c++中高效地实现PHP字符串索引数组
- 如果访问了错误的字符串索引,c++不会抛出错误
- C++中的 Unicode 字符串索引
- 如何按索引为 c++ 字符串索引赋值
- 在 C++ 中使用字符串索引的整数