【问题标题】:Problem with deletion of a map element and insertion of a new element in its place删除地图元素并在其位置插入新元素的问题
【发布时间】:2025-11-23 02:00:01
【问题描述】:

这个问题来自《Jumping into C++》一书,第 19 章。

编写一个程序,读取用户输入的 HTML 文本(别担心,我们稍后会介绍如何从文件中读取)。它应该支持以下 HTML 标签:、

、、。每个 HTML 标签都有一个开放标签,例如 和一个以正斜杠开头的结束标记:。标签内部是由该标签控制的文本:加粗的文本斜体的文本。 head> 标记控制作为元数据的文本,而 标记围绕要显示的文本。 标签用于超链接,并具有以下格式的 URL:text

一旦你的程序读入了一些 HTML,它应该简单地忽略 。它应该从

部分中删除任何文本,以便在输出时不会显示它。然后它应该显示正文中的所有文本,对其进行修改,以便 之间的任何文本将显示为带有星号 (*), 内的任何文本都将显示带有下划线 (_),任何带有 link text 标记的文本都会显示为链接文本 (linkurl)。

我的尝试的简要说明
第 1 步 - 将 body 标签之间的字符串分配给一个新的字符串变量“body”。
第 2 步 - 创建映射“检查”并在“正文”中插入部分字符串,省略 标记之间的字符串。
第 3 步 - 尝试替换包含 标记的地图“检查”的插入元素,首先将 itr->firstitr->second 的值分配给新变量,然后删除该元素并插入新元素删除 标签之间的字符串后在地图中。

这就是我面临的问题。编译器没有给出错误但程序没有返回 0。如果我删除“check.erase(index)”,它可以工作,但这并不能解决目的。

我尝试在单独的代码中做类似的事情,并且成功了。我在下面的代码之后包含了其他代码,这是对上述问题的尝试解决方案。

int main()
{
map<int,string>check;

string x = "<html>";
string xx = "</html>";
string y = "<head>";
string yy = "</head>";
string z = "<body>";
string zz = "</body>";
string a = "<b>";
string aa = "</b>";
string b = "<i>";
string bb = "</i>";
string c = "<a href";
string ccc = ">";
string cc = "</a>";

string type = "<html>\n<head>I dont know what to write here</head>\n<body>\ngurasees is <b>my</b> good <i>name</i>. You can find <b>me</b> on the <i>web</i>. Link is <a href = www.google.com>gura</a>\n</body>\n</html>";

int f = type.find(z)+z.size()+1;
int g = type.find(zz)-f;
string body = type.substr(f,g);

 map<int,string>::iterator itr;
 map<int,string>::iterator ends = check.end();

 //--------------------------STEP 2-----------------------------------------

 check.insert ({body.find(body[0]), body.substr (body.find(body[0]), body.find(a,0)-1)});
 
int k = body.find(a, 0);
k++;
int p = body.find(aa,0);

for (int i = body.find(a,k); i != string::npos && p != string::npos; i = body.find(a, i), p = body.find(aa, p))
{
    check.insert({p+aa.size(),body.substr(p+aa.size(), i-1-(p+aa.size()))});
    i++; p++;
}
    check.insert({p + aa.size() , body.substr(p + aa.size() , body.size() - 1 - p ) } ) ;

 //------------------------STEP 3-------------------------------------------

for (itr = check.begin(); itr != ends; itr++)
{
    cout << itr->second.find(b) << endl; //prints out correctly
    if (itr->second.find(b) != string::npos)
    {
        int index = itr->first;
        string italic_component = itr->second;
        
        check.erase(index);
        check.insert ({index, italic_component.substr(italic_component.find(italic_component[0]), italic_component.find(b, 0)-1)});
        int u = italic_component.find(b, 0);
        u++;

        int j = italic_component.find(bb, 0);

        for (int i = italic_component.find(b, u); i != string::npos && j != string::npos; i = italic_component.find(b, u), j = italic_component.find(bb, j) )
        {
            check.insert({index + j + bb.size() , italic_component.substr(j + bb.size(), i - 1 - (j+bb.size()))});
            i++; j++ ;
        }

        check.insert({j + index + bb.size() ,italic_component.substr(j + bb.size() , italic_component.size() - 1 - j ) } ) ;
    }
}

//------------------------------STEP 4-------------------------------------

 for ( int i = body.find( a , 0 ), j = body.find( aa , 0), k = body.find( b , 0 ), l = body.find( bb , 0); i != string::npos && j != string::npos && j != string::npos && k != string::npos; i = body.find(a, i ), j = body.find(aa,j), k = body.find(b, k), l = body.find(bb,l) )
{
    check.insert( {i, "*" + body.substr(i+a.size(), j-(i+a.size())) + "*" } );
    check.insert( {k, "_" + body.substr(k+b.size(), l-(k+b.size())) + "_" } );
    i++; j++; k++; l++;
}
}

--------------其他代码------ -------------

int main()
{
map<int,string>check;

check[10] = "gurasees is there for you";
check[20] = "aka is not born yet";
check[30] = "sam please wait for years";

map<int,string>::iterator itr;
map<int,string>::iterator ends = check.end();

for(itr = check.begin(); itr!= ends; itr++)
{
    if(itr->second.find("born") != string::npos)
     {
         
         int index = itr->first;
         string italic = itr->second;

         check.erase(itr->first);
         

         check.insert({index, italic.substr(italic.find(italic[0]), 5)});
     }
}

【问题讨论】:

  • 这是什么? body.find(body[0]),0就够了。
  • 在上面展开,body.find(body[0]) 表示返回我们可以在字符串中找到第一个字符的第一个位置的索引。除非字符串为空,否则这显然是 0。
  • eraseing 容器中的元素同时遍历容器几乎总是一个错误。你erase`itr.使用调试器逐步执行程序将帮助您确认或拒绝这种可能的可能性。
  • @user4581301 感谢您的回复。我已经在答案部分写了我尝试过的内容。请检查。

标签: c++ data-structures


【解决方案1】:

我现在明白了。 itr 一直指向已释放的内存(我猜 freed 是正确的词?)使内存无法使用。这也会使指针无效,因此试图增加它会导致程序崩溃。

我通过调试(在较小的代码中)了解到,当我擦除itr 指向的内存时,它在使用check.insert() 后被分配回来(在check.erase() 之后)。在这种情况下,没有分段错误,因为itr++ 变为有效。这就是我编写的较小代码中发生的情况,在我提出问题之前我一直运行它。现在我开始一次又一次地通过调试器来了解发生了什么,程序崩溃了几次,我意识到了问题。

与主代码相同,在这种情况下,注释掉 L66 可以使代码顺利运行而不会出现任何分段错误。原因是与上面描述的情况相同。

但是如果我插入了另一个元素(这意味着在删除一个元素后插入了 2 个元素,一次在 L54 ,另一个在 L66 ),这使得被擦除的内存(itr 仍然指向)极不可能重新分配回来。整个代码只运行一次,在这种情况下返回 0。 如果我错了,请纠正我。

所以我在 L66 之后重新分配了itr = check.begin(),使其再次生效,从而解决了问题。

【讨论】: