【问题标题】:Unexplained out_of_range in string::substrstring::substr 中无法解释的 out_of_range
【发布时间】:2010-02-13 20:20:53
【问题描述】:

在调用 substr.确切的错误是

在抛出一个之后调用终止 “std::out_of_range”的实例
what(): basic_string::substr

我绝对确定 tmp_request 的长度大于 1。无论我将什么传递给 substr——1、2 或 bodypos——它总是会抛出该错误。我在 Unix 上使用 g++。

我能包含的唯一有趣的事情是字符串有多个"\r\n",包括一个"\r\n\r\n"。

在一个 cpp 文件中:

std::string tmp_request, outRequest;

tmp_request = SS_Twitter->readData();
outRequest = SS_Twitter->parse(tmp_request);

另一个:

 std::string parse(const std::string &request)
 {
  std::map<std::string,std::string> keyval;
  std::string outRequest;
  if(request[0]=='P')
  {
   if(request.find("register")!=std::string::npos)
   { //we have a register request
    size_t bodypos = request.find("username");
    if(bodypos==std::string::npos) 
    {
     HttpError(400,"Malformed HTTP POST request. Could not find key username.",request); 
    }
    else
    {
     std::string body = request.substr(bodypos);
     StringExplode(body,"&", "=",keyval);
     outRequest = "doing stuff";
    }

   }

更新:

std::string request2("P\r\nregister\r\nusername=hello\r\n\r\n");

std::string body = request2.substr(4);

这会引发同样的错误。现在我知道这是完全有效且正确的代码,但它仍然抛出错误。 //删除源链接

【问题讨论】:

  • 当涉及到语言时,一个东西是否是一个错误很少是一个意见。您应该严重怀疑这样一个原始函数在实现中是否存在错误,否则您会陷入错误的境地。你坚持你的代码是正确的,而实现是错误的,这一事实永远不会解决你的问题。显然,您在某处有逻辑错误;你必须放弃你的假设并重新开始。
  • 这是错误的:if(int(request.find("register"))!=std::string::npos)。 npos 是一个size_t,一个无符号整数,而int 是一个有符号整数。零需要转换find 的结果,而您所做的只是将范围减半。事实上,您将总是输入这个 if 语句,因为 int 可能永远无法达到 size_t 的最大值(这就是 npos 的值)。如果不是错误,那就是 a 错误。
  • 我建议记录request 的长度和bodypos 的值。这将确定您是否看到来自 substr 的不良行为,或者您是否传递了错误的参数。
  • 我唯一一次在字符串方法(或在任何其他可能没有任何问题的地方)中看到一个神秘的“错误”,它与程序中其他地方的不相关的未定义行为有关.
  • 我不知道为什么人们不赞成这个问题。这是有合法问题的合法代码,让我们帮助这个人找到它并清理我们的答案,以便他们帮助其他可能遇到类似问题的人。

标签: c++ g++


【解决方案1】:

我稍微修改了您的示例以减少使用的压痕量。
有 5 个“测试用例”,没有一个会导致任何问题。您能否提供一个示例请求以重现您遇到的问题。

编辑:忘了提一下:如果这个示例(带有注释掉的位)不会产生该错误,那么最好的选择是您的 StringExplode 函数中有错误。您可以发布其来源,以获得更有用的建议。

编辑2: 在您的StringExplode 中,将results[tmpKey] = tmpKey.substr(found+1); 更改为results[tmpKey] = tmpResult[i].substr(found+1);。将int found 更改为size_t found,并删除所有if (found &gt; 0),这将修复您神秘的out_of_range。你substr-ing 一个错误的字符串。以防万一,这里有一个修复代码:

void StringExplode(std::string str, std::string objseparator, std::string keyseperator,
                   std::map <std::string, std::string> &results)
{
    size_t found;
    std::vector<std::string> tmpResult;
    found = str.find_first_of(objseparator);
    while(found != std::string::npos)
    {
        tmpResult.push_back(str.substr(0,found));
        str = str.substr(found+1);
        found = str.find_first_of(objseparator);
    }
    if(str.length() > 0)
    {
        tmpResult.push_back(str);
    }

    for(size_t i = 0; i < tmpResult.size(); i++)
    {
        found = tmpResult[i].find_first_of(keyseperator);
        while(found != std::string::npos)
        {
                std::string tmpKey = tmpResult[i].substr(0, found);
                results[tmpKey] = tmpResult[i].substr(found+1);
                found = tmpResult[i].find_first_of(keyseperator, found + results[tmpKey].size());
        }

    }
}

初始测试代码:

#include <iostream>
#include <map>
#include <string>

std::string parse(const std::string &request)
{
    std::map<std::string,std::string> keyval;
    std::string outRequest;

    if(request[0] != 'P')
        return outRequest;

    if(request.find("register") == std::string::npos)
        return outRequest;

    //we have a register request
    size_t bodypos = request.find("username");
    if(bodypos==std::string::npos)
    {
        // HttpError(400,"Malformed HTTP POST request. Could not find key username.",request);
        // you said HttpError returns, so here's a return
        return outRequest;
    }

    std::string body = request.substr(bodypos);
    // StringExplode(body,"&", "=",keyval);
    outRequest = "doing stuff";

    return outRequest;
}

int main()
{

    std::string request("P\r\nregister\r\nusername=hello\r\n\r\n");
    std::cout << "[" << parse(request) << "]\n";

    request = "Pregisternusername=hello\r\n\r\n";
    std::cout << "[" << parse(request) << "]\n";

    request = "Pregisternusername=hello";
    std::cout << "[" << parse(request) << "]\n";

    request = "registernusername=hello";
    std::cout << "[" << parse(request) << "]\n";

    request = "";
    std::cout << "[" << parse(request) << "]\n";

    return 0;
}

这输出,可以预见:

[做事]
[做事]
[做事]
[]
[]

【讨论】:

  • 我尝试硬编码那个字符串,它给了我同样的错误。不确定这里发生了什么:/ std::string request2("P\r\nregister\r\nusername=hello\r\n\r\n"); std::string body = request2.substr(4);
  • @Stanislav Palatnik,能否请您发布StringExplode 的来源?很可能存在错误。
  • @Dmitry:我在原帖里贴了完整的代码,这里只是StringExplode的代码:pastebin.ca/1795667@Curtis:是的,Dmitry的代码运行得很好。
  • @Stanislav Palatnik,在答案中检查我的 EDIT2,这应该会有所帮助。
  • 是的,罪魁祸首是 StringExplode!非常感谢!
【解决方案2】:

您确定它在substr 上失败,而不是在HttpError 或StringExplode 函数中的substr 调用上失败吗?如果你还没有,你应该通过调试器运行它,这样你就可以准确地看到它在哪里引发了异常。或者,您可以添加:

std::cout << "calling substr" << std::endl;

在您调用 substr 之前的一行,以及紧接其后的类似行,因此它看起来像:

std::cout << "calling substr" << std::endl;
std::string body = request.substr(bodypos);
std::cout << "finished calling substr" << std::endl;

StringExplode(body,"&", "=",keyval);
outRequest = "doing stuff";

如果substr 真的抛出了异常,那么你就会知道,因为程序将打印“calling substr”而没有匹配的“finished calling substr”。但是,如果它打印了这对调试消息,或者根本不打印,那么其他东西正在抛出异常。

【讨论】:

    【解决方案3】:

    您的代码有一个相当明显的问题:

    int k = read(ns, buf, sizeof(buf)-1);
    buf[k] = '\0';
    

    您没有检查 read() 是否成功 - 它在失败时返回 -1,如果发生它将导致各种内存损坏问题。

    还有:

    char * buf2 = const_cast<char *>(reply.c_str());
    write(ns,buf2,sizeof(buf2));
    

    您正在获取指针的大小 - 您想要输出字符串的长度:

    write(ns, buf2, reply.size() );
    

    您应该再次测试写入是否成功,并且它写入的字节数与您请求的一样多,但这不应直接导致 substr() 错误。

    【讨论】:

      【解决方案4】:

      看起来你需要一个 else 之后

      if(bodypos==std::string::npos)
      {
          HttpError(...);
      }
      

      否则,您将使用 bodypos = npos 调用 substr

      【讨论】:

      • 否,因为 HttpError 退出程序。我很欣赏这个建议,但我在这里发布的主要原因是找出 substr 在 Good 字符串上失败的原因。
      • @Stanislav Palatnik:永远不会发现错误的方法是对错误是什么有一个先入为主的概念。
      • 创建一个包含这段代码的小测试程序,然后调用它。在调试器中设置断点,您会在单步执行代码时检查变量时看到错误。
      • 为了记录,这个答案是在else 存在之前发布的,并且(显然)在我们知道HttpError 将退出该功能之前。一个有效但现在不相关的答案。
      【解决方案5】:

      您可以考虑使用(无符号)类型std::string::size_type 而不是int。

      为什么在这里将 find 的结果转换为 int: int(request.find("register"))!=std::string::npos

      【讨论】:

      • std::string body = request.substr(bodypos); .现在回到我的问题:)
      • 好的,我已经从我的回答中删除了这个问题(“异常发生在哪里?”)我的第二个问题呢:你为什么要转换为 int? find 返回一个无符号的 std::string::size_type
      • 嗨斯坦尼斯拉夫。自从我发布了这个答案以来,你已经编辑了你原来的问题,所以演员现在已经消失了,你使用 size_t(不是 std::string::size_type,但你明白了)。您能否确认您还编辑和测试了您的代码,并且仍然出现异常?干杯。
      • 是的,它仍然会发生。我什至硬编码了一个测试字符串并调用了 substr,它给出了同样的错误。
      猜你喜欢
      • 2015-11-25
      • 2014-09-03
      • 2015-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-24
      • 1970-01-01
      相关资源
      最近更新 更多