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.