Поэтому у меня есть задание, где я читаю 1,9 миллиона строк значений с плавающей запятой (360 плавающих точек на строку), и мне нужно работать с этими данными. Первоначально у меня было это, и я не уверен, почему сегодня я получаю bad_alloc. Вот мой код. Мне интересно, есть ли что-нибудь, что я могу сделать, чтобы оптимизировать его лучше - я использую векторы, и я надеялся, что мне не придется использовать массивы структур. Я могу просто создать структуру и создать массив структур, и каждая структура будет содержать x, y и массив значений с плавающей запятой. Будет ли это иметь большую значимость?
Я бы очень признателен за любую критику моей реализации и кода. Благодарю!
Реализация
#include "file_parser.hpp"
Parser::Parser(char* fname){
fileName = fname;
}
/*
* The function parses a file that was set in the constructor
* And returns a map of the file
*/
VectorsMap Parser::parseFile(){
//open file
fPtr = fopen(fileName, "r");
total_rows = 0;
line = (char*)malloc(sizeof(char)*LINE_MAX);
//parse the file line by line
while(fgets(line, LINE_MAX, fPtr)){
//make sure that we do not read an empty line
if(line[0] != '\0') {
//send the line to be further parsed
parseLine(line);
//increment total rows count
total_rows++;
}
}
return vector_points;
}
void Parser::doCleanUp(){
fclose(fPtr);
free(line);
vector_points.clear();
}
/**
* Parse a line and tokenize it
* while extracting X and Y points
* and vectors and put them in a VectorsMap(deifned in file_parser.h)
*/
void Parser::parseLine(char* line){
//collection of vectors.
std::vector<float> vectors;
char* point;
//grab the x and y tokens
char* tk1 = strtok(line, ",");
char* tk2 = strtok(NULL, ",");
//value for indexing
int i=0;
char* tmp;
//make sure we have two correct x and y points
if(tk1 == NULL || tk2 == NULL){ return; }
//convert the tokens to floats
float x = strtof(tk1, NULL);
float y = strtof(tk2, NULL);
//create the x and y pair used to insert vectors into the map
XYPair pair = XYPair(x, y);
//tokenize until end of line
while(point=strtok(NULL, ",")){
//convert the token to float
float f_point = strtof(point, NULL);
//push the float to the vector
vectors.push_back(f_point);
i++;
}
//insert in the vectormap.
vector_points.insert(VectorsPair(pair, vectors));
}
int Parser::getTotalRows(){
return total_rows;
}
Заголовок файла:
//create specific types to make my life easier later on
typedef std::pair<float, float> XYPair;
typedef std::pair<XYPair, std::vector<float> > VectorsPair;
typedef std::map<XYPair, std::vector<float> > VectorsMap;
class Parser{
public:
//constructor
Parser(char* fname);
VectorsMap parseFile();
int getTotalRows();
int row_values;
int total_rows;
void doCleanUp();
private:
//collection of all x y points and their vectors
VectorsMap vector_points;
FILE* fPtr; //file pointer to file to be parsed
char *line; //line to parse file line by line
char* fileName; //path/name of file to be parsed
void parseLine(char* line);
};
Вызов malloc
и fopen
при вызове parseFile
но затем освобождение в отдельной функции чрезвычайно подвержено ошибкам. Если вы звоните parseFile
дважды без вызова doCleanup
то утечка памяти и дескриптор файла.
Я бы прекратил использовать malloc
и strtok
.
VectorsMap Parser::parseFile(){
//open file
std::ifstream f(fileName);
total_rows = 0;
std::string line;
//parse the file line by line
while(std::getline(f, line)){
//make sure that we do not read an empty line
if(line.size()) {
//send the line to be further parsed
parseLine(line);
//increment total rows count
total_rows++;
}
}
return vector_points;
}
А затем parseLine
чтобы не использовать ужасную функцию strtok
, например, используя Boost.Tokenizer или std::istringstream
и std::getline
Также обратите внимание, что вы читаете данные в vector_points
затем возвращаете копию, что означает, что вам нужно в два раза больше памяти, используемой набором данных. Вы можете сохранить только одну копию данных, выполнив следующие действия:
return std::move(vector_points);
поэтому данные перемещаются в возвращаемое значение, а не копируются.