2017-02-23 16 views
5

Dostałem więc plik z dziesięcioma macierzami i chciałbym odczytać z pliku te macierze i zapisać je w wektorach/tablicach, gdzie każda macierz jest przechowywana jako wektor lub szyk. Jednak format tych macierzy utrudnia odczytanie danych (nie jestem dobry w czytaniu z pliku wejściowego).C++ Odczytaj matryce z pliku z wieloma ogranicznikami

plik ma następujący format. Elementy każdej macierzy są oddzielone ",". Każdy wiersz jest oddzielony ";", a każda macierz oddzielona jest znakiem "|". Na przykład trzy macierze 2 na 2 są następujące.

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

I chcę tylko zapisać macierze na trzy różne wektory, ale nie jestem pewien, jak to zrobić.

Próbowałem

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

Ale to jest oczywiście bardzo surowe, a tylko oddziela dane przecinkami. Czy istnieje sposób na oddzielenie danych od wielu ograniczników?

Dziękujemy!

Odpowiedz

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;      
      } 
    } 
} 

Stosując powyższy kod można budować logiki do przechowywania indywidualny element jak chcesz.

1

Możesz użyć koncepcji finite state machine. Dla każdego kroku potrzebujesz zdefiniować stany. Przeczytaj jeden znak, a następnie zdecyduj, co to jest (liczba lub ogranicznik).

Oto koncepcja, jak można to zrobić. Więcej informacji można znaleźć w Internecie. 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

Używam tego własną funkcję, aby podzielić ciąg w wektorze ciągów:

/** 
* \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; 
} 

Potem, oczekując masz Matrix klasy, można zrobić coś takiego :

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); 
    } 
    } 
} 

Albo sprężone Kod:

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

Przykładowy odwzorowanie na bardzo prostą maszynę bajtową.

Zacznij od wyzerowanej macierzy i czegoś, co będzie śledzić, gdzie w macierzy, którą piszesz. Przeczytaj jedną postać na raz. Jeśli znak jest cyfrą, pomnóż aktualną liczbę w macierzy przez 10 i dodaj do niej cyfrę, jeśli znak jest przecinkiem, przejdź do następnej liczby w rzędzie, jeśli znak jest średnikiem, przejdź do w następnym wierszu, jeśli znak jest potokiem, uruchom nową macierz.

Może nie chcesz tego robić dokładnie w ten sposób, jeśli liczby są zmiennoprzecinkowe. Zapisałbym je w buforze i użyłem standardowej metody analizowania liczb zmiennoprzecinkowych. Ale poza tym nie musisz utrzymywać zbyt złożonego stanu ani budować dużego parsera. Możesz dodać obsługę błędów na późniejszym etapie, ale nawet tam obsługa błędów jest banalna i zależy tylko od skanowanej postaci.