2014-09-29 91 views
0

我有一个字符串,我从ostringstream得到。我目前正试图在此字符串(content.replace(content.begin(), content.end(), "\n", "");)来代替某些字符,但有时我得到一个异常:C++字符串替换字符串的任意长度

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; 
} 
+1

如果你正在寻找的帮助与此特定问题,我想你会需要提供一个[最小的,可验证的和完整的例子](http://stackoverflow.com/help/mcve) – Yann 2014-09-29 14:48:00

+1

有一个很好的机会,错误与这段代码无关。你有没有试过用valgrind跑这个? – dasblinkenlight 2014-09-29 14:48:31

+0

我无法在OSX上运行valgrind。 – ruipacheco 2014-09-29 14:51:22

回答

1

没有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)他们有,崩溃你的程序或不。

您必须使用std::regex或实现自己的逻辑,该逻辑通过std::string进行迭代,并用替换字符串替换任何遇到的模式。

+0

他可以使用[替换方法从算法](http://www.cplusplus.com/reference/algorithm/replace/),它有2个迭代器,一个旧的和新的char参数。 – gbjbaanb 2014-09-29 14:56:56

+0

@gbjbaanb:不,没有*空字符*'''',OP也试图替换整个字符串 – 2014-09-29 14:57:57

+0

啊是的 - 标题说替换字符,但代码说删除它们。所以马虎:) – gbjbaanb 2014-09-29 15:00:02

0

AFAIK STL串总是在堆上分配的,如果他们去了一定的(小)大小,例如32 chars in Visual Studio

什么你可以做,如果你得到分配例外:

  • 使用自定义分配器
  • 使用“rope”类。

错误的分配可能并不意味着你的内存不足,更可能是你的连续内存不足。一个绳索类可能更适合你,因为它在内部分配了字符串。

0

这是正确的(和合理有效)的方式,如果你想打副本从一个字符串中删除字符,并保持原来的不变之一:

#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()); 
+0

为什么'std :: remove_if',而不是简单的'std :: remove'? – 2014-09-29 16:06:54

+0

为什么不呢?有很多正确的方法来皮肤C++猫。 – 2014-09-29 16:10:27

+0

但最简单的通常是最好的。当有一个功能已经完成所需要的功能时,在这里介绍一个lambda表达式是不必要的复杂化。 – 2014-09-29 16:13:31

1

的线条:

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这样做。它用范围[j1,j2)定义的文本替换[i1,i2)的范围 。它 确实不是做任何搜索和比较;在 之后使用您已找到需要更换的范围。呼唤:

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());