解析文本文件时抛出std :: out_of_range

时间:2011-10-18 19:10:18

标签: c++ text file-io

我有以下代码来阅读文本文件。

const string FILENAME = PACKAGES_DIR + pname;
  //the arguments to ifstream is a cstring and hence the conversion must be made
  ifstream freader;
  freader.open(FILENAME.c_str(),ios::in);
  if(freader.is_open())
  {
    while(freader.good())
    {
      string line;
      getline(freader,line);
      cout<<line<<endl;
      if(line.find("PackageId:"))
      {
        cout<<line.substr(11)<<endl;
      }
      else if(line.find("Name:"))
      {
        cout<<line.substr(5)<<endl;
      }
      else if(line.find("Version:"))
      {
        cout<<line.find(8)<<endl;
      }
      else
      {
        cout<<line<<endl;
      }

    }
  }

有问题的文字内容是

PackageId:994
Name:basket
Version:1.80-1
Deps:kdebase-runtime,libc0.1,libc0.1-udeb,libc6,libc6-udeb,libc6.1,libc6.1-udeb,libgcc1,libgpg-error0,libgpgme11,libkdecore5,libkdeui5,libkfile4,libkio5,libkparts4,libkutils4,libphonon4,libqimageblitz4,libqt4-dbus,libqt4-network,libqt4-qt3support,libqt4-svg,libqt4-xml,libqtcore4,libqtgui4,libstdc++6,libunwind7,libx11-6,phonon

我得到的输出是

PackageId:994
geId:994
Name:basket

Version:1.80-1
0-1
Deps:kdebase-runtime,libc0.1,libc0.1-udeb,libc6,libc6-udeb,libc6.1,libc6.1-udeb,libgcc1,libgpg-error0,libgpgme11,libkdecore5,libkdeui5,libkfile4,libkio5,libkparts4,libkutils4,libphonon4,libqimageblitz4,libqt4-dbus,libqt4-network,libqt4-qt3support,libqt4-svg,libqt4-xml,libqtcore4,libqtgui4,libstdc++6,libunwind7,libx11-6,phonon
e-runtime,libc0.1,libc0.1-udeb,libc6,libc6-udeb,libc6.1,libc6.1-udeb,libgcc1,libgpg-error0,libgpgme11,libkdecore5,libkdeui5,libkfile4,libkio5,libkparts4,libkutils4,libphonon4,libqimageblitz4,libqt4-dbus,libqt4-network,libqt4-qt3support,libqt4-svg,libqt4-xml,libqtcore4,libqtgui4,libstdc++6,libunwind7,libx11-6,phonon

terminate called after throwing an instance of 'std::out_of_range'
  what():  basic_string::substr

我想要的输出是:

PackageId:994
994
Name:basket
basket
Version:1.80-1
1.80-1
...

我做错了什么?

5 个答案:

答案 0 :(得分:5)

问题1

while .goodwhile !.eof 几乎总是错误的。扔掉任何书告诉你这样做,do this instead

在这种情况下,更改的代码看起来有点像这样:

const string FILENAME = PACKAGES_DIR + pname;
//the arguments to ifstream is a cstring and hence the conversion must be made
ifstream freader(FILENAME.c_str(), ios::in);
if (freader) {
   string line;
   while (getline(freader,line)) {  // <-----
      cout << line << endl;

      if (line.find("PackageId:"))
         cout << line.substr(11) << endl;
      else if (line.find("Name:"))
         cout << line.substr(5) << endl;
      else if (line.find("Version:"))
         cout << line.find(8) << endl;
      else
         cout << line << endl;
   }
}

问题2

您没有正确使用std::string::find

line.find("PackageId:")返回“搜索内容的字符串中第一次出现的位置”,如果找不到匹配则返回成员值npos

这与未对std::string::substr的第一个参数执行边界检查相结合,导致字符串出现问题。

相反,写一下:

if (line.find("PackageId:") != std::string::npos)

问题3

cout<<line.find(8)<<endl;应该说substr,而不是find


您的代码中包含上述某些内容:

const string FILENAME = PACKAGES_DIR + pname;
//the arguments to ifstream is a cstring and hence the conversion must be made
ifstream freader(FILENAME.c_str(), ios::in);
if (freader) {
   string line;
   while (getline(freader,line)) {  // <-----
      cout << line << endl;

      if (line.find("PackageId:")    != std::string::npos && line.size() > 11)
         cout << line.substr(11) << endl;
      else if (line.find("Name:")    != std::string::npos && line.size() > 5)
         cout << line.substr(5) << endl;
      else if (line.find("Version:") != std::string::npos && line.size() > 8)
         cout << line.substr(8) << endl;
      else
         cout << line << endl;
   }
}

答案 1 :(得分:2)

检查抛出的异常,它清楚地告诉你出了什么问题 抛出的异常的what()打印出来:

  

what():basic_string :: substr

告诉你,
substr

引发异常

请参阅 string::substr

的文档
  

string substr ( size_t pos = 0, size_t n = npos ) const;

     

生成子字符串

     

返回一个字符串对象,其内容初始化为子字符串   当前对象。

     

此子字符串是以字符开头的字符序列   位置pos,长度为n个字符。

     

参数

     
      
  • pos 当前字符串对象中字符的位置,用作子字符串的起始字符。 如果传递的位置超过字符串的结尾,则会抛出out_of_range异常。

  •   
  • n 子字符串的长度。如果此值将使子字符串跨越当前字符串内容的末尾,则仅   使用字符串结尾之前的那些字符。 npos是一个   静态成员常量值,具有最大可能值   因此,当使用此值时,所有类型都是size_t类型的元素   pos和字符串结尾之间的字符用作   初始化子字符串。

  •   

答案 2 :(得分:1)

更改

      else if(line.find("Version:"))
      {
        cout<<line.find(8)<<endl; //<--------------------
      }

      else if(line.find("Version:"))
      {
        cout<<line.substr(8)<<endl; //<----------------------
      }

答案 3 :(得分:1)

突出的第一个问题是,您尝试读取一行并使用该行而不检查读取是否成功。您应该将循环更改为以下内容,而不是检查文件在while条件下是否良好:

string line;
while(getline(freader,line))
{
    // now you can safely process line
}

答案 4 :(得分:0)

不使用substr位置的固定常量,而是使用位置变量:

std::string::size_t  position = 0;
static const char packageIdFieldName[] = "PackageId:";
position = find(packageIdFieldName);
if (position != std::string::npos)
{
    // The "sizeof('\0')" is a blantant reminder to subract
    //    the size of the terminating nul character.
    cout << line.substr(position + sizeof(packageIdFieldName) - sizeof('\0'))
         << endl;
    continue;
}