Személyes eszközök
Keresés

 

A InfoWiki wikiből

(Változatok közti eltérés)
(Új oldal, tartalma: „<cim cim3="File-ból olvasás és írás" cim2="Magasszintű Programozási Nyelvek I." cim1="Imperatív, procedurális nyelvek alapjai" prev="mp1/page550" next="mp...”)
Aktuális változat (2009. november 14., 22:08) (lapforrás)
 
(9 közbeeső változat nincs mutatva)
1. sor: 1. sor:
<cim  
<cim  
-
cim3="File-ból olvasás és írás"
+
cim3="Vektorok"
cim2="Magasszintű Programozási Nyelvek I."  
cim2="Magasszintű Programozási Nyelvek I."  
cim1="Imperatív, procedurális nyelvek alapjai"  
cim1="Imperatív, procedurális nyelvek alapjai"  
-
prev="mp1/page550"
+
prev="mp1/page310"
-
next="mp1/page900"
+
next="mp1/page400"
kep="hz_csik.png"  
kep="hz_csik.png"  
-
back="EBooks"  
+
back="mp1/Nyitolap"  
szerzo="hz|Hernyák Zoltán"
szerzo="hz|Hernyák Zoltán"
/>
/>
<path p1="Kezdolap|Kezdőlap" p2="EBooks|Jegyzetek" p3="mp1/Nyitolap|Tartalomjegyzék"  
<path p1="Kezdolap|Kezdőlap" p2="EBooks|Jegyzetek" p3="mp1/Nyitolap|Tartalomjegyzék"  
       xx="-"  
       xx="-"  
-
       w1="mp2:Nyitolap|Gyakorlatok" w2="mp1/Tematika|Vizsgatematika" w3="cs:Blog|Programok C# nyelven"
+
       w1="mp2/Nyitolap|Gyakorlatok" w2="mp1/Tematika|Vizsgatematika" w3="cs/Blog|Programok C# nyelven"
/>
/>
-
= Text file =
+
= Vektorok =
-
A program futása során sok adatot képes előállítani, illetve sok adatot kell bekérni, amelyekkel számolni tud. Nagyon gyakori az az eset, amikor az előállított adatokat kiírjuk diszkre valamilyen file-ba. Ennek oka, hogy ott tartósan (a gép kikapcsolása esetén) is megmaradnak ezek a részeredmények, illetve ez az egyik legegyszerűbb módja annak, hogy másik program felé az adatokat publikáljuk (egyik program kiírja, másik program beolvassa).
+
Gyakori probléma, hogy annyi adattal kell dolgoznunk, hogy kényelmetlen és zavaró lenne minden egyes
 +
adatnak saját változót deklarálni. Kényelmesebb lenne egyetlen változót használnunk, amely egy időben
 +
több adat tárolását is képes lenne megoldani. Az elv hasonló, mint a szemétkosár. Kényelmesebb
 +
összegyűjteni a szemétdarabkáinkat egy konténerbe, mint egyesével kezelni őket.  
-
A legegyszerűbb felépítésű file az ún. ''text file'' szerkezet. Az ilyen file-ok sorokra tagoltak, és tartalmukat akár a legegyszerűbb windows-os ''Jegyzettömb'' (notepad) alkalmazással is megtekinthető, szerkeszthető.
+
Ennek feltétele, hogy a sok (több) adat egyforma típusú legyen. A második félévben (OOP) látni
 +
fogjuk, hogy ennek gyakorlatilag semmi akadálya nincs, ha adataink alapvetően különböző típusúnak
 +
látszanak is, akkor is mindíg megoldható, hogy egyforma típusúnak látszódjanak (típuskompatibilitás).
-
A text file-ok sorokat (string) tartalmaznak. Minden sor végét speciális sorvége jel zárja, amely windows-ban a \r\n karakterek (13,10 kódú karakterek), Linux alatt a \n (10 kódú karakter) van használatban. Ez persze nem annyira lényeges információ, csak ha olyan alkalmazást akarunk írni, amely mindkét platformon futásképes kell legyen.
+
Ebben a félévben azonban csak azzal az esettel foglalkozunk, amikor az adataink tényleg egyforma típusúak.
 +
Például egy adott időszak napi átlaghőmérséklet adatai. Ha az időszak két hónap, akkor körülbelül 60 darab
 +
double adattal kell egy időben dolgoznunk.
-
<box type="stop">A text file kezeléshez szükséges dolgok a '''System.IO''' névtérben vannak, ezért a '''using System.IO;''' sort bele kell írni a programunk elejére!</box>
+
<code lang="csharp">
 +
double a = 1.2;
 +
</code>
-
== Text file írása ==
+
A fenti kódban létrehoztunk egyetlen double tárolására képes változót. Az alábbi kódban létrehozunk
-
 
+
egy 60 double tárolására képes változót:
-
Az íráshoz a '''StreamWriter'''-t kell használni, megadván a létrehozandó file nevét:
+
<code lang="csharp">
<code lang="csharp">
-
string fname=@"c:\adatok.txt";
+
double[] t;
-
StreamWriter w = new StreamWriter(fname, false, Encoding.Default);
+
</code>
</code>
-
* az első paraméter (''fname'') adja meg a file nevét, amelybe írni szeretnénk.
+
A fenti ''t'' változó típusában (''double[]'') látszik, hogy itt nem egyetlen double, hanem több double
-
* a második paraméter (''false'') azt adja meg, hogy amennyiben ilyen nevű file lenne már, akkor azt a rendszer törölje le, és üres állapotból induljon a fileba írás. Ha ide ''true''-t adunk meg, akkor a már létező file tartalma megmarad, és amit most írunk bele majd a file-ba, az annak a végére fog íródni (hozzáfűzés, append).
+
adat összefogását kell elvégeznünk. A tényleges darabszám egyelőre nem látszik.
-
* a harmadik paraméter a file-ba írás kódlapját adja meg. Az ''Encoding.Default'' a windows-unkban használt kódlap (alapértelmezett kódlap)
+
-
A fentiek után a file-ba írás máris indulhat. Az íráshoz a már ismerős ''WriteLine''-t kell használni, csak nem a konzolra írunk (''Console.WriteLine''), hanem a file-ba szeretnénk írni, melyet most a ''w'' változónk azonosít. E miatt ''w.WriteLine''-t kell megadnunk:
+
= Összetett adattípusok =
-
<code lang="csharp">
+
A fenti példában szereplő ''t'' változó alapvetően különbözik a korábban használt, egyszerű ''double'' változóktól.
-
double r=23.4;
+
Az alaptípusának neve mögött szereplő két szögletes zárójel a tömb képző típusmódosító operátor jele. Jelenéte
-
w.WriteLine("Programozó=Hernyák Zoltán");
+
arra hívja fel a figyelmet, hogy nem egyetlen double, hanem sok double fog a ''t'' változóba kerülni.
-
w.WriteLine("PI={0}",Math.PI);
+
-
w.WriteLine("sugár={0}",r);
+
-
w.WriteLine("Kerület={0}",2*r*Math.PI);
+
-
...
+
-
</code>
+
-
Ha minden adatot beleírtunk a file-ba, akkor a végén le kell azt zárnunk a ''Close()'' hívásával:
+
Az ilyen típusú változókat, amelyek egy időben több érték tárolására is képesek - '''összetett típusú''' változóknak
 +
nevezzük. Ez a tulajdonságuk alapvetően megkülönbözteti őket az '''egyszerű típusoktól''', amelybe eső változók
 +
egy időben csak egyetlen értéket tartalmazhatnak.
-
<code lang="csharp">
+
= Műveletek =
-
w.Close();
+
-
</code>
+
-
<box type="stop">Amennyiben elfelejtjük lezárni a file-t, úgy a kiírt adatok nem kerülnek bele! Ezért ezt a lépést semiképp ne felejtsük el!</box>
+
Nem kell persze nagy dolgokra számítani. Az összetett típusú változókkal jellemzően nem lehet műveletet végezni,
 +
valójában egy időben mindíg csak egyik elemükkel dolgozunk (egy érték). Ezen egy elemük már elemi típusú értékként
 +
viselkedik, tehát rá minden művelet alkalmazható, amit az adott egyszerű típusra megismertünk.
-
A teljes kód:
+
= Jellemzés =
-
<code lang="csharp">
+
-
using System.IO;
+
-
string fname=@"c:\adatok.txt";
+
Összetett adattípusokból is több van, őket különböző jellemzőik különböztetik meg egymástól:
-
StreamWriter w = new StreamWriter(fname, false, Encoding.Default);
+
-
double r=23.4;
+
-
w.WriteLine("Programozó=Hernyák Zoltán");
+
-
w.WriteLine("PI={0}",Math.PI);
+
-
w.WriteLine("sugár={0}",r);
+
-
w.WriteLine("Kerület={0}",2*r*Math.PI);
+
-
w.Close();
+
-
</code>
+
-
Eredménye megtekinthető a jegyzettömb segítségével:
+
* '''tárolási kapacitás''': ebből alapvetően két fajta van:
 +
** ''statikus kapacitás'': az ilyen típusok képesek ugyan sok adat tárolására, de ezen adatok számát jó előre meg kell határozni. Az adott változó végig ugyanennyi adatot tárol, sem többet sem kevesebbet. Az adatok darabszámát a változó létrehozásakor rögzíteni kell.
 +
** ''dinamikus kapacitás'': az ilyen változókban tárolt adatok száma időben változhat. Új adatot tehetünk be, korábban betett adatot később törölhetünk. A változó létrehozásakor esetleg megadható egy kezdeti darabszám, de ettől később bármikor eltérhetünk könnyedén. Előfordul az az eset is, amikor a változó létrehozásakor még nincs is benne egy adat sem (nulla elemszám).
-
<kep width="440" style="margin-left:125px;" kep1="xx_adatok_txt.png">A file tartalma a Jegyzettömb-ben</kep>
+
* '''memóriabeli elhelyezkedés''': ez a jellemző azt mutatja, hogy a sok adat a memóriában hogyan helyezkedik el. Ezt a jellemzőt ''tárolási reprezentációnak'' is szokták nevezni:
 +
** ''folytonos'': a tárolt adatok a memória egy összefüggő területén helyezkendnek el, általában szorosan egymás mellett, mögött.
 +
** ''szétszórt'': a folytonos ellentéte, az egyes elemek egymástól akár nagyobb távolságra is elhelyezkedhetnek, nem jellemzi ezt a távolságot semmi, oda kerülnek, ahol szabad hely volt a memóriában abban a pillanatban.
-
== File-ból olvasás ==
+
* '''típusosság''': nem feltétlenül igaz, hogy a sok adat mindíg egyforma típusú, bár az adott összetett típus ezt kötelezővé teheti:
 +
** ''homogén'': az ilyen összetett adattípus megköveteli, hogy az összes adat egyforma típusú legyen
 +
** ''inhomogén'': ez esetben nem követelmény, hogy minden adat egyforma típusú legyen
-
A fileból olvasáshoz a '''StreamReader'''-t kell használni. A
 
-
<code lang="csharp">
+
* '''adathozzáférési stratégia''': ez egy bonyolult jellemző. azt mutatja meg, hogy amennyiben szükségünk van valamely, az összetett típusú változónkban tárolt ''n'' sorszámú adatra, akkor milyen lépéssorozat kell az érték előkereséséhez
-
string fname = @"c:\adatok.txt";
+
** ''direkt'' hozzáférés: a legjobb, ez esetben az adott adat sorszámának ismeretében az adat azonnal elérhető. Az adatelérés sebessége (ideje) nem az ''n'' értékétől függ (egyenletes sebesség)
-
StreamReader r = new StreamReader(fname, Encoding.Default);
+
** ''szekvenciális'': az ''n'' sorszámú adat eléréséhez minden előtte lévő elemet el kell érni (a feldolgozás az első adattal kezdődik, és halad sorban amíg el nem érjük az ''n'' sorszámút). Ekkor nyilvánvaló, hogy ha ''n'' kicsi, akkor gyorsabban hozzá tudunk férni az értékhez, mint nagyobb ''n'' esetekben
-
</code>
+
** ''FIFO'': speciálisan a ''sor'' (az ékezetek hiánya nem hiba) adatszerkezet jellemzője. Az adatok csakis abban a sorrendben kerülhetnek feldolgozásra, amilyen sorrendben az adatszerkezetbe bekerültek (first in first out)  
 +
** ''LIFO'': speciális a ''verem'' adatszerkezet jellemzője. Az adatok csakis fordított sorrendben dolgozhatók fel, mint amilyen sorrendben az adatszerkezetbe bekerültek (last in first out)
-
A megnyitás során meg kell adni
+
Vannak további, nagyon speciális adatelérések, mint az indexelt szekvenciális, a hash, a logaritmikus, stb. A fentiekről
-
* a file nevét (''fname'')
+
azért nem írunk részletesen, mert ez inkább az ''Algoritmusok és Adatszerkezetek'' tárgy témája.
-
* a file-ba íráskor használt kódlapot (ez most is a windows-unk alapértelmezett kódlapja lesz, ''Encoding.Default'')
+
-
A file-ból olvasás során a ''ReadLine()''-t kell használni, csak épp most nem a konzolról olvasunk (Console.ReadLine()), hanem a file-ból. A file-unkat most az ''r'' változó képviseli, ezért '''r.ReadLine()'''-al kell olvasni. A ''ReadLine'' most is string-et ad meg. Minden egyes olvasás során a file-unk egy-egy sorát kapjuk meg:
 
-
<code lang="csharp">
+
= Példák =
-
string s;
+
-
s = r.ReadLine(); // "Programozó: Hernyák Zoltán"
+
-
s = r.ReadLine(); // "PI=3,14159265358979"
+
-
s = r.ReadLine(); // "sugár=23,4"
+
-
s = r.ReadLine(); // "Kerület=147,026536188002"
+
-
</code>
+
-
Amikor befejezzük a file-ból olvasást, akkor a file-t le kell zárni a ''Close()'' segítségével:
+
A részletes tárgyalást mellőzve adunk egy listát, amely az egyes (az informatikában előforduló) összetett adatszerkezeteket
 +
jellemzi:
 +
 
 +
* ''tömb'': statikus, homogén, folytonos, direkt
 +
* ''lista'': dinamikus, homogén, folytonos, direkt
 +
* ''láncolt lista'': dinamikus, homogén, szétszórt, szekvenciális
 +
* ''verem'': dinamikus, homogén, folytonos, fifo
 +
* ''sor'': dinamikus, homogén, folytonos, lifo
 +
* ''rekord'': statikus, inhomogén, folytonos, direkt
 +
* ''fa'': dinamikus, homogén, szétszórt, (egyfajta) szekvenciális
 +
* ''szótár'': dinamikus, homogén, folytonos v. szétszórt, hash
 +
 
 +
= Vektorok =
 +
 
 +
A vektorok speciális (egydimenziós) tömbök (igazából a tömb a fenti jellemzőkkel bíró típusok összefoglaló neve).
 +
Ezért a vektorok statikus, homogén, folytonos, direkt elérésű összetett adatszerkezetek.
 +
 
 +
Mivel homogének, létrehozásukkor meg kell adni az alaptípust. Ez szerepel a ''double[]'' típusnévben. A ''[]'' operátor
 +
jelzi, hogy nem egy double-ről beszélünk, hanem több double-ről.
 +
 
 +
A vektor statikus, vagyis induláskor meg kell adni a méretét. Ez nem szerepel a ''double[]'' típusnévben. Ez ugyanis
 +
absztrakt típusnév, az összes double alaptípusból álló vektor közös típusneve. Konkrét double vektor esetén konkrét
 +
méretet kell adni. Ezt a ''t'' változó kezdőértékének beállításakor kell közölni:
<code lang="csharp">
<code lang="csharp">
-
r.Close();
+
double[] t = new double[30];
</code>
</code>
-
A teljes kód:
+
Ebben az esetben a ''new'' operátor a memóriafoglalást jelöli (a konkrét méretű double vektornak konkrét mennyiségű
 +
memóriaterületet kell lefoglalni. Ebben a példában ez 30*8=240 byte. Ezt a new operátor az alaptípus helyigényéből (egy
 +
double 8 byte), és a méretből (30 darab double) önállóan számolja ki (valójában ezt az értéket a fordítóprogram fordításkor
 +
számolja ki, a generált kódban a new mögött már a konkrét 240 érték szerepel, a new-t ugyanis csak a méret érdekli, a
 +
felhasználási cél és mód már nem).
-
<code lang="csharp">
+
Amennyiben a fenti kezdőértékadást nem adjuk ki, a ''t'' változó nem képes egyetlen double adat tárolására sem. Az ilyen
-
using System.IO;
+
próbálkozásokat a fordítóprogram még fordításkor kiszúrja, és szintaktikai hibával leáll. Bár a vektor típusú változó
 +
deklarációja, és a helyfoglalás elválhat egymástól, de ritkán történik ilyen. Jellemzően a vektor deklarálásakor annak
 +
helyfoglalása is megtörténik.
-
string fname = @"c:\adatok.txt";
+
<code lang="csharp">
-
StreamReader r = new StreamReader(fname, Encoding.Default);
+
double[] t;
-
string s;
+
...
-
s = r.ReadLine(); // "Programozó: Hernyák Zoltán"
+
t = new double[30];
-
s = r.ReadLine(); // "PI=3,14159265358979"
+
-
s = r.ReadLine(); // "sugár=23,4"
+
-
s = r.ReadLine(); // "Kerület=147,026536188002"
+
-
r.Close();
+
</code>
</code>
-
== Minden sor beolvasása ==
+
= Vektor feltöltése billentyűzetről =
-
Amennyiben tudjuk előre, hogy a text file-unk 4 sorból áll, akár ciklust is szervezhetünk hozzá:
+
Egy (a példában szereplő) 30 elemű double vektorba 30 különböző double érték tárolható. A vektorok elemei induláskor
 +
típusuknak megfelelő nulla kezdőértékkel rendelkeznek. A szám (int, double,...) típusú vektorok ezért induláskor már
 +
0-kal vannak feltöltve. Ettől eltérő értéket elhelyezhetünk a vektor valamely elemében egy értékadó utasítással.
 +
Ekkor meg kell mondani, melyik elembe kívánunk értéket írni. Az elemeket sorszámukkal azonosítjuk, a sorszámok
 +
0-tól indulnak.
 +
 
 +
<box type="warn">Egy 30 elemű double vektor elemeinek sorszáma 0,1,2,..,28,29. A sorszámok mindíg 0-tól indulnak, még akkor is, ha ez nekünk nem tetszik (idővel meg lehet szokni). Más programozási nyelveknél a szabály eltérő is lehet: a BASIC-ben pl a sorszámozás 1-től indul. Pascal-ban a sorszámozás (szinte) tetszőlegesen választható.</box>
<code lang="csharp">
<code lang="csharp">
-
using System.IO;
+
double[] t = new double[30];
-
 
+
int i = 0;
-
string fname = @"c:\adatok.txt";
+
while (i<30)
-
StreamReader r = new StreamReader(fname, Encoding.Default);
+
-
for (int i=0;i<4;i++)
+
{
{
-
   string s = r.ReadLine();  
+
   Console.Write("Kérek egy számot");
-
   ...
+
   double x = double.Parse( Console.ReadLine() );
 +
  t[i] = x;
 +
  i++;
}
}
-
r.Close();
 
</code>
</code>
-
Ne rontsuk el: a file bezárását nem a ciklusban, hanem a cikluson kívül csak a legvégén kell megtenni!
+
A vektor valamely elemére hivatkozás során meg kell adni a vektor nevét (''t''), és a sorszámot (''i''), a sorszámot
 +
a vektor neve mögé írt szögletes zárójelpárba kell elhelyezni. A ''t[i]'' olvasata: ''a t vektor i. eleme'', vagy
 +
''a t vektor i sorszámú eleme''.
-
Sokkal gyakoribb eset, amikor nem tudjuk előre, hogy a text file-unk hány sorból áll. Ekkor a fenti ''for'' ciklus nem használható, helyette ''while'' ciklust fogalmazunk. A feltételben azt kérdezzük le, hogy elértük-e már (end of file, end of stream) a text file végét.
+
A fenti esetben a tömbbe a billentyűzetről beírt számokat helyezzük el. Figyeljük meg, hogy a sorszámok tárolására,
 +
kezelésére az ''i'' változót használjuk. A sorszámok ''mindíg egész'' számok, egész a sorszámozást végző változónk
 +
mindíg int (vagy valamelyik testvére). A vektor első elemének sorszáma 0, ezért az ''i'' változónkba ezt az értéket
 +
helyezzük el induláskor. Az első bekért szám a ''t[0]'' pozícióra kerül. Az ''i++'' miatt a sorszámunk lép egyet,
 +
és a következő bekért szám már a ''t[1]'' pozícióra kerül. Az ''i<30'' miatt az utolsó sorszám, amire végrehajtódik
 +
a szám elhelyezés a vektorban az a ''t[29]'' pozíció. Ugyan az ''i'' ekkor is nő egyet, de ''i=30''-ra már
 +
nem kerül végrehajtásra a ciklus.
-
<code lang="csharp">
+
<box type="warn">Egy ilyen ''t'' vektornak 0..29 sorszámú elemei vannak. Egyéb sorszámú elemekre (kisebb v. nagyobb sorszámok használata) való hivatkozás futás közbeni hibát generál, és a program azon a ponton leáll. Mindíg nagyon gondoljuk át, nehogy ezt elvétsük!</box>
-
using System.IO;
+
-
string fname = @"c:\adatok.txt";
+
''Megjegyzés'': a fenti kódban az ''x'' változóra igazából nincs szükség, a bekért értéket közvetlenül a vektorba
-
StreamReader r = new StreamReader(fname, Encoding.Default);
+
is elhelyezhetjük:
-
while (!r.EndOfStream)
+
 
 +
<code lang="csharp">
 +
double[] t = new double[30];
 +
int i = 0;
 +
while (i<30)
{
{
-
   string s = r.ReadLine();  
+
   Console.Write("Kérek egy számot");
-
   ...
+
   t[i] = double.Parse( Console.ReadLine() );
 +
  i++;
}
}
-
r.Close();
 
</code>
</code>
-
== Substring ==
+
= Vektor feltöltése véletlen számokkal =
-
Tegyük fel, hogy a beolvasó program szeretné tudni, mennyi volt a sugár értéke. Hogyan szedjük ki ezt az információt a text file-ból?
+
A fenti megoldáshoz nagyon hasonló, amikor a vektort nem billentyűzetről, hanem véletlen számokkal töltjük fel:
-
Ehhez tudnunk kell, hogy a keresett adat a ''sugár='' sorban található, az egyenlőség jel jobb oldalán. Minden egyes sort beolvasunk, keresvén azt a sort, amelyiknek az eleje pontosan ez a karaktersorozat:
 
<code lang="csharp">
<code lang="csharp">
-
while (!r.EndOfStream)
+
double[] t = new double[30];
 +
Random rnd = new Random();
 +
int i = 0;
 +
while (i<30)
{
{
-
   string s = r.ReadLine();  
+
   t[i] = rnd.Next(1,100);
-
   if (s.StartsWith("sugár=")) Console.WriteLine(s);
+
   i++;
-
  ...
+
}
}
</code>
</code>
-
A ''StartsWith''-el lehet a stringünk (sorunk) elejét megvizsgálni. A fenti ''if'' ki fogja keresni ezt a sort, és jelenleg ezt a sort kiírjuk a képernyőre.
+
= Vektor kiírása a képernyőre =
-
De hogyan vegyük ki magát a számértéket a sorból? Le kell választani az ''s'' stringünkből ezt a darabkát a ''Substring()'' segítségével. Ennek meg kell adni:
+
Gyakran van arra szükség, hogy a vektorban lévő elemeket a képernyőre is kiírjuk. Ez szolgálhat ellenőrzési szerepet
-
* hanyadik betűtől akarjuk leválasztani
+
(látni akarjuk, hogy a vektorban tényleg benne vannak a számok, és melyek azok). Amikor a vektort véletlen számokkal
-
* hány betű hosszú darabot
+
töltjuk fel, természetes igény azokat meg is tekinteni.
-
Ezzel két gond is van: a ''hanyadik betű'' megadásakor ügyeljünk rá, hogy a betűk sorszámozása 0-tól kezdődik. A ''sugár=23,4'' szövegünkben a ''23,4'' szakasz a 6 betűtől kezdődik, és 4 betű hosszú:
+
<code lang="csharp">
 +
...
 +
int i = 0;
 +
while (i<30)
 +
{
 +
  Console.WriteLine("{0}. sorszámú elem={1}",i,t[i]);
 +
  i++;
 +
}
 +
</code>
 +
 
 +
Amikor sok elemről van szó, egy takarékosabb megoldást szoktunk választani: az elemeket a képernyőre
 +
egymás mellé, pl. vesszővel elválasztva írjuk ki:
<code lang="csharp">
<code lang="csharp">
-
if (s.StartsWith("sugár="))  
+
...
 +
int i = 0;
 +
while (i<30)
{
{
-
  string szam = s.Substring(6,4); // "23,4"
+
   Console.Write("{0}, ",t[i]);
-
  double sugar = double.Parse(szam);
+
  i++;
-
   Console.WriteLine("sugar beolvasas utan={0}",sugar);
+
}
}
 +
Console.WriteLine();
</code>
</code>
-
== Split ==
+
A bezáró üres ''Console.WriteLine()'' szerepe, hogy az utolsóként kiírt vektorelem mögött álló kurzort
 +
a következő sor elejére rakja át, így megakadályozva, hogy a program következő kiírásai ugyanide kerüljenek.
 +
 
 +
= Vektor mérete =
 +
 
 +
A vektor mérete létrehozásakor adott. Ugyanakkor a vele dolgozó ciklusokban vezérlő feltételében is folyton
 +
előkerül. Amennyiben változik a vektor mérete, a ciklusok vezérlő feltételét is át kell(ene) írni. Erre
 +
a problémára két megoldás szokott születni.
 +
 
 +
Az első szerint a vektor méretét egy konstanban (vagy változóban) tároljuk el, és a ciklusokban is erre
 +
az értékre hivatkozunk:
-
Másik (és sokkal jobb módszer) a ''Split'' használata. A Split segítségével egy sztringet több felé vághatunk szét valamilyen elválasztó karakter mentén. A  ''sugár=23,4'' sort kettévághatjuk az '''=''' jel mentén két darabra, így különválik a bal oldali rész ''sugár'' és a jobb oldali rész ''23,4''. A kettévágás egy string[] tömböt eredményez. A tömbünk két elemű lesz, a 0 sorszámú elem az első darab, a 1 sorszámú elem pedig a második darabka lesz:
 
<code lang="csharp">
<code lang="csharp">
-
if (s.StartsWith("sugár="))  
+
const int N = 20;
 +
bool[] l = new bool[N];
 +
int i = 0;
 +
while (i<N)
{
{
-
   string[] darabok = s.Split('='); // "23,4"
+
   t[i] = ...;
-
  double sugar = double.Parse(darabok[1]);
+
   i++;
-
   Console.WriteLine("sugar beolvasas utan={0}",sugar);
+
}
}
</code>
</code>
-
Miért jobb a ''Split'' mint a ''Substring''? Ugyanis ha az értékünk nem ''23,4'', hanem mondjuk ''123,345''', amely már nem 4 karakter hosszú, akkor a fenti Substring máris nem jól működik. Van persze arra mód, hogy ezen segítsünk a string hosszából (s.Length) kiindulva, de a ''Split'' annyival egyszerűbben működik, hogy kár ezzel foglalkozni.
+
<code lang="csharp">
 +
Console.WriteLine("hány adatról van szó:");
 +
int N = int.Parse( Console.ReadLine() );
 +
bool[] l = new bool[N];
 +
int i = 0;
 +
while (i<N)
 +
{
 +
  l[i] = ...;
 +
  i++;
 +
}
 +
</code>
 +
Ez a megoldás szinte minden programozási nyelvben változatlan formában létezik. Néhány programozási nyelv (mint
 +
pl a PHP is) függvényt biztosít a vektor méretének utólagos lekérdezésére (ekkor ez a szám használható a
 +
vezérlő feltételben):
 +
<code lang="php">
 +
$l = array(2,3,5);
 +
...
 +
int i = 0;
 +
while (i<sizeof($t))
 +
{
 +
  l[i] = ...;
 +
  i++;
 +
}
 +
</code>
 +
 +
A C# vektorai azonban önállóan is intelligens tárgyak (objektumok :). Egy C# vektortól mindíg meg lehet kérdezni,
 +
mekkora méretű. Erre a ''Length'' jellemzője (length=hossz) szolgál. Meg kell adni, melyik vektornak a méretét
 +
kívánjuk megtudni. A vektor neve és a ''Length'' szó közé pontot teszünk. Az ''l.Length'' ebben a példában
 +
a vektor összes elemszámát (20) képviseli a while vezérlő feltételében.
 +
 +
<code lang="csharp">
 +
bool[] l = new bool[20];
 +
int i = 0;
 +
while (i<l.Length)
 +
{
 +
  t[i] = ...;
 +
  i++;
 +
}
 +
</code>
<alairas>Hernyák Zoltán</alairas>
<alairas>Hernyák Zoltán</alairas>
__NOTOC__
__NOTOC__

Aktuális változat


Vektorok

Gyakori probléma, hogy annyi adattal kell dolgoznunk, hogy kényelmetlen és zavaró lenne minden egyes adatnak saját változót deklarálni. Kényelmesebb lenne egyetlen változót használnunk, amely egy időben több adat tárolását is képes lenne megoldani. Az elv hasonló, mint a szemétkosár. Kényelmesebb összegyűjteni a szemétdarabkáinkat egy konténerbe, mint egyesével kezelni őket.

Ennek feltétele, hogy a sok (több) adat egyforma típusú legyen. A második félévben (OOP) látni fogjuk, hogy ennek gyakorlatilag semmi akadálya nincs, ha adataink alapvetően különböző típusúnak látszanak is, akkor is mindíg megoldható, hogy egyforma típusúnak látszódjanak (típuskompatibilitás).

Ebben a félévben azonban csak azzal az esettel foglalkozunk, amikor az adataink tényleg egyforma típusúak. Például egy adott időszak napi átlaghőmérséklet adatai. Ha az időszak két hónap, akkor körülbelül 60 darab double adattal kell egy időben dolgoznunk.

double a = 1.2;

A fenti kódban létrehoztunk egyetlen double tárolására képes változót. Az alábbi kódban létrehozunk egy 60 double tárolására képes változót:

double[] t;

A fenti t változó típusában (double[]) látszik, hogy itt nem egyetlen double, hanem több double adat összefogását kell elvégeznünk. A tényleges darabszám egyelőre nem látszik.

Összetett adattípusok

A fenti példában szereplő t változó alapvetően különbözik a korábban használt, egyszerű double változóktól. Az alaptípusának neve mögött szereplő két szögletes zárójel a tömb képző típusmódosító operátor jele. Jelenéte arra hívja fel a figyelmet, hogy nem egyetlen double, hanem sok double fog a t változóba kerülni.

Az ilyen típusú változókat, amelyek egy időben több érték tárolására is képesek - összetett típusú változóknak nevezzük. Ez a tulajdonságuk alapvetően megkülönbözteti őket az egyszerű típusoktól, amelybe eső változók egy időben csak egyetlen értéket tartalmazhatnak.

Műveletek

Nem kell persze nagy dolgokra számítani. Az összetett típusú változókkal jellemzően nem lehet műveletet végezni, valójában egy időben mindíg csak egyik elemükkel dolgozunk (egy érték). Ezen egy elemük már elemi típusú értékként viselkedik, tehát rá minden művelet alkalmazható, amit az adott egyszerű típusra megismertünk.

Jellemzés

Összetett adattípusokból is több van, őket különböző jellemzőik különböztetik meg egymástól:

  • tárolási kapacitás: ebből alapvetően két fajta van:
    • statikus kapacitás: az ilyen típusok képesek ugyan sok adat tárolására, de ezen adatok számát jó előre meg kell határozni. Az adott változó végig ugyanennyi adatot tárol, sem többet sem kevesebbet. Az adatok darabszámát a változó létrehozásakor rögzíteni kell.
    • dinamikus kapacitás: az ilyen változókban tárolt adatok száma időben változhat. Új adatot tehetünk be, korábban betett adatot később törölhetünk. A változó létrehozásakor esetleg megadható egy kezdeti darabszám, de ettől később bármikor eltérhetünk könnyedén. Előfordul az az eset is, amikor a változó létrehozásakor még nincs is benne egy adat sem (nulla elemszám).
  • memóriabeli elhelyezkedés: ez a jellemző azt mutatja, hogy a sok adat a memóriában hogyan helyezkedik el. Ezt a jellemzőt tárolási reprezentációnak is szokták nevezni:
    • folytonos: a tárolt adatok a memória egy összefüggő területén helyezkendnek el, általában szorosan egymás mellett, mögött.
    • szétszórt: a folytonos ellentéte, az egyes elemek egymástól akár nagyobb távolságra is elhelyezkedhetnek, nem jellemzi ezt a távolságot semmi, oda kerülnek, ahol szabad hely volt a memóriában abban a pillanatban.
  • típusosság: nem feltétlenül igaz, hogy a sok adat mindíg egyforma típusú, bár az adott összetett típus ezt kötelezővé teheti:
    • homogén: az ilyen összetett adattípus megköveteli, hogy az összes adat egyforma típusú legyen
    • inhomogén: ez esetben nem követelmény, hogy minden adat egyforma típusú legyen


  • adathozzáférési stratégia: ez egy bonyolult jellemző. azt mutatja meg, hogy amennyiben szükségünk van valamely, az összetett típusú változónkban tárolt n sorszámú adatra, akkor milyen lépéssorozat kell az érték előkereséséhez
    • direkt hozzáférés: a legjobb, ez esetben az adott adat sorszámának ismeretében az adat azonnal elérhető. Az adatelérés sebessége (ideje) nem az n értékétől függ (egyenletes sebesség)
    • szekvenciális: az n sorszámú adat eléréséhez minden előtte lévő elemet el kell érni (a feldolgozás az első adattal kezdődik, és halad sorban amíg el nem érjük az n sorszámút). Ekkor nyilvánvaló, hogy ha n kicsi, akkor gyorsabban hozzá tudunk férni az értékhez, mint nagyobb n esetekben
    • FIFO: speciálisan a sor (az ékezetek hiánya nem hiba) adatszerkezet jellemzője. Az adatok csakis abban a sorrendben kerülhetnek feldolgozásra, amilyen sorrendben az adatszerkezetbe bekerültek (first in first out)
    • LIFO: speciális a verem adatszerkezet jellemzője. Az adatok csakis fordított sorrendben dolgozhatók fel, mint amilyen sorrendben az adatszerkezetbe bekerültek (last in first out)

Vannak további, nagyon speciális adatelérések, mint az indexelt szekvenciális, a hash, a logaritmikus, stb. A fentiekről azért nem írunk részletesen, mert ez inkább az Algoritmusok és Adatszerkezetek tárgy témája.


Példák

A részletes tárgyalást mellőzve adunk egy listát, amely az egyes (az informatikában előforduló) összetett adatszerkezeteket jellemzi:

  • tömb: statikus, homogén, folytonos, direkt
  • lista: dinamikus, homogén, folytonos, direkt
  • láncolt lista: dinamikus, homogén, szétszórt, szekvenciális
  • verem: dinamikus, homogén, folytonos, fifo
  • sor: dinamikus, homogén, folytonos, lifo
  • rekord: statikus, inhomogén, folytonos, direkt
  • fa: dinamikus, homogén, szétszórt, (egyfajta) szekvenciális
  • szótár: dinamikus, homogén, folytonos v. szétszórt, hash

Vektorok

A vektorok speciális (egydimenziós) tömbök (igazából a tömb a fenti jellemzőkkel bíró típusok összefoglaló neve). Ezért a vektorok statikus, homogén, folytonos, direkt elérésű összetett adatszerkezetek.

Mivel homogének, létrehozásukkor meg kell adni az alaptípust. Ez szerepel a double[] típusnévben. A [] operátor jelzi, hogy nem egy double-ről beszélünk, hanem több double-ről.

A vektor statikus, vagyis induláskor meg kell adni a méretét. Ez nem szerepel a double[] típusnévben. Ez ugyanis absztrakt típusnév, az összes double alaptípusból álló vektor közös típusneve. Konkrét double vektor esetén konkrét méretet kell adni. Ezt a t változó kezdőértékének beállításakor kell közölni:

double[] t = new double[30];

Ebben az esetben a new operátor a memóriafoglalást jelöli (a konkrét méretű double vektornak konkrét mennyiségű memóriaterületet kell lefoglalni. Ebben a példában ez 30*8=240 byte. Ezt a new operátor az alaptípus helyigényéből (egy double 8 byte), és a méretből (30 darab double) önállóan számolja ki (valójában ezt az értéket a fordítóprogram fordításkor számolja ki, a generált kódban a new mögött már a konkrét 240 érték szerepel, a new-t ugyanis csak a méret érdekli, a felhasználási cél és mód már nem).

Amennyiben a fenti kezdőértékadást nem adjuk ki, a t változó nem képes egyetlen double adat tárolására sem. Az ilyen próbálkozásokat a fordítóprogram még fordításkor kiszúrja, és szintaktikai hibával leáll. Bár a vektor típusú változó deklarációja, és a helyfoglalás elválhat egymástól, de ritkán történik ilyen. Jellemzően a vektor deklarálásakor annak helyfoglalása is megtörténik.

double[] t;
...
t = new double[30];


Vektor feltöltése billentyűzetről

Egy (a példában szereplő) 30 elemű double vektorba 30 különböző double érték tárolható. A vektorok elemei induláskor típusuknak megfelelő nulla kezdőértékkel rendelkeznek. A szám (int, double,...) típusú vektorok ezért induláskor már 0-kal vannak feltöltve. Ettől eltérő értéket elhelyezhetünk a vektor valamely elemében egy értékadó utasítással. Ekkor meg kell mondani, melyik elembe kívánunk értéket írni. Az elemeket sorszámukkal azonosítjuk, a sorszámok 0-tól indulnak.

Egy 30 elemű double vektor elemeinek sorszáma 0,1,2,..,28,29. A sorszámok mindíg 0-tól indulnak, még akkor is, ha ez nekünk nem tetszik (idővel meg lehet szokni). Más programozási nyelveknél a szabály eltérő is lehet: a BASIC-ben pl a sorszámozás 1-től indul. Pascal-ban a sorszámozás (szinte) tetszőlegesen választható.


double[] t = new double[30];
int i = 0;
while (i<30)
{
  Console.Write("Kérek egy számot");
  double x = double.Parse( Console.ReadLine() );
  t[i] = x;
  i++;
}

A vektor valamely elemére hivatkozás során meg kell adni a vektor nevét (t), és a sorszámot (i), a sorszámot a vektor neve mögé írt szögletes zárójelpárba kell elhelyezni. A t[i] olvasata: a t vektor i. eleme, vagy a t vektor i sorszámú eleme.

A fenti esetben a tömbbe a billentyűzetről beírt számokat helyezzük el. Figyeljük meg, hogy a sorszámok tárolására, kezelésére az i változót használjuk. A sorszámok mindíg egész számok, egész a sorszámozást végző változónk mindíg int (vagy valamelyik testvére). A vektor első elemének sorszáma 0, ezért az i változónkba ezt az értéket helyezzük el induláskor. Az első bekért szám a t[0] pozícióra kerül. Az i++ miatt a sorszámunk lép egyet, és a következő bekért szám már a t[1] pozícióra kerül. Az i<30 miatt az utolsó sorszám, amire végrehajtódik a szám elhelyezés a vektorban az a t[29] pozíció. Ugyan az i ekkor is nő egyet, de i=30-ra már nem kerül végrehajtásra a ciklus.

Egy ilyen t vektornak 0..29 sorszámú elemei vannak. Egyéb sorszámú elemekre (kisebb v. nagyobb sorszámok használata) való hivatkozás futás közbeni hibát generál, és a program azon a ponton leáll. Mindíg nagyon gondoljuk át, nehogy ezt elvétsük!


Megjegyzés: a fenti kódban az x változóra igazából nincs szükség, a bekért értéket közvetlenül a vektorba is elhelyezhetjük:

double[] t = new double[30];
int i = 0;
while (i<30)
{
  Console.Write("Kérek egy számot");
  t[i] = double.Parse( Console.ReadLine() );
  i++;
}

Vektor feltöltése véletlen számokkal

A fenti megoldáshoz nagyon hasonló, amikor a vektort nem billentyűzetről, hanem véletlen számokkal töltjük fel:


double[] t = new double[30];
Random rnd = new Random();
int i = 0;
while (i<30)
{
  t[i] = rnd.Next(1,100);
  i++;
}

Vektor kiírása a képernyőre

Gyakran van arra szükség, hogy a vektorban lévő elemeket a képernyőre is kiírjuk. Ez szolgálhat ellenőrzési szerepet (látni akarjuk, hogy a vektorban tényleg benne vannak a számok, és melyek azok). Amikor a vektort véletlen számokkal töltjuk fel, természetes igény azokat meg is tekinteni.

...
int i = 0;
while (i<30)
{
  Console.WriteLine("{0}. sorszámú elem={1}",i,t[i]);
  i++;
}

Amikor sok elemről van szó, egy takarékosabb megoldást szoktunk választani: az elemeket a képernyőre egymás mellé, pl. vesszővel elválasztva írjuk ki:

...
int i = 0;
while (i<30)
{
  Console.Write("{0}, ",t[i]);
  i++;
}
Console.WriteLine();

A bezáró üres Console.WriteLine() szerepe, hogy az utolsóként kiírt vektorelem mögött álló kurzort a következő sor elejére rakja át, így megakadályozva, hogy a program következő kiírásai ugyanide kerüljenek.

Vektor mérete

A vektor mérete létrehozásakor adott. Ugyanakkor a vele dolgozó ciklusokban vezérlő feltételében is folyton előkerül. Amennyiben változik a vektor mérete, a ciklusok vezérlő feltételét is át kell(ene) írni. Erre a problémára két megoldás szokott születni.

Az első szerint a vektor méretét egy konstanban (vagy változóban) tároljuk el, és a ciklusokban is erre az értékre hivatkozunk:


const int N = 20;
bool[] l = new bool[N];
int i = 0;
while (i<N)
{
  t[i] = ...;
  i++;
}
Console.WriteLine("hány adatról van szó:");
int N = int.Parse( Console.ReadLine() );
bool[] l = new bool[N];
int i = 0;
while (i<N)
{
  l[i] = ...;
  i++;
}

Ez a megoldás szinte minden programozási nyelvben változatlan formában létezik. Néhány programozási nyelv (mint pl a PHP is) függvényt biztosít a vektor méretének utólagos lekérdezésére (ekkor ez a szám használható a vezérlő feltételben):

$l = array(2,3,5);
...
 
int i = 0;
while (i<sizeof($t))
{
  l[i] = ...;
  i++;
}

A C# vektorai azonban önállóan is intelligens tárgyak (objektumok :). Egy C# vektortól mindíg meg lehet kérdezni, mekkora méretű. Erre a Length jellemzője (length=hossz) szolgál. Meg kell adni, melyik vektornak a méretét kívánjuk megtudni. A vektor neve és a Length szó közé pontot teszünk. Az l.Length ebben a példában a vektor összes elemszámát (20) képviseli a while vezérlő feltételében.

bool[] l = new bool[20];
int i = 0;
while (i<l.Length)
{
  t[i] = ...;
  i++;
}
Hernyák Zoltán
A lap eredeti címe: „http://wiki.ektf.hu/wiki/Mp1/page320
Nézetek
nincs sb_3.138.200.66 cikk