2017-02-23 49 views
5

Итак, мне задан файл с десятью матрицами, и я хотел бы прочитать из файла эти матрицы и сохранить их в векторах/массивах, где каждая матрица хранится либо в векторе, либо в виде массив. Однако формат этих матриц затрудняет чтение данных (я не очень хорошо читаю из входного файла).C++ Чтение матриц из файла с несколькими разделителями

файл имеет следующий формат. Элементы каждой матрицы разделяются символом «,». Каждая строка разделяется символом «;», и каждая матрица разделяется символом «|». Например, три матрицы 2 на 2 являются следующими.

1,2; 3,4 | 0,1; 1,0 | 5,3; 3,1 |

И я просто хочу сохранить матрицы в трех разных векторах, но я не уверен, как это сделать.

Я попытался

while(getline(inFile,line)){ 
     stringstream linestream(line); 
     string value; 
     while(getline(linestream, value, ','){ 
       //save into vector 
     } 
    } 

Но это, очевидно, очень грубо, и только данные по отделяет запятой. Есть ли способ разделить данные с несколькими разделителями?

Спасибо!

ответ

6
string line; 
while(getline(infile, line, '|')) 
{ 
    stringstream rowstream(line); 
    string row; 
    while(getline(rowstream, row, ';')) 
    { 
      stringstream elementstream(row); 
      string element; 
      while(getline(elementstream, element, ',')) 
      { 
       cout << element << endl;      
      } 
    } 
} 

Используя выше кода вы можете построить логику для хранения индивидуального element, как вам нравится.

1

Вы можете использовать концепцию finite state machine. Вам нужно определить состояния для каждого шага. Прочитайте один символ, а затем определите, что это (число или разделитель).

Вот как вы могли это сделать. Для получения дополнительной информации проверьте это в Интернете. text parsing, finite state machine, lexical analyzer, formal grammar

enum State 
{ 
    DECIMAL_NUMBER, 
    COMMA_D, 
    SEMICOLON_D, 
    PIPE_D, 
    ERROR_STATE, 
}; 

char GetChar() 
{ 
    // implement proper reading from file 
    static char* input = "1,2;3,4|0,1;1,0|5,3;3,1|"; 
    static int index = 0; 

    return input[index++]; 
} 

State GetState(char c) 
{ 
    if (isdigit(c)) 
    { 
     return DECIMAL_NUMBER; 
    } 
    else if (c == ',') 
    { 
     return COMMA_D; 
    } 
    else if (c == ';') 
    { 
     return SEMICOLON_D; 
    } 
    else if (c == '|') 
    { 
     return PIPE_D; 
    } 

    return ERROR_STATE; 
} 

int main(char* argv[], int argc) 
{ 
    char c; 
    while (c = GetChar()) 
    { 
     State s = GetState(c); 
     switch (c) 
     { 
     case DECIMAL_NUMBER: 
      // read numbers 
      break; 
     case COMMA_D: 
      // append into row 
      break; 
     case SEMICOLON_D: 
      // next row 
      break; 
     case PIPE_D: 
      // finish one matrix 
      break; 
     case ERROR_STATE: 
      // syntax error 
      break; 
     default: 
      break; 
     } 
    } 
    return 0; 
} 
2

Я использую эту собственную функцию, чтобы разделить строку на вектор строк:

/** 
* \brief Split a string in substrings 
* \param sep Symbol separating the parts 
* \param str String to be splitted 
* \return Vector containing the splitted parts 
* \pre  The separator can not be 0 
* \details Example : 
* \code 
* std::string str = "abc.def.ghi..jkl."; 
* std::vector<std::string> split_str = split('.', str); // the vector is ["abc", "def", "ghi", "", "jkl", ""] 
* \endcode 
*/ 
std::vector<std::string> split(char sep, const std::string& str); 

std::vector<std::string> split(char sep, const std::string& str) 
{ 
    assert(sep != 0 && "PRE: the separator is null"); 
    std::vector<std::string> s; 
    unsigned long int i = 0; 
    for(unsigned long int j = 0; j < str.length(); ++j) 
    { 
    if(str[j] == sep) 
    { 
     s.push_back(str.substr(i, j - i)); 
     i = j + 1; 
    } 
    } 
    s.push_back(str.substr(i, str.size() - i)); 
    return s; 
} 

Тогда, ожидая вас есть класс Matrix, вы можете сделать что-то вроде :

std::string matrices_str; 
std::ifstream matrix_file(matrix_file_name.c_str()); 
matrix_file >> matrices_str; 
const std::vector<std::string> matrices = split('|', matrices_str); 
std::vector<Matrix<double> > M(matrices.size()); 
for(unsigned long int i = 0; i < matrices.size(); ++i) 
{ 
    const std::string& matrix = matrices[i]; 
    const std::vector<std::string> rows = split(';', matrix); 
    for(unsigned long int j = 0; j < rows.size(); ++j) 
    { 
    const std::string& row = matrix[i]; 
    const std::vector<std::string> elements = split(',', row); 
    for(unsigned long int k = 0; k < elements.size(); ++k) 
    { 
     const std::string& element = elements[k]; 
     if(j == 0 && k == 0) 
     M[i].resize(rows.size(), elements.size()); 
     std::istringstream iss(element); 
     iss >> M[i](j,k); 
    } 
    } 
} 

Или, сжатый код:

std::string matrices_str; 
std::ifstream matrix_file(matrix_file_name.c_str()); 
matrix_file >> matrices_str; 
const std::vector<std::string> matrices = split('|', matrices_str); 
std::vector<Matrix<double> > M(matrices.size()); 
for(unsigned long int i = 0; i < matrices.size(); ++i) 
{ 
    const std::vector<std::string> rows = split(';', matrices[i]); 
    for(unsigned long int j = 0; j < rows.size(); ++j) 
    { 
    const std::vector<std::string> elements = split(',', matrix[i]); 
    for(unsigned long int k = 0; k < elements.size(); ++k) 
    { 
     if(j == 0 && k == 0) 
     M[i].resize(rows.size(), elements[k].size()); 
     std::istringstream iss(elements[k]); 
     iss >> M[i](j,k); 
    } 
    } 
} 
1

Пример, который вы на самом деле сопоставляет с очень простой байт-машиной.

Начните с обнуленной матрицы и что-то, что отслеживает, где в матрице, которую вы пишете. Прочитайте один символ за раз. Если символ является цифрой, умножьте текущее число в матрице на 10 и добавьте к нему цифру, если символ является запятой, перейдите к следующему номеру в строке, если символ является точкой с запятой, перейдите к следующая строка, если символ - это труба, запустите новую матрицу.

Возможно, вы не захотите делать это именно так, если числа являются плавающей точкой. Я бы сохранил их в буфере и использовал стандартный метод анализа чисел с плавающей запятой. Но кроме этого вам не нужно сохранять сложное состояние или создавать большой парсер. Возможно, вам захочется добавить обработку ошибок на более позднем этапе, но даже там обработка ошибок довольно тривиальна и зависит только от текущего пользователя, который вы просматриваете.