EirikO Skrevet 1. oktober 2010 Del Skrevet 1. oktober 2010 Har en fil som inneholder et visst antall rader (en for hvert tidssted) og et antall kolonner (forskjellige variable). Det jeg ønsker å gjøre er å lese inn en og en fil, hente ut hver kolonne til en variabel, for så å kalle en funksjon med disse varablene. Pseudo: while(!eof) { line = read_line('\t'); time = line[0]; xpos = line[1]; ypos = line[2]; compute_velocity(time,xpos, ypos); } Å lese inn en string-linje og printe den ut er greit, men hvordan får jeg delt opp etter tab og lagret til variabel? Lenke til kommentar
zotbar1234 Skrevet 1. oktober 2010 Del Skrevet 1. oktober 2010 Pseudo: while(!eof) { line = read_line('\t'); time = line[0]; xpos = line[1]; ypos = line[2]; compute_velocity(time,xpos, ypos); } "while ( !feof )" er typisk et idiom som ikke virker i C/C++, men på det konseptuelle planet er det riktig. Å lese inn en string-linje og printe den ut er greit, men hvordan får jeg delt opp etter tab og lagret til variabel? Du trenger et par hjelpefunksjoner: * En for å dele opp en string i biter etter en vilkårlig mengde skilletegn (som du da kaller med "\t") * En for å konvertere en tekstlig representasjon av et tall til det spesifikke tallet (som du da bruker på hver av bitene du trekker ut). Til det første kan jeg tipse om find_first_of/find_first_not_of. Til det andre kan det være lærerikt å se på lexical_cast<> som finnes hos boost. Om løsningen skulle være i C, er tankegangen den samme, men det vil være mer pes mtp minneadministrasjon og slikt. Hva har du skrevet så langt? Lenke til kommentar
EirikO Skrevet 1. oktober 2010 Forfatter Del Skrevet 1. oktober 2010 (endret) Hva har du skrevet så langt? Jeg har bare søkt og fant fort følgende (hvor jeg vel kan legge til find_first_of()?: // reading a text file #include <iostream> #include <fstream> #include <string> using namespace std; int main () { string line; ifstream myfile ("example.txt"); if (myfile.is_open()) { while (! myfile.eof() ) { getline (myfile,line); cout << line << endl; string::size_type found = line.find_first_of("\t"); } } myfile.close(); } else cout << "Unable to open file"; return 0; } Fila ser førøvrig typisk slik ut: 0.0 1.234 1.2434 0.1 1.343 1.4533 0.2 1.454 1.3534 ...osv Endret 1. oktober 2010 av EirikO Lenke til kommentar
zotbar1234 Skrevet 1. oktober 2010 Del Skrevet 1. oktober 2010 Hva har du skrevet så langt? Jeg har bare søkt og fant fort følgende (hvor jeg vel kan legge til find_first_of()?: Jeg spurte hva du hadde skrevet så langt, ikke hva du fant. Så, dermed nok et par oppfølgerspørsmål -- hvordan vil du lage en funksjon som tar en tekstlig representasjon av 3 tall slik du angir i eksempelet under og deler opp en slik linje i 3 biter? Hvordan ser ut en funksjon som konverterer "123.45" til 123.45? (tips: atof, lexical_cast<>, stringstreams). int main () { /* ... */ while (! myfile.eof() ) { getline (myfile,line); cout << line << endl; string::size_type found = line.find_first_of("\t"); } Dette vil ikke fungere etter hensikten. Årsaken til dette er at eof-indikatoren ikke trenger å settes før man faktisk har forsøkt å lese forbi slutten av filen. Standardmåten vil være noe i retning av: while ( getline(myfile, line, '\n') ) { /* do something with line */ } Lenke til kommentar
EirikO Skrevet 1. oktober 2010 Forfatter Del Skrevet 1. oktober 2010 Så, dermed nok et par oppfølgerspørsmål -- hvordan vil du lage en funksjon som tar en tekstlig representasjon av 3 tall slik du angir i eksempelet under og deler opp en slik linje i 3 biter? Hvordan ser ut en funksjon som konverterer "123.45" til 123.45? (tips: atof, lexical_cast<>, stringstreams). Dette vil ikke fungere etter hensikten. Årsaken til dette er at eof-indikatoren ikke trenger å settes før man faktisk har forsøkt å lese forbi slutten av filen. Standardmåten vil være noe i retning av: while ( getline(myfile, line, '\n') ) { /* do something with line */ } Hvis jeg har forstått det rett returnerer "found = line.find_first_of("\t");" indexene til tab'ene. så noe sånt som: double var[]; int i = 0; while( found != string::npos ) { string temp1 = line[found]; found = line.find_first_of("\t",found+1); var[i++] = atof(temp1); } Er en annen ting jeg ikke har nevt. Jeg må kjøre denne koden på en maskin hvor jeg ikke kan legge til biblioteker som ikke finnes fra før. Dermed er jeg noe usikker på om jeg kan bruke denne boost-saken.. Lenke til kommentar
zotbar1234 Skrevet 1. oktober 2010 Del Skrevet 1. oktober 2010 Hvis jeg har forstått det rett returnerer "found = line.find_first_of("\t");" indexene til tab'ene. Jepp. så noe sånt som: double var[]; int i = 0; while( found != string::npos ) { string temp1 = line[found]; found = line.find_first_of("\t",found+1); var[i++] = atof(temp1); } Du trenger å deklarere hvor mange elementer arrayet skal ha (vector<double> er dog et bedre valg). Dessuten er line[found] det ene tegnet i den posisjonen. Det du trenger er et intervall, fra (siste) sted hvor tall begynner fram til det neste stedet der noe annet enn tall opptrer. Derav tipset om find_first_not_of. Er en annen ting jeg ikke har nevt. Jeg må kjøre denne koden på en maskin hvor jeg ikke kan legge til biblioteker som ikke finnes fra før. Dermed er jeg noe usikker på om jeg kan bruke denne boost-saken.. Da lager du bare din egen variant som du kan ta med. lexical_cast<> var ment som et forslag/en inspirasjon. Lenke til kommentar
EirikO Skrevet 2. oktober 2010 Forfatter Del Skrevet 2. oktober 2010 Nå har jeg kommet litt i gang.. Men får ikke returnert rett index.. Ser ut til at jeg heller får en adresse elns.. string line; ifstream myfile ("example.txt"); if (myfile.is_open()) { while (getline(myfile,line) ) { getline (myfile,line); cout << line << endl; string::size_type found = line.find_first_of("\t"); string::size_type nfound = line.find_first_not_of("\t"); cout << "first: " << found << " not found: " << nfound << endl; } myfile.close(); } Men den printer ut 4932433434 som både found og nfound. Men hvis jeg får ut indexer kan jeg vel bruke substr for å hente ut fra found til nfound, for så å finne sette found til neste tab.. Lenke til kommentar
EirikO Skrevet 2. oktober 2010 Forfatter Del Skrevet 2. oktober 2010 (endret) Men den printer ut 4932433434 som både found og nfound. Fant ut at det var editoren som byttet ut tab med 4 mellomrom, så tab ble aldri funnet.. Nå har jeg: while (getline(myfile,line,'\n') ) { cout << line << endl; start = 0; length = 0; string::size_type found = line.find_first_of("\t"); string::size_type nfound = line.find_first_not_of("\t",found+1); while( found != string::npos ) { length = found-start; vas = line.substr(start,length); start = nfound; cout << "vas: " << vas << endl; found = line.find_first_of("\t",found+1); nfound = line.find_first_not_of("\t",found+1); } } Og det ser ut til å fungere greit. For å caste om til double, fant jeg istringstream(vas) >> varSomErDouble[i++] ok? Hvis noen har forslag til hva som kan gjøres bedre/enklere er det fint om dere skriker ut. Takk for tips! Endret 2. oktober 2010 av EirikO Lenke til kommentar
zotbar1234 Skrevet 2. oktober 2010 Del Skrevet 2. oktober 2010 Fant ut at det var editoren som byttet ut tab med 4 mellomrom, så tab ble aldri funnet.. Oppgaven du har foran deg har flere deler som opplagt skiller seg ut: * Lese inn en fil linjevis * Ta en linje og dele den opp i felt * Ta et felt og konvertere det til et tall Det enkleste (mest intuitive?) er å skrive en funksjon til hver oppgave som gjør kun det. Til slutt kan du da lage en funksjon som bruker de 3 du har til rådighet. Som ekstra bonus vil det også være lettere å skrive testkoden på denne måten (slik du skrev feltsplittingen, er det umulig å teste den uten å måtte lese en fil -- et opplagt verre, sett med TDD-øyne, opplegg). Så, for å begynne med oppdeling av felt, det enkleste er kanskje å la den funksjonen ta en linje og deretter returnere alle felt hver for seg. Siden du opplagt må kunne takle både " " og "\t", så er det greieste kanskje å la funksjonen ta i mot både linjen og separatortegnmengde: std::vector<std::string> split_fields(const std::string &source, const std::string &separators) { /* ... */ } Når denne funksjonen er skrevet og testet, så er det aktuelt med en funksjon som konverterer fra et felt (et element i std::vector<std::string>) til et tall som ligger i det feltet: doule field_to_double(const std::string &s) { std::istringstream iss(s); double d; if ( iss >> d ) return d; throw "fuck it"; } Og dermed kan man ha funksjon som syr disse sammen (jeg tenker nå på python sine list comprehensions og/eller map, men det blir muligens litt for klønete i C++, selv om std::transform kan ha en viss eleganse): while ( <next line> ) { std::vector<std::string> fields = split_fields(<next line>, " \t\r\n\v"); std::vector<double> doubles; std::transform(fields.begin(), fields.end(), std::back_inserter(doubles), field_to_double); /* do something with doubles */ } Hvordan du vil akkumulere de resulterende tallene er litt opptil deg (vector av tripler? bare en 1D-vector? noe annet?) For å caste om til double, fant jeg (...) Ja, du er på riktig spor. Har du forstått hva stringstreams gjør? Lenke til kommentar
[kami] Skrevet 4. oktober 2010 Del Skrevet 4. oktober 2010 her er kode for å lese èn linje og dele opp etter space/tab. int main() { std::fstream inputFile; std::string buffer; inputFile.seekg(std::ios_base::beg); getline(inputFile, buffer ); std::istringstream instream; instream.clear(); instream.str(buffer ); while (!instream.eof()) { double col; instream >> col; } } Lenke til kommentar
zotbar1234 Skrevet 4. oktober 2010 Del Skrevet 4. oktober 2010 (endret) ' date=' 4. oktober 2010 - 12:31' timestamp='1286188296' post='16289294']her er kode for å lese èn linje og dele opp etter space/tab. ... koden som ikke virker (og poenget var liksom å unngå å gjøre andres hjemmelekser). Endret 4. oktober 2010 av zotbar1234 Lenke til kommentar
[kami] Skrevet 4. oktober 2010 Del Skrevet 4. oktober 2010 joda, den funker test.txt > cat test.txt 1 2 3 4 5 6 int main () { std::fstream inputFile("test.txt"); std::string buffer; inputFile.seekg(std::ios_base::beg); getline(inputFile, buffer ); std::istringstream instream; instream.clear(); instream.str(buffer ); while (!instream.eof()) { double col; instream >> col; std::cout << "col: " << col; } } > ./a.out col: 1col: 2col: 3col: 4col: 5col: 6 jeg tar jo her seff høyde for at man selv klarer å sette fila til å peke til den fila man vil åpne, og finne includes man trenger selv. Lenke til kommentar
zotbar1234 Skrevet 4. oktober 2010 Del Skrevet 4. oktober 2010 ' date=' 4. oktober 2010 - 16:17' timestamp='1286201830' post='16290173']joda, den funker Nei, det gjør den ikke. Trivia -- hvorfor ikke? $ cat fjas.data 12.3 43.3 45.2 $ g++ reading.cpp $ ./a.out fjas.data next number 12.3 next number 43.3 next number 45.2 next number 45.2 jeg tar jo her seff høyde for at man selv klarer å sette fila til å peke til den fila man vil åpne, og finne includes man trenger selv. Ja. Det du ikke tar høyde for er hvordan man leser fra en fil riktig. Lenke til kommentar
[kami] Skrevet 4. oktober 2010 Del Skrevet 4. oktober 2010 (endret) hmm, rart. jeg får ikke den outputten du produserer. > cat test.txt 12.3 43.3 45.2 > ./a.out col: 12.3col: 43.3col: 45.2 jeg klarer ikke selv å se hva jeg har skrevet galt, så om du kan peke det ut på en ikke kryptisk måte tar jeg imot med takk. edit: ser nå at om du legger til space bak så vil jo seff streamen freake ut. da går det an å bruke if (!stream.fail()) sjekk før man legger inn i vectoren Uansett håper jeg eksempelet mitt (om enn ikke perfekt) gir en idè om hvordan dette kan løses. Endret 4. oktober 2010 av [kami] Lenke til kommentar
zotbar1234 Skrevet 4. oktober 2010 Del Skrevet 4. oktober 2010 ' date=' 4. oktober 2010 - 16:45' timestamp='1286203519' post='16290290']hmm, rart. jeg får ikke den outputten du produserer. Jeg fikk det til på 1. forsøk (men det er fordi jeg bevisst skapte en bestemt situasjon). edit: ser nå at om du legger til space bak så vil jo seff streamen freake ut. Problemet er ikke ' ' som ligger bak. Men ' ' illustrerer hva som kan gå galt. Problemet er at eof-flagget trenger IKKE å bli satt før ETTER den 1. operasjonen som leser forbi eof. da går det an å bruke if (!stream.fail()) sjekk før man legger inn i vectoren Feil tilnærming. Ideomatisk leser man i C++ på en slik måte at man tester stream-tilstanden rett etter IO-operasjonen, og kun dersom den lykkes går man videre (dette gjelder formattert såvel som rå IO): $ cat reading.cpp #include <iostream> #include <sstream> int main() { std::string buffer; while ( std::getline(std::cin, buffer ) ) { std::istringstream source(buffer); double col; while ( source >> col ) std::cout << "next: " << col << "\n"; } } $ g++ reading.cpp $ echo -e '12.34 -11.34\n4.5 5.6 \n' | ./a.out next: 12.34 next: -11.34 next: 4.5 next: 5.6 $ Uansett håper jeg eksempelet mitt (om enn ikke perfekt) gir en idè om hvordan dette kan løses. Jeg prøvde å unngå å servere en komplett løsning. Lenke til kommentar
[kami] Skrevet 5. oktober 2010 Del Skrevet 5. oktober 2010 takker, visste ikke at stream >> blah returnerte en verdi. det gjør ihvertfall lesning mer elegant og kompakt enn å gjøre stream >> blah if (stream.fail()) Lenke til kommentar
zotbar1234 Skrevet 5. oktober 2010 Del Skrevet 5. oktober 2010 ' date=' 5. oktober 2010 - 10:10' timestamp='1286266256' post='16293541']takker, visste ikke at stream >> blah returnerte en verdi. Det må den, for at "chaining" av operatorne skal virke: src >> foo >> bar >> zot; er ekvivalent med: f(f(f(src, foo), bar), zot) ... der f er operator>>(). Lenke til kommentar
Anbefalte innlegg
Opprett en konto eller logg inn for å kommentere
Du må være et medlem for å kunne skrive en kommentar
Opprett konto
Det er enkelt å melde seg inn for å starte en ny konto!
Start en kontoLogg inn
Har du allerede en konto? Logg inn her.
Logg inn nå