如何使用std::string将所有出现的一个字符替换为两个字符
How to replace all occurrences of one character with two characters using std::string?
有没有一种简单的方法可以用"/"
替换std::string
中所有出现的"/"
,以转义std::string
中的所有斜杠?
可能最简单的方法是使用boost字符串算法库。
boost::replace_all(myString, "/", "\/");
std::string result = boost::replace_all_copy(myString, "/", "\/");
答案是否定的……如果你指的是标准库已经提供的一行程序,那么就没有"简单"的方法。然而,实现该功能并不困难。
首先,我认为您可能还需要将替换为
\
和其他特殊字符。在这种情况下,使用ildjarn提供的replaceAll
实现会很烦人(您需要多次替换相同的字符串(。
在我看来,在字符串处理的许多情况下,没有什么比使用显式char *
方法更好的了。然而,在这种特定情况下,可能只使用索引就可以了:
std::string escape(const std::string& s)
{
int n = s.size(), wp = 0;
std::vector<char> result(n*2);
for (int i=0; i<n; i++)
{
if (s[i] == '/' || s[i] == '')
result[wp++] = '';
result[wp++] = s[i];
}
return std::string(&result[0], &result[wp]);
}
基本上,这个想法是在字符串上移动,并在任何特殊字符之前添加一个额外的字符(在上面的文章中,我刚刚处理了
/
和,但你已经明白了(。已知结果的长度最大为
2*n
,因此我预先分配它,使整个处理为O(n((replaceAll
方法保持将字符串的其余部分向右移动,使其为O(n^2((。即使对于像"this is a test with /slashes/ that should be /escaped/"
这样的短字符串,即使只调用replaceAll
一次并在escape
中处理两个特殊字符,上述函数在我的PC上也更高效(速度为1.3倍(。
还要注意的是,这个函数自然会返回一个单独的字符串,而不是原地修改字符串(IMO是一个更好的接口(,在时间比较中,我必须为每个调用创建一个字符串,这样由于增加了恒定时间,结果甚至会朝着相等的方向移动。
上述读/写方法也可以很容易地扩展到更复杂的替换(例如,用>
替换>
或用%xx
编码替换不在可打印范围内的字符(,仍然对大字符串保持良好的效率(仅一次通过(。
cppreference.com std::string::replace
页面上给出了如何执行此操作的示例:
std::string& replaceAll(std::string& context, std::string const& from, std::string const& to)
{
std::size_t lookHere = 0;
std::size_t foundHere;
while((foundHere = context.find(from, lookHere)) != std::string::npos)
{
context.replace(foundHere, from.size(), to);
lookHere = foundHere + to.size();
}
return context;
}
std::string::replace
用另一个子字符串替换字符串中出现的所有子字符串:
#include <iostream>
void replace_all(std::string& input, const std::string& from, const std::string& to) {
size_t pos = 0;
while ((pos = input.find(from, pos)) != std::string::npos) {
input.replace(pos, from.size(), to);
pos += to.size();
}
}
int main() {
std::string str("i am a geek/nerd/crazy person.");
replace_all(str, "/", "\/");
std::cout << str << 'n';
}
输出:
$ g++-6.1.0 -std=c++17 -g -Og -Werror -Wall -Wextra -pedantic -Wold-style-cast -Wnon-virtual-dtor -Wshadow -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wmisleading-indentation -fsanitize=address,leak,undefined; ./a.out
i am a geek/nerd/crazy person.
我推断了这个问题,制作了一个流式实现,允许您转义各种字符。
流式处理对于大容量来说真的很麻烦[1],因为否则你会陷入堆碎片/性能地狱。此外,这允许您转义存储在几乎任何源中的字符串,因为样本确实显示
查看Coliru直播
#include <iostream>
#include <iterator>
#include <set>
#include <sstream>
#include <string>
template <class _II, class _OI>
static _OI escapeSomeChars(const _II inIt, const _II endIt, _OI outIt)
{
for (_II it=inIt; it!=endIt; ++it)
switch (*it)
{
case ' ': outIt++ = ''; outIt++ = '0'; break;
case 'n': outIt++ = ''; outIt++ = 'n'; break;
case '':
case '"' :
case '$' :
case '/' : outIt++ = '';
default : outIt++ = *it;
}
return outIt;
}
static std::string escapeSomeChars(const std::string& input)
{
std::ostringstream os;
escapeSomeChars(input.begin(), input.end(), std::ostream_iterator<char>(os));
return os.str();
}
namespace /*anon*/ {
struct rawchar { // helper - see e.g. http://bytes.com/topic/c/answers/436124-copy-istream_iterator-question
char _c; rawchar(char c=0) : _c(c) {}
operator const char&() const { return _c; }
friend std::istream& operator>>(std::istream& is, rawchar& out) { return is.get(out._c); }
};
}
int main()
{
static const char data[] = ""I will \$one day \have \all \\my slash\es escaped, much \like\ in the source!n"";
// use the overload for std::string
std::cout << escapeSomeChars(data);
std::cout << std::endl;
// streaming in & out:
std::istringstream is(data);
escapeSomeChars(std::istream_iterator<rawchar>(is), std::istream_iterator<rawchar>(), std::ostream_iterator<char>(std::cout));
std::cout << std::endl;
// but you don't need an istream, you can use any STL iterator range
escapeSomeChars(data, data+sizeof(data)/sizeof(data[0]), std::ostream_iterator<char>(std::cout));
std::cout << std::endl;
// but any source and target will do:
std::string asstring(data);
std::set<char> chars(asstring.begin(), asstring.end());
asstring.clear();
escapeSomeChars(chars.begin(), chars.end(), std::back_inserter(asstring));
std::cout << "Unique characters in data: '" << asstring << "', but properly escaped!" << std::endl;
return 0;
}
我选择了一个开关,因为它将由编译器进行优化。对于可转义字符的动态集,我更喜欢某种查找(带有std::find的向量就可以了,尽管对于大型集,带有set::find的std::set会成为更好的选择(。
希望这能帮助
[1] 例如,请参阅我最近遇到的这个漂亮的错误:GParted:简化的cleanup_cursor((实现
- 为什么 sscanf 无法从一个字符串中读取uint64_t和字符?
- 如何将一个ostringstream十六进制字符串字符对转换为单个unit8t等价的二进制值
- 如何检查一个c++字符串中有多少相同的字符/数字
- 使用.find函数在c++中查找字符和另一个字符之间的大小
- 我的目标是编写一个程序来计算和存储字符串在字符数组中出现的位置
- 关于字符数组,我正在尝试将数组中的每个字符分配给另一个值
- 我已经建立了递归关系,它找到了两个字符串之间最长的连续公共字符串,我怎么能跳过其中一个字符串中的一个字符
- 使用动态分配将 char* 复制到另一个字符**
- 将字符向量复制到另一个向量
- 如何将一个结构的字符数组复制到结构的另一个字符数组中?
- 如何在不知道对应关系的情况下在字符串中搜索字符并将其分配给另一个字符?
- 计算在同一位置至少包含一个常用字符的不同字符串对
- 如果我们在其中输入一个整数,则字符会给出整数作为输出,但是当分配给它一个整数时,这不会发生。为什么?
- 如何检查一个字符是否与字符数组中的另一个字符匹配?
- C++:使用另一个字符将一个字符大写
- 我可以得到一个字符 * 到一个 std::sregex_iterator 匹配 str() 吗?
- C ++读取用户输入的字符一个一个符号
- 遍历数组或从打开的文件中获取字符 - 一个比另一个有什么优势
- 将文本文件的内容一个字符一个字符地读入矢量,不跳过空白或新行
- 这是更快和更有效的,处理一个字符一个字符作为字符或流