0

我有一个从中得到的字符串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;
}
4

4 回答 4

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"并且""被视为 range <first; last),这取决于它们拥有的地址,是否会使您的程序崩溃。

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

于 2014-09-29T14:52:50.790 回答
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作用。[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;
}

对于非常大的字符串,猜测大小的一些启发式方法,然后进行reserveonresults可能也是一个好主意。

最后,由于您的第二行刚刚删除'\t',您最好使用std::remove

content.erase( std::remove( content.begin(), content.end(), '\t' ), content.end() );
于 2014-09-29T15:56:06.957 回答
0

如果 AFAIK stl 字符串超过某个(小)大小,则始终在堆上分配,例如Visual Studio 中的 32 个字符

如果您遇到分配异常,您可以做什么:

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

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

于 2014-09-29T14:52:48.750 回答
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());
于 2014-09-29T15:51:31.730 回答