[英]String replacement in C++ on string of arbitrary length
我有一個從ostringstream
獲得的字符串。 我目前正在嘗試替換此字符串中的某些字符( content.replace(content.begin(), content.end(), "\\n", "");
)),但有時會出現異常:
malloc: *** mach_vm_map(size=4294955008) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
std::bad_alloc
我懷疑發生這種情況是因為字符串太大。 這些情況下的最佳做法是什么? 聲明堆上的字符串?
更新資料
我的完整方法:
xml_node HTMLDocument::content() const {
xml_node html = this->doc.first_child();
xml_node body = html.child("body");
xml_node section = body.child("section");
std::ostringstream oss;
if (section.type() != xml_node_type::node_null) {
section.print(oss);
} else {
body.print(oss);
}
string content;
content = oss.str();
content.replace(content.begin(), content.end(), "<section />", "<section></section>");
content.replace(content.begin(), content.end(), "\t", "");
xml_node node;
return node;
}
沒有std::string::replace
成員函數的重載接受一對迭代器,一個const char*
進行搜索並const char*
用作替換,這是你的問題來自:
content.replace(content.begin(), content.end(), "\n", "");
匹配以下重載:
template <class InputIterator>
string& replace(iterator i1, iterator i2,
InputIterator first, InputIterator last);
也就是說, "\\n"
和""
被視為范圍<first; last)
<first; last)
,這取決於它們具有的地址,是否會使程序崩潰。
您必須使用std::regex
或實現自己的邏輯,該邏輯遍歷std::string
並用替換std::string
替換所有遇到的模式。
這些行:
content.replace(content.begin(), content.end(), "<section />", "<section></section>");
content.replace(content.begin(), content.end(), "\t", "");
導致未定義的行為。 它們與功能匹配:
template<class InputIterator>
std::string& std::string::replace(
const_iterator i1, const_iterator i2,
InputIterator j1, InputIterator j2);
使用InputIterator
解析為char const*
。 問題在於兩個迭代器之間的距離以及是否可以從第一個迭代器到達第二個迭代器是不確定的,因為它們指向完全無關的內存位。
從您的代碼中,我認為您不了解std::string::replace
功能。 它將字符串中的范圍[i1,i2)
替換為范圍[j1,j2)
定義的文本。 它沒有做任何搜索和比較; 找到需要更換的范圍后 ,即可使用。 致電:
content.replace(content.begin(), content.end(), "<section />", "<section></section>");
具有與以下功能完全相同的效果:
content = std::string( "<section />", "<section></section>");
,這當然不是您想要的。
在C ++ 11中,有一個regex_replace
函數可能有用,盡管如果您確實在非常大的字符串上執行此操作,則它可能並不是最有效的(正則表達式的靈活性增加了代價); 我可能會使用類似:
std::string
searchAndReplace(
std::string const& original,
std::string const& from,
std::string const& to)
{
std::string results;
std::string::const_iterator current = original.begin();
std::string::const_iterator end = original.end();
std::string::const_iterator next = std::search( current, end, from.begin(), from.end() );
while ( next != end ) {
results.append( current, next );
results.append( to );
current = next + from.size();
next = std::search( current, end, from.begin(), from.end() );
}
results.append( current, next );
return results;
}
對於非常大的字符串,還可以使用一些試探法來猜測大小,然后對results
進行reserve
,這也是一個好主意。
最后,由於第二行只是刪除了'\\t'
,因此最好使用std::remove
:
content.erase( std::remove( content.begin(), content.end(), '\t' ), content.end() );
如果AFAIK stl字符串超過某個(較小)大小(例如, 在Visual Studio中為32個字符 ),則始終在堆上分配它們
如果遇到分配異常,該怎么辦:
錯誤的分配可能並不意味着您已用完內存,更可能是連續內存已用完。 繩索類可能會更適合您,因為它會在內部逐段分配字符串。
如果要進行復制並保持原樣不變,這是從字符串中刪除字符的正確(且相當有效)的方法之一:
#include <algorithm>
#include <string>
std::string delete_char(std::string src, char to_remove)
{
// note: src is a copy so we can mutate it
// move all offending characters to the end and get the iterator to last good char + 1
auto begin_junk = std::remove_if(src.begin(),
src.end(),
[&to_remove](const char c) { return c == to_remove; });
// chop off all the characters we wanted to remove
src.erase(begin_junk,
src.end());
// move the string back to the caller's result
return std::move(src);
}
這樣稱呼:
std::string src("a\nb\bc");
auto dest = delete_char(src, '\n');
assert(dest == "abc");
如果您希望在適當位置修改字符串,則只需:
src.erase(std::remove_if(src.begin(), src.end(), [](char c) { return c == '\n'; }), src.end());
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.