Problem with std::sort function. Seem to always have null value for 1 element after 2 rounds of iteraion

别说谁变了你拦得住时间么 提交于 2021-01-28 12:25:50

问题


I am trying to write a sort program trying to sort a data set I have. The key to the sorting is Grid_ID and it happened to be an alpha-numeric data. I have tried to sort accordingly

It give me an error

terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::at: __n (which is 0) >= this->size() (which is 0)

On doing debugging, the reading part of the code seems to function. The reading of file content into DataContainer data get filled with the right data of key and text position. But when it come to std::sort, when the program "less" is invoked, const GridLabel& elem2 always turn up to be zero or null after 2nd iteration

Below is some data set and partial source code (I do not include the write the content in sorted order here but should be runnable)

THanks for help!

This is the partial data set

Grid_Id,Speed,Acc,ID
K31,173,8.37,1
K29,143,3.36,2
K29,107,4.56,3
K30,133,5.97,4
K30,153,2.38,5
J27,203,1.86,6
J27,143,1.59,7
I26,73,7.66,8
I27,134,2.86,9

This is the code

#include <algorithm>
#include <functional>
#include <fstream>
#include <string>

#include <deque>
#include <vector>

#include <iostream>
#include <sstream>

struct GridLabel
{
    std::string key_;
    std::istream::pos_type pos_;        // position in stream where this line starts

    GridLabel( const std::string& key, const std::istream::pos_type& pos) : key_( key)
                                                                   , pos_( pos)
    {
    }

    const GridLabel& operator=( const GridLabel& other)
    {
        key_ = other.key_;
        pos_ = other.pos_;

        return *this;
    }
};

typedef std::vector<  GridLabel> DataContainer;

// Return whether first element is smaller than the second
bool less( const  GridLabel& elem1, const  GridLabel& elem2 )
{
   std::stringstream ss1, ss2;
   ss1 <<  elem1.key_.at(0);
   ss2 <<  elem2.key_.at(0);

   int value  = (ss1.str()).compare(ss2.str());

   if( value < 0 )
   {
       return true;
   }
   else if( value == 0)
   {
       // need to check if the rest are smaller
       std::string substr1 = elem1.key_.substr(1, std::string::npos);
       std::string substr2 = elem2.key_.substr(1, std::string::npos);

       return (std::atoi(substr1.c_str()) < std::atoi(substr2.c_str()));
   }
   else
   {
       return false;
   }
 }

int main(int argc, char* argv[])
{
   DataContainer data;

   // read data into the vector here
   std::ifstream input( "some_file.csv");

   // check if it is correct
   if ( !input.good())
   {
        std::cerr << "Input file can not be openned." << std::endl;
        return -1;
   }

   std::string text;
   std::string key;
   std::istream::pos_type pos;
   int count=0, save=0;

   // to skip the header
   std::getline( input, text);

   for( int line = 0; !input.eof(); ++line)
   {
      // store the position before reading the line
      pos = input.tellg();

      std::getline( input, text);

      // parse it
      save = text.find(",");

      key = text.substr(0,(save));

      data.push_back(  GridLabel( key, pos));
   }

   // sort the data in sorted order
   std::sort( data.begin(), data.end(), less);

   // create the new file
   ...............

   return 0;
}

回答1:


A simplified less() to compare

  1. the first characters of GridLabel::key
  2. the integral number starting from 2nd character of GridLabel::key.

This will not consider what else is stored in GridLabel::key. (This might be intended by OP.)

Sample:

#include <algorithm>
#include <iostream>
#include <string>

struct GridLabel {
  std::string key;
};

bool less(const GridLabel &elem1, const GridLabel &elem2)
{
  // compare first chars of keys
  const char c1 = elem1.key.at(0), c2 = elem2.key.at(0);
  if (c1 != c2) return c1 < c2;
  // compare integral beginning in 2nd char of keys
  const int i1 = atoi(elem1.key.c_str() + 1);
  const int i2 = atoi(elem2.key.c_str() + 1);
  return i1 < i2;
}

int main()
{
  GridLabel data[] = {
    { "K31,173,8.37,1" },
    { "K29,143,3.36,2" },
    { "K29,107,4.56,3" },
    { "K30,133,5.97,4" },
    { "K30,153,2.38,5" },
    { "J27,203,1.86,6" },
    { "J27,143,1.59,7" },
    { "I26,73,7.66,8" },
    { "I27,134,2.86,9" }
  };
  { std::cout << "Original data:\n";
    int i = 0;
    for (const GridLabel &entry : data) {
      std::cout << i++ << ": '" << entry.key << "'\n";
    }
  }
  std::cout << "Sorting...";
  std::sort(std::begin(data), std::end(data), less);
  std::cout << " Done.\n";
  { std::cout << "Sorted data:\n";
    int i = 0;
    for (const GridLabel &entry : data) {
      std::cout << i++ << ": '" << entry.key << "'\n";
    }
  }
}

Output:

Original data:
0: 'K31,173,8.37,1'
1: 'K29,143,3.36,2'
2: 'K29,107,4.56,3'
3: 'K30,133,5.97,4'
4: 'K30,153,2.38,5'
5: 'J27,203,1.86,6'
6: 'J27,143,1.59,7'
7: 'I26,73,7.66,8'
8: 'I27,134,2.86,9'
Sorting... Done.
Sorted data:
0: 'I26,73,7.66,8'
1: 'I27,134,2.86,9'
2: 'J27,203,1.86,6'
3: 'J27,143,1.59,7'
4: 'K29,143,3.36,2'
5: 'K29,107,4.56,3'
6: 'K30,133,5.97,4'
7: 'K30,153,2.38,5'
8: 'K31,173,8.37,1'

Live Demo on coliru

Please, note that (according to how predicate less() is implemented) there are a lot elements which are considered as equal:

  • I26,73,7.66,8 with I27,134,2.86,9
  • J27,203,1.86,6 with J27,143,1.59,7
  • etc.

These elements will appear in abitrary order after sorting.

Alternatively, std::stable_sort() could be used which will preserve the original order in these cases.



来源:https://stackoverflow.com/questions/58761130/problem-with-stdsort-function-seem-to-always-have-null-value-for-1-element-af

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!