Я думаю, что вы можете хранить данные построчно как полные std::string
.
Зная типы данных, вы сможете легко преобразовать std::string
в реальный тип (std::string
, int
, double
, ...).
Например, если у вас есть std::string
, который в действительности является двойным, вы можете использовать std::stod
для его преобразования.
Я сделал пример, чтобы быть более ясным. Для обработки данных рассмотрим следующее struct
:
typedef std::vector<std::string> StringVec;
struct FileData
{
StringVec col_names;
StringVec type_names;
StringVec data_lines;
bool loadData(const std::string & file_path);
bool getColumn(const std::string & col_name, StringVec & result);
};
typedef
здесь только для того, чтобы упростить код и сделать его более читабельным.
Метод loadData()
будет считывать файл и сохранять его содержимое в структуре.
col_names
- список имен столбцов, type_names
- список типов, data_lines
- список прочитанные строки.
Метод getColumn()
записывает в аргументе result
содержимое нужного столбца, указанного в аргументе col_name
.
Эти два метода возвращают логическое значение, которое указывает, была ли операция успешно выполнена (true
) или произошла ошибка (false
).
loadData()
может возвращать false, если данный файл не может быть открыт или поврежден.
getColumn()
может возвращать false, если имя указанного столбца не существует.
Возможная реализация этих методов может быть:
#include <fstream>
// ========== ========== ========== ========== ==========
StringVec split(const std::string & s, char c)
{
StringVec splitted;
std::string word;
for(char ch : s)
{
if((ch == c) && (!word.empty()))
{
splitted.push_back(word);
word.clear();
}
else
word += ch;
}
if(!word.empty())
splitted.push_back(word);
return splitted;
}
void removeExtraSpaces(std::string & word)
{
while(!word.empty() && (word[0] == ' '))
word.erase(word.begin());
while(!word.empty() && (word[word.size()-1] == ' '))
word.erase(word.end()-1);
}
// ========== ========== ========== ========== ==========
bool FileData::loadData(const std::string & file_path)
{
bool success(false);
std::ifstream in_s(file_path);
if(in_s)
{
bool names_read(false);
bool types_read(false);
std::string line;
while(getline(in_s, line))
{
if(!names_read) // first line
{
col_names = split(line, ',');
if(col_names.empty())
return false; // FILE CORRUPTED
for(std::string & word : col_names)
removeExtraSpaces(word);
names_read = true;
}
else if(!types_read) // second line
{
type_names = split(line, ',');
if(type_names.size() != col_names.size())
{
col_names.clear();
type_names.clear();
return false; // FILE CORRUPTED
}
for(std::string & word : type_names)
removeExtraSpaces(word);
types_read = true;
}
else // other lines
{
if(split(line, ',').size() != col_names.size())
{
col_names.clear();
type_names.clear();
data_lines.clear();
return false; // FILE CORRUPTED
}
data_lines.push_back(line);
}
}
in_s.close();
success = true;
}
return success;
}
bool FileData::getColumn(const std::string & col_name, StringVec & result)
{
bool success(false);
bool contains(false);
size_t index(0);
while(!contains && (index < col_names.size()))
{
if(col_names[index] == col_name)
contains = true;
else
++index;
}
if(contains)
{
for(const std::string & line : data_lines)
{
std::string field(split(line, ',').at(index));
removeExtraSpaces(field);
result.push_back(field);
}
success = true;
}
return success;
}
// ========== ========== ========== ========== ==========
Функции split()
и removeExtraSpaces()
определены, чтобы упростить код (и сделать этот пример более читабельным).
Со стороны пользователя это можно использовать следующим образом:
DataFile df;
bool loadSuccessful = df.loadData("data.txt"); // if true, df contains now the content of the file.
StringVec col;
bool columnFound = df.getColumn("col_name", col); // if true, col contains now the content of the desired column.
Как видите, очень прост в использовании :)
Я знаю, что на данный момент у вас есть вектор std::string
, но так как структура содержит имена реального типа каждого столбца, вы можете преобразовать что вы получили в реальном типе.
Возможно, вы можете добавить шаблонный метод convert()
в структуру, чтобы сделать его незаметным для пользователя.
Я провел тесты со следующими файлами данных:
data.txt:
first_col, second_col
string, double
line1, 1.1
line2, -2.5
line3, 10.03
_other_data.txt: _
first_col, second_col, third_col
int, string, char
0, line1, a
5, line2, b
И он успешно работал для обоих.
Я не знаю, достаточно ли элегантна обработка данных, как std::string
, но я надеюсь, что она вам поможет.