2008-11-06 8 views
8

mam pliku z danymi zawartymi w następujący sposób:C++ plik IO & rozszczepienie przez separator

0,  2, 10 
10,  8, 10 
10,  10, 10 
10,  16, 10 
15,  10, 16 
17,  10, 16 

że chce móc wprowadzić plików i podzielone na trzy macierze w procesie obcinania wszystkie nadmiar miejsc i konwersja każdego elementu na liczby całkowite.

Z jakiegoś powodu nie mogę znaleźć prostego sposobu na zrobienie tego w języku C++. Jedyny sukces, jaki miałem, to wprowadzenie każdej linii do tablicy, a następnie wygaszenie wszystkich spacji, a następnie ich podział. Cały ten proces zajął mi dobre 20-30 linii kodu i jest to trudny do zmodyfikowania, na przykład kolejny separator (np. Przestrzeń), itp.

To jest odpowiednik pytona, co chciałbym mieć w C++:

f = open('input_hard.dat') 
lines = f.readlines() 
f.close() 

#declarations 
inint, inbase, outbase = [], [], [] 

#input parsing 
for line in lines: 
    bits = string.split(line, ',') 
    inint.append(int(bits[0].strip())) 
    inbase.append(int(bits[1].strip())) 
    outbase.append(int(bits[2].strip())) 

Łatwość użycia tego w pythonie jest jednym z powodów, dla których wprowadziłem się do niego w pierwszej kolejności. Jednak wymagam tego w C++ teraz i nie chciałbym używać mojego brzydkiego kodu linii 20-30.

Każda pomoc będzie doceniona, dzięki!

Odpowiedz

4

Naprawdę nie ma nic złego w fscanf, który jest prawdopodobnie najszybszym rozwiązaniem w tym przypadku. Jest równie krótki i czytelny jak kod Pythona:

FILE *fp = fopen("file.dat", "r"); 
int x, y, z; 
std::vector<int> vx, vy, vz; 

while (fscanf(fp, "%d, %d, %d", &x, &y, &z) == 3) { 
    vx.push_back(x); 
    vy.push_back(y); 
    vz.push_back(z); 
} 
fclose(fp); 
+0

Dobra robota. Ludzie zapominają o dopasowaniu wzorców scanf. Najprostsze rozwiązanie jest najlepsze. – jbruni

2

Coś jak:

vector<int> inint; 
vector<int> inbase; 
vector<int> outbase; 
while (fgets(buf, fh)) { 
    char *tok = strtok(buf, ", "); 
    inint.push_back(atoi(tok)); 
    tok = strtok(NULL, ", "); 
    inbase.push_back(atoi(tok)); 
    tok = strtok(NULL, ", "); 
    outbase.push_back(atoi(tok)); 
} 

z wyjątkiem sprawdzania błędów.

+0

Wolałbym uniknąć takiego rozwiązania „C-owski” za dobrze, estetyka ... ale co ważniejsze w tym przypadku ponieważ strtok ma poważne problemy z wątkami. Prawidłowy kod! – MattyT

1

std :: getline pozwala odczytać wiersz tekstu, można użyć strumienia ciąg do analizowania indywidualną linię:

string buf; 
getline(cin, buf); 
stringstream par(buf); 

char buf2[512]; 
par.getline(buf2, 512, ','); /* Reads until the first token. */ 

Gdy pojawi się wiersz tekstu do łańcucha, można w rzeczywistości używaj dowolnej funkcji parsowania, którą chcesz, nawet sscanf (buf.c_str(), "% d,% d '% d", & i1, & i2, & i3), używając atoi na podciągu z liczbą całkowitą lub przez inna metoda.

Możesz również ignorować niechciane znaki w strumieniu wejściowym, jeśli wiedzą, że tam jesteś:

if (cin.peek() == ',') 
    cin.ignore(1, ','); 
cin >> nextInt; 
1

Jeśli nie przeszkadza przy użyciu biblioteki Boost, ...

#include <string> 
#include <vector> 
#include <boost/lexical_cast.hpp> 
#include <boost/regex.hpp> 

std::vector<int> ParseFile(std::istream& in) { 
    const boost::regex cItemPattern(" *([0-9]+),?"); 
    std::vector<int> return_value; 

    std::string line; 
    while (std::getline(in, line)) { 
     string::const_iterator b=line.begin(), e=line.end(); 
     boost::smatch match; 
     while (b!=e && boost::regex_search(b, e, match, cItemPattern)) { 
      return_value.push_back(boost::lexical_cast<int>(match[1].str())); 
      b=match[0].second; 
     }; 
    }; 

    return return_value; 
} 

To wyciąga linie ze strumienia, a następnie wykorzystuje bibliotekę Boost :: RegEx (z grupą przechwytującą) do wyodrębnienia każdej liczby z linii. Automatycznie ignoruje wszystko, co nie jest prawidłową liczbą, ale można to zmienić, jeśli chcesz.

To wciąż około dwudziestu linii z #include s, ale można go użyć do wyodrębnienia w zasadzie niczego, co jest z linii pliku. Jest to trywialny przykład: używam prawie identycznego kodu do wyodrębniania tagów i opcjonalnych wartości z pola bazy danych, a jedyną istotną różnicą jest wyrażenie regularne.

EDYCJA: Ups, chciałeś trzech oddzielnych wektorów.Spróbuj tego niewielką modyfikację zamiast:

const boost::regex cItemPattern(" *([0-9]+), *([0-9]+), *([0-9]+)"); 
std::vector<int> vector1, vector2, vector3; 

std::string line; 
while (std::getline(in, line)) { 
    string::const_iterator b=line.begin(), e=line.end(); 
    boost::smatch match; 
    while (b!=e && boost::regex_search(b, e, match, cItemPattern)) { 
     vector1.push_back(boost::lexical_cast<int>(match[1].str())); 
     vector2.push_back(boost::lexical_cast<int>(match[2].str())); 
     vector3.push_back(boost::lexical_cast<int>(match[3].str())); 
     b=match[0].second; 
    }; 
}; 
6

Nie ma realne potrzeby korzystania impuls w tym przykładzie jako strumienie rade ładnie:

int main(int argc, char* argv[]) 
{ 
    ifstream file(argv[1]); 

    const unsigned maxIgnore = 10; 
    const int delim = ','; 
    int x,y,z; 

    vector<int> vecx, vecy, vecz; 

    while (file) 
    { 
     file >> x; 
     file.ignore(maxIgnore, delim); 
     file >> y; 
     file.ignore(maxIgnore, delim); 
     file >> z; 

     vecx.push_back(x); 
     vecy.push_back(y); 
     vecz.push_back(z); 
    } 
} 

chociaż gdybym zamiar wykorzystać impuls Wolałbym prostota tokenizer na wyrecytowanie ... :)

1

dlaczego nie ten sam kod co w pytonie :)?

std::ifstream file("input_hard.dat"); 
std::vector<int> inint, inbase, outbase; 

while (file.good()){ 
    int val1, val2, val3; 
    char delim; 
    file >> val1 >> delim >> val2 >> delim >> val3; 

    inint.push_back(val1); 
    inbase.push_back(val2); 
    outbase.push_back(val3); 
} 
0

Jeśli chcesz mieć możliwość skalowania do twardszych formatów wejściowych, należy wziąć pod uwagę ducha, impuls parsera combinator biblioteki.

This page ma przykład, który niemal co trzeba zrobić (z liczb rzeczywistych i jednego wektora chociaż)