Avatar billede jackass- Nybegynder
19. oktober 2004 - 13:05 Der 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..

Plz enlighten me :)
Avatar billede hrc Mester
19. oktober 2004 - 14:49 #1
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;
Avatar billede jackass- Nybegynder
19. oktober 2004 - 14:58 #2
Data'en skal gemmes i en fil..?
Avatar billede hrc Mester
19. oktober 2004 - 16:10 #3
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.
Avatar billede jackass- Nybegynder
19. oktober 2004 - 20:39 #4
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 :/
Avatar billede jackass- Nybegynder
19. oktober 2004 - 22:06 #5
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? :/
Avatar billede jackass- Nybegynder
19. oktober 2004 - 22:48 #6
Og str1-4 er:

str1 := edit1.text;
osv..
Avatar billede hrc Mester
20. oktober 2004 - 10:25 #7
lvStrenge er mit TListView (derfor det foranstillede lv). Du får lige pas og dfm-filen her.

Først pas-filen:
unit FMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, DB, DBTables;

type
  TfrmMain = class(TForm)
    lvStrenge: TListView;
    LaesButton: TButton;
    GemButton: TButton;
    AsciiTable: TTable;
    procedure GemButtonClick(Sender: TObject);
    procedure LaesButtonClick(Sender: TObject);
  private
  public
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

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>
Avatar billede hrc Mester
20. oktober 2004 - 10:30 #8
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.
Avatar billede jackass- Nybegynder
20. oktober 2004 - 12:51 #9
Ok jeg må være dum eller noget..

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 :/
Avatar billede hrc Mester
20. oktober 2004 - 15:21 #10
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;
Avatar billede hrc Mester
20. oktober 2004 - 15:22 #11
Det er ikke testet...

Et spg. om den vil "æde" readln(fp,ListItem.Caption); ellers må du bare bruge st og tildele den til caption bagefter.
Avatar billede jackass- Nybegynder
20. oktober 2004 - 22:33 #12
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)
Avatar billede hrc Mester
21. oktober 2004 - 09:42 #13
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.
Avatar billede jackass- Nybegynder
21. oktober 2004 - 10:45 #14
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? :/
Avatar billede hrc Mester
21. oktober 2004 - 12:43 #15
Se min kommentar d. 20.10 kl. 15:21. Hvis ellers dit listview er sat ordentligt op, så er det det.
Avatar billede jackass- Nybegynder
21. oktober 2004 - 12:58 #16
1) Ved linien "while not fp.Eof do" får jeg fejlen: Record, object or class type required.

2) Ved linien "readln(fp,ListItem.Caption);" får jeg fejlen: Left side cannot be assigned to [og så ikk mere]

3) Ved de 2 linier:

                readln(fp,st);
                ListItem.SubItem.Add(st);

Får jeg fejlene "Undeclaret identifier 'st'" og "Undeclared identifier 'SubItem'"

Du burde sq få nogle flere points for al den hjælp her!!
Avatar billede hrc Mester
21. oktober 2004 - 13:22 #17
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;
Avatar billede hrc Mester
21. oktober 2004 - 16:39 #18
Jeg skulle lige prøve denne version med TStream:

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);

  Writer := TWriter.Create(fs,1024);
  try
    Writer.WriteString(eString1.Text); // Fire TEdits
    Writer.WriteString(eString2.Text);
    Writer.WriteString(eString3.Text);
    Writer.WriteString(eString4.Text);
  finally
    Writer.Free;
    fs.Free;
  end;
end;

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.
Avatar billede Ny bruger Nybegynder

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.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester