19. oktober 2004 - 13:05Der er
17 kommentarer og 1 løsning
Tekst database, skriv/læs/formater
Hej..
Jeg har nogle problemer med at bruge tekstfil som db og trække info ud i en TListView, som jeg håber nogen kan hjælpe med.
Jeg har 4 variabler (lad os bare kalde dem str1, str2, str3, str4 - string) som skal gemmes i db-filen ved tryk på en knap "Save".
Ligeledes kan jeg ikke rigtigt hitte ud af en funktion der skal trække info ud fra filen og liste dem i en TListView i str1 står i column1, str2 står i column2 osv..
Det bliver jo aldrig helt perfekt med ascii-tabeller, men jeg har en TTable, sat op til at være en ASCII tabel. Den er defineret med fire felter str1-4 og er helt basal. Mit listview har tilsvarende kolonner og så er der endelig to knapper:
procedure TfrmMain.LaesButtonClick(Sender: TObject); begin // Bemærk: Ingen tjek om filen findes eller ej AsciiTable.Open; lvStrenge.Items.BeginUpdate; try lvStrenge.Items.Clear; while not AsciiTable.Eof do begin with lvStrenge.Items.Add do begin Caption := AsciiTable.Fields[0].AsString; SubItems.Add(AsciiTable.Fields[1].AsString); SubItems.Add(AsciiTable.Fields[2].AsString); SubItems.Add(AsciiTable.Fields[3].AsString); end; AsciiTable.Next; end; finally AsciiTable.Close; lvStrenge.Items.EndUpdate; end; end;
procedure TfrmMain.GemButtonClick(Sender: TObject); var i : integer; begin AsciiTable.CreateTable; AsciiTable.Open; try for i := 0 to lvStrenge.Items.Count - 1 do with lvStrenge.Items[i] do AsciiTable.AppendRecord([Caption,SubItems[0],SubItems[1],SubItems[2]]); finally AsciiTable.Close; end; end;
Jamn' det bliver de også. Man kan tage en BDE-TTable og sætte dens TableType = ttAscii. Man skal selv definere felterne i tabellen via FieldDefs, men så er du klar. Resultatet er en fast-form tekstfil som du kan læse ind via en TTable og du skrev jo selv "textfil som DB" - og det er hvad det er.
Jeg syntes det var det letteste at lave og resultatet blev jo en tekstfil. Ulempen er at der er en begrænsning på strengstørrelsen, max 255 tegn pr. streng.
Hvis du gerne vil have en anden løsning så hedder det enten gammeldags TextFile eller nymodens TStream m. TFiler objekter.
Nu er jeg ikke så vild til det her Delphi sjov.. kunne jeg (udover det ovenover) få dig til at skrive det der skal med?
Jeg går ud fra nogle ting skal declares.. TTable og ting..? Tror desværre jeg er nødt til at få det serveret på et sølvfad for at kunne se hvordan det hænger sammen med det eks. her :/
Altså... har fundet ud af det med TTable og sårn.. men den prøver at skrive lvStrenge'ene i filen ikk? Den skal jo skrive str1-4 deri.. det kan jeg ikke se der sker? :/
procedure TfrmMain.LaesButtonClick(Sender: TObject); begin AsciiTable.Open; lvStrenge.Items.BeginUpdate; try lvStrenge.Items.Clear; while not AsciiTable.Eof do begin with lvStrenge.Items.Add do begin Caption := AsciiTable.Fields[0].AsString; SubItems.Add(AsciiTable.Fields[1].AsString); SubItems.Add(AsciiTable.Fields[2].AsString); SubItems.Add(AsciiTable.Fields[3].AsString); end; AsciiTable.Next; end; finally AsciiTable.Close; lvStrenge.Items.EndUpdate; end; end;
procedure TfrmMain.GemButtonClick(Sender: TObject); var i : integer; begin AsciiTable.CreateTable; AsciiTable.Open; try for i := 0 to lvStrenge.Items.Count - 1 do with lvStrenge.Items[i] do AsciiTable.AppendRecord([Caption,SubItems[0],SubItems[1],SubItems[2]]); finally AsciiTable.Close; end; end;
end.
Dernæst dfm-filen:
object frmMain: TfrmMain Left = 419 Top = 204 Width = 273 Height = 261 Caption = 'frmMain' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False DesignSize = ( 265 234) PixelsPerInch = 96 TextHeight = 13 object lvStrenge: TListView Left = 8 Top = 8 Width = 250 Height = 185 Anchors = [akLeft, akTop, akRight, akBottom] Columns = < item Caption = 'Str1' end item Caption = 'Str2' end item Caption = 'Str3' end item Caption = 'Str4' end> Items.Data = { 890000000300000000000000FFFFFFFFFFFFFFFF030000000000000003546865 05717569636B0562726F776E03466F7800000000FFFFFFFFFFFFFFFF03000000 00000000056A756D7073046F76657203746865046C617A7900000000FFFFFFFF FFFFFFFF030000000000000004446F677301E601F801E5FFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFF} ReadOnly = True TabOrder = 0 ViewStyle = vsReport end object LaesButton: TButton Left = 8 Top = 200 Width = 75 Height = 25 Anchors = [akTop, akBottom] Caption = 'Læs' TabOrder = 1 OnClick = LaesButtonClick end object GemButton: TButton Left = 88 Top = 200 Width = 75 Height = 25 Anchors = [akTop, akBottom] Caption = 'Gem' TabOrder = 2 OnClick = GemButtonClick end object AsciiTable: TTable DatabaseName = 'AsciiLink' FieldDefs = < item Name = 'Str1' DataType = ftString Size = 40 end item Name = 'Str2' DataType = ftString Size = 40 end item Name = 'Str3' DataType = ftString Size = 40 end item Name = 'Str4' DataType = ftString Size = 40 end> StoreDefs = True TableName = 'Strenge.Txt' TableType = ttASCII Left = 176 Top = 200 end end
Gem disse to filer som hhv. FMain.pas og FMain.dfm og inkludér dem til et projekt vha. <Shift-F11>
Glemte lige at nævne at du under Kontrolpanel/BDE Administrator skal lave et alias der hedder AsciiLink.
Du skal naturligvis også fjerne den eksisterende mainform før du inkluderer FMain ovenfor - ellers bliver den ikke den primære => den oprettes og vises automatisk.
1) Jeg kan ikke finde nogen BDE administrator .. 2) dfm-fil? Har jeg ikke set før, ved jeg ikke hvordan jeg opretter..
Er det nødvendigt? Kan det ikke bare klares i en procedure/funktion i mainform bare? Ellers har jeg vist rodet mig ud hvor jeg ikke kan bunde, selvom opgaven virkede rimelig do-able :/
Dum, sikkert ikke, nok nærmere mig der vælger en "interessant" løsning...
ad. 1: Hvis du har installeret Delphi så burde der være en DBE administrator under XP's kontrolpanel. Det er i alt fald der jeg plejer at finde den. Prøv evt. at gennemsøge din harddisk efter bdeadmin.exe, det er den vi leder efter.
ad. 2: En Delphi form (vindue) består af en kodedel (*.pas) og en visuel del (*.dfm) De hedder altid det samme, med forskellige ekstensioner.
Mht. dine tvivl om projektets gennemførsel. Måske er det den forkerte vej, men det vi roder med, er jo ikke selve løsningen men det at nå frem til teste den. Det er noget der har med generel Delphi-erfaring at gøre. Jeg vil godt prøve at lave en anden version som ikke involverer TTable (og dermed et alias i BDE), men jeg kørte nok af sporet da du begyndte at snakke om tekstdatabaser.
Dette her burde også virke og det er helt basalt med en tekstfil hvor hver kolonne i en linie i et listview har sin egen linie (så er hverken længden eller eventuelle separatorer nemlig ikke et problem)
const Datafil = 'Data.txt';
procedure TfrmMain.LaesButtonClick(Sender: TObject); var i : integer; fp : TextFile; ListItem : TListItem; begin assignfile(fp,Datafil); if not FileExists(Datafil) then exit;
// Vi har fire linier, ‚n til hver kolonne i ‚n linie i et listview reset(fp); i := -1; lvStrenge.Items.BeginUpdate; try lvStrenge.Items.Clear; while not fp.Eof do begin inc(i); case i mod 4 of 0 : begin ListItem := lvStrenge.Items.Add; readln(fp,ListItem.Caption); end; 1..3 : begin readln(fp,st); ListItem.SubItem.Add(st); end; end; end; finally closefile(fp); lvStrenge.Items.EndUpdate; end; end;
procedure TfrmMain.GemButtonClick(Sender: TObject); var i : integer; fp : TextFile; ListItem : TListItem; begin assignfile(fp,Datafil); rewrite(fp); try for i := 0 to lvStrenge.Items.Count - 1 do with lvStrenge.Items[i] do begin writeln(fp,Caption); writeln(fp,SubItem[0]); writeln(fp,SubItem[1]); writeln(fp,SubItem[2]); end; finally closefile(fp); end; end;
Den compiler fint med BtnSaveClick, men jeg får en "List index out of bounds(0)" fejl når jeg trykker på den. Jeg har følgende:
const Datafil = 'Data.txt';
procedure TForm1.BtnSaveClick(Sender: TObject); var i : integer; fp : TextFile; ListItem : TListItem; begin assignfile(fp,Datafil); rewrite(fp); try for i := 0 to lvStrenge.Items.Count - 1 do with lvStrenge.Items[i] do begin writeln(fp,Caption); writeln(fp,SubItems[0]); writeln(fp,SubItems[1]); writeln(fp,SubItems[2]); end; finally closefile(fp); end; end;
(nu starter jeg lige BtnSaveClick, så kan den anden komme bagefter :D)
Du har nok ikke tre subitems at gemme, har du? Jeg antager i eksemplet ovenfor at der er add'et 3 subitems (eks. ListItem.SubItems.Add()). Du kan evt prøve denne variant:
procedure TForm1.BtnSaveClick(Sender: TObject); var i,j : integer; fp : TextFile; ListItem : TListItem; begin assignfile(fp,Datafil); rewrite(fp); try for i := 0 to lvStrenge.Items.Count - 1 do with lvStrenge.Items[i] do begin writeln(fp,Caption); for j := 0 to SubItems.Count - 1 do writeln(fp,SubItems[j]); end; finally closefile(fp); end; end;
Så burde den være ligeglad med hvad der er defineret i listviewet, men det bliver svært at indlæse igen når man ikke kender antallet af subitems.
procedure TForm1.BtnSaveClick(Sender: TObject); var i : integer; fp : TextFile; begin assignfile(fp,Datafil); rewrite(fp); if ((Edit1.Text <> '') AND (Edit3.Text <> '') AND (Edit4.Text <> '')) then begin writeln(fp,Edit1.Text); writeln(fp,ComboYear.Text); writeln(fp,Edit3.Text); writeln(fp,Edit4.Text); end; closefile(fp); end;
Dette virker nu, og den skriver i Data.txt med linieskift for hver .Text der er input.
Min TForm1.BtnLoadClick() skal så læse fra Data.txt ind i min lvStrenge (TListView) hvor der er 4 kolonner - altså Caption og 3 subitems.. men hvordan læser jeg data filen gennem og loader dem ind i den listview? :/
Det var som sagt ikke testet, men det burde da heller ikke være så svært at hitte ud af:
procedure TfrmMain.LaesButtonClick(Sender: TObject); var i : integer; fp : TextFile; ListItem : TListItem; st : string; // (3) Den var ikke oprettet (ganske som der stod beskrevet) begin assignfile(fp,Datafil); if not FileExists(Datafil) then exit;
// Vi har fire linier, ‚n til hver kolonne i ‚n linie i et listview reset(fp); i := -1; lvStrenge.Items.BeginUpdate; try lvStrenge.Items.Clear; while not Eof(fp) do // (1) Denne her burde du have rettet selv begin inc(i); case i mod 4 of 0 : begin ListItem := lvStrenge.Items.Add; readln(fp,st); // (2) Denne havde jeg nævnt måske ville fejle ListItem.Caption := st; end; 1..3 : begin readln(fp,st); ListItem.SubItems.Add(st); // (3b) Manglede lige et s end; end; end; finally closefile(fp); lvStrenge.Items.EndUpdate; end; end;
procedure TfrmMain.btnSaveClick(Sender: TObject); var fs : TFileStream; Writer : TWriter; begin if FileExists(FileName) then begin fs := TFileStream.Create(FileName,fmOpenRead); fs.Seek(0,soFromEnd); // Burde få den til at gå til slut af fil end else fs := TFileStream.Create(FileName,fmCreate);
procedure TfrmMain.btnLoadClick(Sender: TObject); var fs : TFileStream; Reader : TReader; ListItem : TListItem; begin if not FileExists(FileName) then exit;
fs := TFileStream.Create(FileName,fmOpenRead); Reader := TReader.Create(fs,1024); try while fs.Position < fs.Size do begin ListItem := lvStrings.Items.Add; ListItem.Caption := Reader.ReadString; ListItem.SubItems.Add(Reader.ReadString); ListItem.SubItems.Add(Reader.ReadString); ListItem.SubItems.Add(Reader.ReadString); end; finally Reader.Free; fs.Free; end; end;
Det er ikke længere en tekstfil, men til gengæld er der ikke rigtig nogen begrænsning på hvad du fylder i filen (buffer her sat til 1kb). At vi kun bruger strenge her, gør det måske lidt unødvendigt.
Det vigtigste ved eksemplet er TWriter og TReader objekter som er geniale til at hente strenge (og alt andet) ud af en stream. Hvis du prøver at arbejde med streams så skal du også prøve disse her.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.