如何将所有出现的子字符串替换为另一个字符串,对于
std::string
s?
std::string s ("One hello, two hellos.");
s = s.replace("hello", "world"); // something like this
boost::replace_all
:
#include <boost/algorithm/string.hpp> // include Boost, a C++ library
...
std::string target("Would you like a foo of chocolate. Two foos of chocolate?");
boost::replace_all(target, "foo", "bar");
为什么不实现自己的替换?
void myReplace(std::string& str,
const std::string& oldStr,
const std::string& newStr)
{
std::string::size_type pos = 0u;
while((pos = str.find(oldStr, pos)) != std::string::npos){
str.replace(pos, oldStr.length(), newStr);
pos += newStr.length();
}
}
regex_replace
: 来完成此操作
#include <string>
#include <regex>
using std::string;
string do_replace( string const & in, string const & from, string const & to )
{
return std::regex_replace( in, std::regex(from), to );
}
string test = "Remove all spaces";
std::cout << do_replace(test, " ", "") << std::endl;
输出:
Removeallspaces
为什么不返回修改后的字符串?
std::string ReplaceString(std::string subject, const std::string& search,
const std::string& replace) {
size_t pos = 0;
while((pos = subject.find(search, pos)) != std::string::npos) {
subject.replace(pos, search.length(), replace);
pos += replace.length();
}
return subject;
}
如果您需要性能,这里有一个修改输入字符串的优化函数,它不会创建字符串的副本:
void ReplaceStringInPlace(std::string& subject, const std::string& search,
const std::string& replace) {
size_t pos = 0;
while((pos = subject.find(search, pos)) != std::string::npos) {
subject.replace(pos, search.length(), replace);
pos += replace.length();
}
}
测试:
std::string input = "abc abc def";
std::cout << "Input string: " << input << std::endl;
std::cout << "ReplaceString() return value: "
<< ReplaceString(input, "bc", "!!") << std::endl;
std::cout << "ReplaceString() input string not changed: "
<< input << std::endl;
ReplaceStringInPlace(input, "bc", "??");
std::cout << "ReplaceStringInPlace() input string modified: "
<< input << std::endl;
输出:
Input string: abc abc def
ReplaceString() return value: a!! a!! def
ReplaceString() input string not modified: abc abc def
ReplaceStringInPlace() input string modified: a?? a?? def
我的模板化内联就地查找和替换:
template<class T>
int inline findAndReplace(T& source, const T& find, const T& replace)
{
int num=0;
typename T::size_t fLen = find.size();
typename T::size_t rLen = replace.size();
for (T::size_t pos=0; (pos=source.find(find, pos))!=T::npos; pos+=rLen)
{
num++;
source.replace(pos, fLen, replace);
}
return num;
}
它返回替换的项目数的计数(如果您想连续运行此操作等,则可以使用)。使用方法:
std::string str = "one two three";
int n = findAndReplace(str, "one", "1");
std::string::replace
,这需要重复覆盖字符串,导致性能不佳。相反,它使用 std::string
缓冲区,以便字符串的每个字符仅遍历一次:
void replace_all(
std::string& s,
std::string const& toReplace,
std::string const& replaceWith
) {
std::string buf;
std::size_t pos = 0;
std::size_t prevPos;
// Reserves rough estimate of final size of string.
buf.reserve(s.size());
while (true) {
prevPos = pos;
pos = s.find(toReplace, pos);
if (pos == std::string::npos)
break;
buf.append(s, prevPos, pos - prevPos);
buf += replaceWith;
pos += toReplace.size();
}
buf.append(s, prevPos, s.size() - prevPos);
s.swap(buf);
}
用途:
replace_all(s, "text to replace", "new text");
#include <iostream>
void replace_all(
std::string& s,
std::string const& toReplace,
std::string const& replaceWith
) {
std::string buf;
std::size_t pos = 0;
std::size_t prevPos;
// Reserves rough estimate of final size of string.
buf.reserve(s.size());
while (true) {
prevPos = pos;
pos = s.find(toReplace, pos);
if (pos == std::string::npos)
break;
buf.append(s, prevPos, pos - prevPos);
buf += replaceWith;
pos += toReplace.size();
}
buf.append(s, prevPos, s.size() - prevPos);
s.swap(buf);
}
int main() {
std::string s("hello hello, mademoiselle!");
replace_all(s, "hello", "bye");
std::cout << s << std::endl;
}
输出:
bye bye, mademoiselle!
std::ostringstream
,这有一些开销。最新版本使用 std::string::append
代替,如 @LouisGo 推荐。
最简单的方法(提供接近您所写的内容)是使用Boost.Regex,特别是regex_replace。
std::string 内置了 find() 和 Replace() 方法,但它们使用起来比较麻烦,因为它们需要处理索引和字符串长度。
我相信这会起作用。 它采用 const char*'s 作为参数。
//params find and replace cannot be NULL
void FindAndReplace( std::string& source, const char* find, const char* replace )
{
//ASSERT(find != NULL);
//ASSERT(replace != NULL);
size_t findLen = strlen(find);
size_t replaceLen = strlen(replace);
size_t pos = 0;
//search for the next occurrence of find within source
while ((pos = source.find(find, pos)) != std::string::npos)
{
//replace the found string with the replacement
source.replace( pos, findLen, replace );
//the next line keeps you from searching your replace string,
//so your could replace "hello" with "hello world"
//and not have it blow chunks.
pos += replaceLen;
}
}
#include <string>
using std::string;
void myReplace(string& str,
const string& oldStr,
const string& newStr) {
if (oldStr.empty()) {
return;
}
for (size_t pos = 0; (pos = str.find(oldStr, pos)) != string::npos;) {
str.replace(pos, oldStr.length(), newStr);
pos += newStr.length();
}
}
检查 oldStr 是否为空很重要。如果出于某种原因该参数为空,您将陷入无限循环。
但是,如果可以的话,请使用久经考验的 C++11 或 Boost 解决方案。
// Replace all occurrences of searchStr in str with replacer
// Each match is replaced only once to prevent an infinite loop
// The algorithm iterates once over the input and only concatenates
// to the output, so it should be reasonably efficient
std::string replace(const std::string& str, const std::string& searchStr,
const std::string& replacer)
{
// Prevent an infinite loop if the input is empty
if (searchStr == "") {
return str;
}
std::string result = "";
size_t pos = 0;
size_t pos2 = str.find(searchStr, pos);
while (pos2 != std::string::npos) {
result += str.substr(pos, pos2-pos) + replacer;
pos = pos2 + searchStr.length();
pos2 = str.find(searchStr, pos);
}
result += str.substr(pos, str.length()-pos);
return result;
}
这是我的纯C++标准库replace_all实现,重点是尽可能获得最佳性能。必要时最多可进行 1 次重新分配。代码注释得很好。
///
/// string replace all pattern occurences with replacement
/// returns number of performed replaces
/// assert() may be replaced with any kind of error reporting
/// may be easy changed to pure C implementation
/// NOTE: result of string_replace_all("xxx", "xx", "XX") is undefined, may ends with: "XXx" or "xXX" (for #2b)
///
size_t string_replace_all(std::string& s, const char* pattern, size_t pattern_len, const char* replacewith, size_t replacewith_len) {
if (s.empty()) return 0; // no space to search
if (pattern_len == 0) return 0; // nothing to find
// sanity checks:
assert(pattern != nullptr);
if (replacewith_len != 0) assert(replacewith != nullptr);
size_t l = s.size(), founds = 0; // string size, number of performed replaces
const char* const pattern_end = pattern + pattern_len;
char* it2, *it = &s.front(); // ,processed string first character
char* dst, *end = it + l; // ,end of processed string
// I hope nobody has a such idea, but of course...
assert(pattern_end <= it || pattern >= end); // ...pattern can't be the part of the string
if (replacewith_len != 0)
assert(replacewith + replacewith_len <= it || replacewith >= end); // ...replacewith can't be the part of the string
if (replacewith_len > pattern_len) { // #1 possible growing:
// 1st pass - compute grow size:
while ((it = std::search(it, end, pattern, pattern_end)) != end) {
it += pattern_len;
++founds;
}
if (founds == 0) return 0;
size_t osz = l, nsz = l + founds * (replacewith_len - pattern_len); // old size, compute new size
// 2nd pass:
if (nsz > s.capacity()) { // #1a realloaction needed
std::string s2; // it's better to allocate new string and just write from the beginging
s2.reserve(nsz); // allocate enough memory
it = &s.front(); // rewind 'it' (as source)
it2 = std::search(it, end, pattern, pattern_end);
assert(it2 != end); // first found must exists
do {
s2.append(it, it2 - it); // append [it, it2) unchanged
s2.append(replacewith, replacewith_len); // append replacement in the pattern place
it = it2 + pattern_len; // jump over found pattern
}
while ((it2 = std::search(it, end, pattern, pattern_end)) != end);
s2.append(it, end - it); // extend with fragment after last found
assert(s2.size() == nsz); // to make sure
std::swap(s, s2); // s2 becomes s and original s will be free
}
else { // #1b no reallocation needed:
s.resize(nsz); // extending size with null bytes - should not reallocate
end = &s.front() + nsz; // expected string end after 2nd pass
char* oend = &s.front() + osz; // old end / real current string end
it = std::find_end(&s.front(), oend, pattern, pattern_end);
assert(it != oend); // first found must exists
do {
// first move following (after pattern) fragment to the right:
char* b = it + pattern_len; // fragment begin
l = oend - b; // fragment length
end -= l; // moving destination
std::char_traits<char>::move(end, b, l); // move right: b -> end
// then insert replacement:
end -= replacewith_len;
std::char_traits<char>::copy(end, replacewith, replacewith_len);
oend = it;
}
while ((it = std::find_end(&s.front(), oend, pattern, pattern_end)) != oend);
}
}
else if (replacewith_len < pattern_len) { // #2 possible shrinking:
it = std::search(it, end, pattern, pattern_end);
if (it == end) return 0;
std::char_traits<char>::copy(it, replacewith, replacewith_len); // paste replacement on the first pattern found
dst = it + replacewith_len; // destination of the following fragments
it += pattern_len; // continue from pattern end
++founds;
while ((it2 = std::search(it, end, pattern, pattern_end)) != end) {
// first move preceding (before this and after previous) pattern to the dest:
l = it2 - it;
std::char_traits<char>::move(dst, it, l); // move left: dst <- it
dst += l;
// then insert replacement:
std::char_traits<char>::copy(dst, replacewith, replacewith_len);
dst += replacewith_len;
it = it2 + pattern_len; // continue from pattern end
++founds;
}
l = end - it;
std::char_traits<char>::move(dst, it, l); // move fragment after last pattern
dst += l;
s.resize(dst - &s.front()); // update string size (to lower)
}
else { // #3 - replacing without size affection:
// detect: pattern == replacewith
if (std::char_traits<char>::compare(pattern, replacewith, pattern_len) == 0) return 0;
// perform simple replacing:
while ((it = std::search(it, end, pattern, pattern_end)) != end) {
std::char_traits<char>::copy(it, replacewith, replacewith_len);
it += replacewith_len;
++founds;
}
}
return founds;
}
// std::string arguments overload
inline size_t string_replace_all(std::string& s, const std::string& pattern, const std::string& replacewith) {
return string_replace_all(s, pattern.data(), pattern.size(), replacewith.data(), replacewith.size());
}
// null termiated arguments overload
inline size_t string_replace_all(std::string& s, const char* pattern, const char* replacewith) {
assert(pattern != nullptr);
return string_replace_all(s, pattern, std::strlen(pattern), replacewith, (replacewith != nullptr) ? std::strlen(replacewith) : 0);
}
测试
void make_test(const char* str, const char* p, const char* r, const char* expect, std::string& target) {
target = str;
size_t c = string_replace_all(target, p, r);
std::printf("string_replace_all('%s', '%s', '%s') => %i '%s' %s\n", str, p, r, (int)c, target.c_str(), target == expect ? "OK" : "FAIL!");
}
void make_test(const char* str, const char* p, const char* r, const char* expect) {
std::string target;
make_test(str, p, r, expect, target);
}
int main() {
std::string reserved;
reserved.reserve(128);
make_test("abc", "x", "XX", "abc"); // #1 not found
make_test("abc", "x", "", "abc"); // #2 not found
make_test("abc", "x", "X", "abc"); // #3 not found
make_test("axbcxdxxefxgxh", "x", "XXXX", "aXXXXbcXXXXdXXXXXXXXefXXXXgXXXXh"); // #1a middle
make_test("xaxbcdxefxgxhx", "x", "XXXX", "XXXXaXXXXbcdXXXXefXXXXgXXXXhXXXX"); // #1a edges
make_test("abxdxefgxhixj", "x", "XX", "abXXdXXefgXXhiXXj", reserved); // #1b middle
make_test("xxabxxcdxexfx", "x", "XX", "XXXXabXXXXcdXXeXXfXX", reserved); // #1b edges
make_test("abxxdexxxxgh", "xx", "X", "abXdeXXgh"); // #2 middle
make_test("xxabxxxxcdxxefxx", "xx", "X", "XabXXcdXefX"); // #2 edges
make_test("abxxxcdxexf", "x", nullptr, "abcdef"); // #2 middle remove
make_test("xxabxxxcdxefxx", "x", nullptr, "abcdef"); // #2 edges remove
make_test("xxxxxxxxxxxxxxx", "x", nullptr, ""); // #2 remove all
make_test("abxxcxdexxxxf", "xx", "XX", "abXXcxdeXXXXf"); // #3 middle
make_test("xxabxxxcxxxxx", "xx", "XX", "XXabXXxcXXXXx"); // #3 edges
make_test("abxxcd", "x", "x", "abxxcd"); // #3 equals
}
输出:
string_replace_all('abc', 'x', 'XX') => 0 'abc' OK
string_replace_all('abc', 'x', '') => 0 'abc' OK
string_replace_all('abc', 'x', 'X') => 0 'abc' OK
string_replace_all('axbcxdxxefxgxh', 'x', 'XXXX') => 6 'aXXXXbcXXXXdXXXXXXXXefXXXXgXXXXh' OK
string_replace_all('xaxbcdxefxgxhx', 'x', 'XXXX') => 6 'XXXXaXXXXbcdXXXXefXXXXgXXXXhXXXX' OK
string_replace_all('abxdxefgxhixj', 'x', 'XX') => 4 'abXXdXXefgXXhiXXj' OK
string_replace_all('xxabxxcdxexfx', 'x', 'XX') => 7 'XXXXabXXXXcdXXeXXfXX' OK
string_replace_all('abxxdexxxxgh', 'xx', 'X') => 3 'abXdeXXgh' OK
string_replace_all('xxabxxxxcdxxefxx', 'xx', 'X') => 5 'XabXXcdXefX' OK
string_replace_all('abxxxcdxexf', 'x', '(null)') => 5 'abcdef' OK
string_replace_all('xxabxxxcdxefxx', 'x', '(null)') => 8 'abcdef' OK
string_replace_all('xxxxxxxxxxxxxxx', 'x', '(null)') => 15 '' OK
string_replace_all('abxxcxdexxxxf', 'xx', 'XX') => 3 'abXXcxdeXXXXf' OK
string_replace_all('xxabxxxcxxxxx', 'xx', 'XX') => 4 'XXabXXxcXXXXx' OK
string_replace_all('abxxcd', 'x', 'x') => 0 'abxxcd' OK