Eli teen generoituvia nimiä peliini. Nyt ongelma on, että miten voin lukea kaikki rivit ja taulukoittaa sen? Eli tyyliin:
eli esim: nimet.dat tiedosto
Kalle
Harri
Jukka
Mikko
Kun se luetaan tallentuisi ne näin:
Nimi[0] = Kalle
Nimi[1] = Harri
Nimi[2] = Jukka
Nimi[3] = Mikko
Eli esim nimet.dat tiedostossa on neljä riviä ja kun sieltä luetaan tieto se tallentaa ne tuollaisiin muutujiin. Ja sieltä sitten niitä voisi käyttää. Toivottavasti ymmärsitte mitä tarkoitan.
Mikset vain käyttäisi vectoria tavallisen taulukon sijaan?
Tavallista taulukkoa tuohon tarkoitukseen käyttäessäsi joudut joka tapauksessa käymään tiedoston läpi ja laskemaan taulukkoa varten varattavan muistin määrän (paitsi tietysti siinä tapauksessa, että rivimäärä on aina vakio).
Voit tietenkin käydä tiedoston läpi ja lukea rivit ensin vaikka listaan, varata taulukon ja lopuksi siirtää listasta taulukkoon.
PL/I:llä homma olisi aika triviaali:
readLines: proc (parm) options(main); dcl parm char (*) var; dcl in file; dcl i fixed bin; dcl line char (100) var controlled; dcl lines (*) char (100) var controlled; dcl (lbound, hbound, trim, allocation) builtin; ON UNDEFINEDFILE (in) begin; put skip list ('Can''t open file: ' || parm || ' for reading'); stop; end; ON ENDFILE (in) goto eof; open file(in) title (parm || ', type (text), recsize (100)') input; do forever; allocate line; get file (in) edit (line) (L); end; eof: close file(in); allocate lines(allocation(line)); do i = allocation(line) to 1 by -1; lines(i) = line; free line; end; do i = lbound(lines) to hbound(lines); put skip list ( 'line:'|| trim(i) || ': ' || trim(lines(i)) ); end; end readLines;
En yleensä anna suoraa koodia tällaisiin vaan kehotan siirtymään C++ tai C:n refrenssiin, mutta nyt alkoi jalskin vastaus vituttamaan sen verran, että annan suorat vastaukset.
Siitä huolimatta ensikerralla etsi googlella miten c++ luetaan tiedostoja. Tämäkään ratkaisu ei välttämättä ole täydellinen, sillä se vaan lukee joka rivin alkioihin.
jalski: Miten niin triviaali, tossa on 38 riviä kun vastaavan ja helppolukuisemman c++ esimerkin saa aikaan 31 rivillä (ilman kommentteja).
Ps. Tää on C/C++ osasto ei mikään purkka-PL/I osasto.
#include <iostream> #include <vector> #include <string> #include <fstream> int main(int argc, char **argv){ // Jos ohjelmalle on annettu parametri niin ajetaan ohjelma muuten tulostetaan virhe :( if(argc == 2){ // Alustellaan muutamat muuttujat std::string rivi = ""; // nimet-taulukko (vektori) std::vector<std::string> nimet; // Tässä avataan itse tiedosto tutustu C++ refrenssiin std::ifstream tiedosto(argv[1]); // Jos tiedosto on auki niin... if(tiedosto.is_open()){ // Luetaan tiedostoa niin kauan kun rivejä riittää tai ei tapahdu jotain muuta hasardia. while(tiedosto.good()){ // Luetaan rivi std::getline(tiedosto, rivi); // Tunkataan rivi taulukkoon nimet.push_back(rivi); } // Kaikki rivit on nyt luettu joten voidaan käyttää aineistoa. // Esim tulostamalla ne: for(unsigned int i=0; i<nimet.size(); i++){ std::cout << "nimet[" << i << "] = " << nimet[i] << std::endl; } return 0; }else{ std::cout << "Tiedoston avaaminen ei onnistunut" << std::endl; } tiedosto.close(); }else{ std::cout << "Anna tiedostonimi" << std::endl; return -1; } }
Sienikasvusto kirjoitti:
En yleensä anna suoraa koodia tällaisiin vaan kehotan siirtymään C++ tai C:n refrenssiin, mutta nyt alkoi jalskin vastaus vituttamaan sen verran, että annan suorat vastaukset.
Ehdotin vastaukseksi tavaran tallentamista vectoriin. Oma esimerkkisi näyttäisi tekevän juurikin näin. Jos olet mika132:n aikaisempia viestejä lukenut, niin niissä on käsitelty vectoreita ja tiedostostakin lukemista...
Ai niin, näyttäisi tuo C++ esimerkkisi olevan osapuilleen yhtä pitkä laittamani PL/I toteutuksen kanssa (ota pois turhat tyhjät rivit). Väitätkö muuten tosiaan, että oma if-lause "hirviösi" on helppolukuisempi, kuin muutama lyhyt silmukka ilman yhtään vertailua?
On se huomattavasti helppolukuisempaa ottaen huomioon että c/c++ osastosta on kyse eikä PL/I
C# olisi
StreamReader s = new Streamreader("tiedosto.dat"); String data = s.ReadToEnd(); S.Close(); String[] names = data.Split(new string[] {"/r","/n"});
Mutta tätäkään ei kysytty.
nyt kun tähän mentiin, niin php olisi
$nimet = file('nimet.dat');
Tehkää nyt vielä brainfuckilla. ;)
jalski kirjoitti:
Jos olet mika132:n aikaisempia viestejä lukenut, niin niissä on käsitelty vectoreita ja tiedostostakin lukemista...
Juu. Sama kysymys toistuu tasaisin väliajoin. Tässä niistä muutama:
https://www.ohjelmointiputka.net/keskustelu/
https://www.ohjelmointiputka.net/keskustelu/
https://www.ohjelmointiputka.net/keskustelu/
https://www.ohjelmointiputka.net/keskustelu/
Heippa taas!
... koskapa alkuperäinen kysyjä halusi kaman taulukkoon...
//VC++ 6.0 #include <string> #include <iostream> #include <fstream> #include <sstream> using namespace std; bool taulukoi_nimet(char* filename); string * Nimi; int koko; int main() { if(taulukoi_nimet("nimet.dat")) for(int i=0;i<=koko;i++) cout << Nimi[i] << endl; Nimi = NULL; return 0; } bool taulukoi_nimet(char* filename) { ifstream file; bool palaute; file.open(filename, ios::in | ios::binary); if(file.is_open()) { ostringstream oss; oss << file.rdbuf(); string str = oss.str(); oss.clear(); for (size_t i=0;i<str.length();i++) if(str.substr(i, 1) == "\n") koko++; Nimi = new string[koko]; file.close(); char * temppi = (char *)str.c_str(); char * rivi = strtok(temppi, "\n"); int laskuri = 0; size_t found; string key("\r"); while(rivi != NULL) { Nimi[laskuri] = ((string)rivi); found=Nimi[laskuri].rfind(key); if (found!=string::npos) Nimi[laskuri].replace(found, key.length(), ""); laskuri++; palaute = true; rivi = strtok(NULL, "\n"); } str.erase(); delete [] rivi; temppi = NULL; } return palaute; }
//VC++ .NET using namespace System; using namespace System::IO; int main(array<System::String^> ^args) { cli::array<System::String^>^ Nimi; StreamReader ^ sr = gcnew StreamReader("nimet.dat"); Nimi = sr->ReadToEnd()-> Replace(char("\r"),(char)"")->Split((char)"\n"); sr->Close(); sr = nullptr; for(int i=0;i<Nimi->Length;i++) { Console::WriteLine(Nimi[i]); } Console::WriteLine("Press any key to continue..."); Console::ReadKey(true); delete [] Nimi; Nimi = nullptr; return 0; }
Scheme
(define (read-names input) (let ((line (read input))) (cond ((eof-object? line) nil) (else (cons line (read-names input)))))) (define p (open-input-file "nimet.dat")) (define names (read-names p)) (close-input-port p)
> (car names) Kalle > (cadr names) Harri > (caddr names) Jukka > (cadddr names) Mikko
x86 windows assembly (fasm)
format PE CONSOLE include 'win32a.inc' SEEK_END equ 2 section '.flat' code data import readable executable writeable library msvcrt,'msvcrt.dll' import msvcrt,\ fopen,'fopen', fclose,'fclose', fread,'fread',\ fseek,'fseek', ftell,'ftell', rewind,'rewind',\ printf,'printf', malloc,'malloc', free,'free',\ exit,'exit' Nimi dd 0 entry $ ; open file xor ebx, ebx ; file handle call @f ; push offset ASCII 'rb' to the stack db 'r',0 @@: call @f db 'nimet.dat',0 @@: call [fopen] add esp, 8h ; __cdecl test eax, eax xchg eax, ebx jz .finish ; obtain file size ccall [fseek], ebx, 0, SEEK_END ccall [ftell], ebx inc eax push eax ccall [rewind], ebx ; allocate memory for the file and read it call [malloc] mov edi, eax pop esi ; filesize ccall [fread], edi, 1, esi, ebx test eax, eax jz .finish ; failure ;_; mov esi, eax mov byte [edi+eax], 0h ; NULL-terminate ; count lines (result: edx) push 1 ; shorter than mov edx, 1 ;) pop edx push edi mov ecx, esi mov al, 0ah ; "\n" @@: repnz scasb jnz @f inc edx jmp @b @@: pop edi ; allocate memory for ptrs push edx shl edx, 2 ccall [malloc], edx mov [Nimi], eax pop edx ; line count ; store ptrs to names and substitute newlines with NULLs push edx push edi mov [eax], edi mov esi, eax jmp @f .next: add esi, 4 mov al, 0ah repnz scasb mov byte [edi-1], 0 mov [esi], edi @@: dec edx jnz .next pop edi ; print dem names. note [esp] == linecount mov esi, [Nimi] .namef: lodsd push eax call @f db '%s',0dh,0ah,0 @@: call [printf] add esp, 8h dec dword [esp] jnz .namef pop eax ; clean exit .finish:mov eax, [Nimi] ; free pointers test eax, eax jz @f ccall [free], eax @@: test edi, edi ; and file contents jz @f ccall [free], edi @@: or ebx, ebx ; close handle jz @f ccall [fclose], ebx @@: push 0 call [exit]
Mahtavatko nuo kaikki edes toimia, on sen verran villiäkin meininkiä. Jos
#include <deque> #include <fstream> #include <iostream> #include <string> using namespace std; deque<string> lue_nimet(istream &is); int main() { ifstream tiedosto("nimet.dat"); deque<string> nimet = lue_nimet(tiedosto); for (size_t i = 0; i < nimet.size(); ++i) cout << nimet[i] << "\n"; return 0; }
niin yksinkertainen toteutus voisi olla
#include <deque> #include <istream> #include <iterator> #include <string> deque<string> lue_nimet(istream &is) { istream_iterator<string> begin(is), end; return deque<string>(begin, end); }
Tuo lukee kaikki "sanat" "taulukkoon". Rivit voi lukea
deque<string> lue_nimet(istream &is) { deque<string> nimet; string nimi; while (getline(is, nimi)) nimet.push_back(nimi); return nimet; }
Ainakin neau33:n kannattaisi jättää vastaamatta, kun noinkin lyhyeen koodiin tulee noin iso määrä todella pahoja virheitä. Tuurilla koodi taitaa kuitenkin toimia, jos on 32-bittiset (4-tavuiset) osoittimet, 4-rivinen tiedosto ja Windowsin \r\n-rivinvaihdot ja jos tuon dynaamisen char-taulukon perään sattuu ylimääräinen nollatavu.
Sienikasvuston koodia selventäisi huomattavasti, jos ei olisi noin pitkiä lohkoja if-lauseissa vaan sen sijaan virheenkäsittelyssä käänteinen if-lause, jossa heitettäisiin poikkeus tai suljettaisiin ohjelma. Tulee ihan mieleen aikanaan töissä nähty kommentti: "// tässä if-lauseessa on 800 riviä eikä sillä ole else-lohkoa", vai miten olikaan.
koo teki tyypilliseen tapaansa hyvän esimerkin. Kun includet toistuvat toisessa listauksessa, ehkä myös using-lause olisi selvyyden vuoksi paikallaan.
Pascal, luokka-versio:
var nimet: TStringList; i: integer; begin nimet:=TStringList.Create; nimet.LoadFromFile('nimet.dat'); for i:=0 to nimet.Count-1 do writeln(nimet[i]); // Tulostetaan vielä nimet konsoliin yksi kerrallaan nimet.Free; end;
Aihe on jo aika vanha, joten et voi enää vastata siihen.