2014-09-29 120 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());