Avatar billede dex_dexter Nybegynder
11. august 2004 - 08:20 Der er 25 kommentarer og
1 løsning

læse linjer fra fil, array?

jeg har en fil der er opbygget på følgende måde:

100000, 20000 {
12 1 1
13 1 1
}

disse parm er der så omkring 100 af.

findes der en nem måde hvor på jeg kan hente dette ind fra en fil og smide i et array? f.eks med flere niveauer?
Avatar billede hrc Mester
11. august 2004 - 08:50 #1
Jeg ville (som sædvanligt) gå til min favoritløsning: En TObjectList der indeholder objekter der kan have en liste af 12 1 1'ere. Det vil tage mig en middagspause at strikke en kørbar løsning sammen om du er interesseret - med kobling til TTreeView (selvom dette er ret langsomt om man har mange TTreeNodes).

Er

100000, 20000 {
12 1 1
13 1 1
}

en dataenhed eller står 100000 alene? Med andre ord: Hvordan er strukturen? Skal data kunne skrives anden enhed?
Avatar billede dex_dexter Nybegynder
11. august 2004 - 09:14 #2
jeg har lavet en mindre fejl, data står som følgende:

"100000" 20000 {
12 1 1
13 1 1
}
jeg skal bruge data til videre forarbejdelse, bla smide i en database.
men det ville være rart med en lille løsning, du kan dog bare pege mig i rette vej hvis det er for omstændigt at lave et komplet eksempel
Avatar billede dex_dexter Nybegynder
11. august 2004 - 09:47 #3
det hele er en enhed, og der står ca 100 af dem ned af i filen
Avatar billede hrc Mester
11. august 2004 - 11:03 #4
Er der altid kun to linier af

12 1 1
13 1 1

eller kan det variere? Skal de enkelte tal i "12 1 1" separeres?
Avatar billede dex_dexter Nybegynder
11. august 2004 - 11:08 #5
det kan variere, og ja
Avatar billede hrc Mester
11. august 2004 - 12:29 #6
Her kommer unitten:

unit UData;

interface

uses
  SysUtils, ContNrs, Classes;

type
  TDataList = class;
  TSegmentData = class;
  TSegmentSubData = class;

  TDataList = class(TObjectList)
  private
    fFileName : string;
    function GetSegmentData(const aIndex: integer): TSegmentData;
  public
    constructor Create(const aFileName : string);
    property FileName : string read fFileName;
    property Items[const aIndex : integer] : TSegmentData read GetSegmentData; default;
  end;

  TSegmentData = class(TObjectList)
  private
    fField1 : string;
    fField2 : integer;
    function GetSegmentSubData(const aIndex: integer): TSegmentSubData;
  public
    constructor Create(const aString : string);
    property Field1 : string read fField1;
    property Field2 : integer read fField2;
    property Items[const aIndex : integer] : TSegmentSubData read GetSegmentSubData; default;
  end;

  TSegmentSubData = class
  private
    fSubData : TStringList;
    function GetString(const aIndex: integer): string;
  public
    constructor Create(const aString : string);
    destructor Destroy; override;
    property Items[const aIndex : integer] : string read GetString; default;
    function Count : integer;
  end;

implementation

{ TDataList }

type
  TStringStates = (ssHeader, ssSegment);

constructor TDataList.Create(const aFileName: string);
var
  fp : TextFile;
  st : string;
  StringState : TStringStates;
  SegmentData : TSegmentData;
//  SegmentSubData : TSegmentSubData;
begin
  inherited Create(true);
  fFileName := aFileName;
  SegmentData := nil;

  assignfile(fp,fFileName);
  reset(fp);
  try
    StringState := ssHeader;
    while not eof(fp) do begin
      readln(fp,st);
      st := trim(st);
      if st <> '' then
        case StringState of
          ssHeader  : begin
                        SegmentData := TSegmentData.Create(st);
                        if pos('{',st) > 0 then
                          StringState := ssSegment;
                      end;
          ssSegment : if st = '}' then begin
                        if not assigned(SegmentData) then
                          raise Exception.Create('Error in file structure');
                        Add(SegmentData); // Add finished record object to list
                        StringState := ssHeader;
                      end
                      else
                        SegmentData.Add(TSegmentSubData.Create(st));
        end; // case
    end;
  finally
    closefile(fp);
  end;
end;

function TDataList.GetSegmentData(const aIndex: integer): TSegmentData;
begin
  result := TSegmentData(inherited Items[aIndex]);
end;

{ TSegmentData }

constructor TSegmentData.Create(const aString: string);
var
  p1,p2 : integer;
begin
  inherited Create(true);
  p1 := pos(',',aString);
  if p1 > 0 then begin
    fField1 := copy(aString,2,p1-3);
    p2 := pos('{',aString);
    if p2 = 0 then
      fField2 := StrToInt(trim(copy(aString,p1+1,MaxInt)))
    else
      fField2 := StrToInt(trim(copy(aString,p1+1,p2-p1-1)));
  end;
end;

function TSegmentData.GetSegmentSubData(const aIndex: integer): TSegmentSubData;
begin
  result := TSegmentSubData(inherited Items[aIndex]);
end;

{ TSegmentSubData }

function TSegmentSubData.Count: integer;
begin
  result := fSubData.Count;
end;

constructor TSegmentSubData.Create(const aString: string);
begin
  inherited Create;
  fSubData := TStringList.Create;
  fSubData.Delimiter := #32;
  fSubData.DelimitedText := aString;
end;

destructor TSegmentSubData.Destroy;
begin
  try
    fSubData.Free;
  finally
    inherited;
  end;
end;

function TSegmentSubData.GetString(const aIndex: integer): string;
begin
  result := fSubData[aIndex];
end;

end.


og her er rutinen der gennemløber strukturen:


procedure TfrmMain.Button1Click(Sender: TObject);
var
  Data : TDataList;
  i,j,k : integer;
begin
  Data := TDataList.Create('test.txt');
  try
    for i := 0 to Data.Count - 1 do begin
      mLog.Lines.Add(format('%s %d',[Data[i].Field1,Data[i].Field2]));
      for j := 0 to Data[i].Count - 1 do
        for k := 0 to Data[i][j].Count - 1 do
          mLog.Lines.Add(Data[i][j][k]);
    end;
  finally
    Data.Free;
  end;
end;


Jeg har forudsat at filen findes (i constructoren) og at sub-data afslutteren } altid står alene på linien - så har jeg sikkert forudsat en del mere, men det kan jeg ikke huske - er sulten og vil ned og se hvad kantinen byder.
Avatar billede dex_dexter Nybegynder
11. august 2004 - 12:33 #7
god frokost, kommer selv lige derfra :)
takker, jeg kigger på det
Avatar billede dex_dexter Nybegynder
11. august 2004 - 12:41 #8
hum, den giver mig så omkring 50 fejl, men det giver da noget ar arbejde med
Avatar billede dex_dexter Nybegynder
11. august 2004 - 13:07 #9
den kan ikke finde ud af Tdatalist, den siger bla:
[Error] Unit1.pas(28): Undeclared identifier: 'TDataList' til linjen

hvilket kunne tyde på at jeg mangler et eller andet komponent eller andet?
Avatar billede hrc Mester
11. august 2004 - 13:12 #10
Næ, såmænd ikke. Jeg har kaldt unitten UData.pas og den skal naturligvis inkluderes i det projekt hvor du tester fra. Det vil sige:

Tryk på "folder +"-ikonen og tilslut filen til projektet. Inkludér den derefter i den form hvor du tester, vha <alt-f11>
Avatar billede dex_dexter Nybegynder
11. august 2004 - 13:37 #11
okay, jeg havde så lavet inkluden forkert ;)
Avatar billede dex_dexter Nybegynder
11. august 2004 - 13:38 #12
men nu klager den over mlog, hvad skal den declares som?

mLog.Lines.Add(format('%s %d',[Data[i].Field1,Data[i].Field2]));

det er den eneste fejl der er tilbage
Avatar billede dex_dexter Nybegynder
11. august 2004 - 13:47 #13
nu har jeg fundet ud af det, det virker sådan set fint, men skulle også gerne have de første 2 tal med ud også, altså "100000" 20000 da det ene er et id og det andet et timestamp
Avatar billede hrc Mester
11. august 2004 - 14:05 #14
Det har du også - da jeg ikke vidste hvad de betød, så hedder de Field1 og Field2
Avatar billede dex_dexter Nybegynder
11. august 2004 - 14:10 #15
hvordan får jeg dem sparket ud til noget output?
Avatar billede dex_dexter Nybegynder
11. august 2004 - 14:37 #16
for at det ikke skal være løgn ser det faktisk ud til at de eneste tal jeg skal bruge er de 2 foran, resten er urelevant, skruer lige lidt point extra på for dit besvær
Avatar billede hrc Mester
11. august 2004 - 15:04 #17
Arrrgh.. Skidt, faktisk gav det mig en nørdet tilfredsstillelse at skulle brygge den afsluttende testkode sammen:

  for i := 0 to Data.Count - 1 do begin
    mLog.Lines.Add(format('%s %d',[Data[i].Field1,Data[i].Field2]));
    for j := 0 to Data[i].Count - 1 do
      for k := 0 to Data[i][j].Count - 1 do
        mLog.Lines.Add(Data[i][j][k]);
  end;

Det her med en tredimensionel liste var sjov at lave
Avatar billede dex_dexter Nybegynder
11. august 2004 - 15:51 #18
et lille tillægsspørgsmål
hvis jeg har en streng der siger
111901111
hvordan får jeg så erstattet 90 med det er står i strengen otherstr uden at røre resten af strengen, kan det gøres nemt eller skal man til at klippe klistre?
Avatar billede dex_dexter Nybegynder
11. august 2004 - 16:02 #19
test := '111901111';
test := copy(test,0,9);
ascii := char(strtoint(copy(test,4 ,2)));
delete(test,4,2);
insert(ascii, test, 4);
label1.Caption := test;

jeg kan jo godt :)
Avatar billede dex_dexter Nybegynder
12. august 2004 - 10:04 #20
hm.. ligemeget hvad jeg gør får jeg en accessviolation, kan jeg ikke få dig til at lave et eksempel der smider det ud i en memo? kun de 2 felter, men gerne i hver deres felt
Avatar billede hrc Mester
12. august 2004 - 11:33 #21
Du kan vel bare bruge ovenstående eksempel og så lade være med at bruge dybereliggenede data. Jeg har rettet list i eksemplet:

type
  TDataList = class;
  TSegmentData = class;

  TDataList = class(TObjectList)
  private
    fFileName : string;
    function GetSegmentData(const aIndex: integer): TSegmentData;
  public
    constructor Create(const aFileName : string);
    property FileName : string read fFileName;
    property Items[const aIndex : integer] : TSegmentData read GetSegmentData; default;
  end;

  TSegmentData = class
  private
    fID : string;
    fTimeStamp : integer;
  public
    constructor Create(const aString : string);
    property ID : string read fID1;
    property TimeStamp : integer read fTimeStamp;
  end;

implementation

{ TDataList }

type
  TStringStates = (ssHeader, ssSegment);

constructor TDataList.Create(const aFileName: string);
var
  fp : TextFile;
  st : string;
  StringState : TStringStates;
  SegmentData : TSegmentData;
begin
  inherited Create(true);
  fFileName := aFileName;
  SegmentData := nil;

  assignfile(fp,fFileName);
  reset(fp);
  try
    StringState := ssHeader;
    while not eof(fp) do begin
      readln(fp,st);
      st := trim(st);
      if st <> '' then
        case StringState of
          ssHeader  : begin
                        SegmentData := TSegmentData.Create(st);
                        if pos('{',st) > 0 then
                          StringState := ssSegment;
                      end;
          ssSegment : if st = '}' then begin
                        if not assigned(SegmentData) then
                          raise Exception.Create('Error in file structure');
                        Add(SegmentData); // Add finished record object to list
                        StringState := ssHeader;
                      end;
        end; // case
    end;
  finally
    closefile(fp);
  end;
end;

function TDataList.GetSegmentData(const aIndex: integer): TSegmentData;
begin
  result := TSegmentData(inherited Items[aIndex]);
end;

{ TSegmentData }

constructor TSegmentData.Create(const aString: string);
var
  p1,p2 : integer;
begin
  inherited Create(true);
  p1 := pos(',',aString);
  if p1 > 0 then begin
    fID := copy(aString,2,p1-3);
    p2 := pos('{',aString);
    if p2 = 0 then
      fTimeStamp := StrToInt(trim(copy(aString,p1+1,MaxInt)))
    else
      fTimeStamp := StrToInt(trim(copy(aString,p1+1,p2-p1-1)));
  end;
end;

Koden der smækker det over i Memoen (mLog):

procedure TfrmMain.Button1Click(Sender: TObject);
var
  Data : TDataList;
  i,j : integer;
begin
  Data := TDataList.Create('test.txt');
  try
    mLog.Lines.Clear;
    for i := 0 to Data.Count - 1 do
      with Data[i] do
        mLog.Lines.Add(format('%s %d',[ID,TimeStamp]));
  finally
    Data.Free;
  end;
end;
Avatar billede dex_dexter Nybegynder
12. august 2004 - 12:34 #22
property ID : string read fID1; melder den fejl på
[Error] UData.pas(28): Field or method identifier expected

og inherited Create(true);
[Error] UData.pas(88): Too many actual parameters

har du et tip?
Avatar billede hrc Mester
12. august 2004 - 13:23 #23
ad. 1: Ret stavefejlen fID1 til fID - den var nem, ikke?
ad. 2: Lidt mere speciel. Vi nedarver ikke længere fra TObjectList (da du ikke ville have det skrammel med) men fra TObject og så er inherited Create anderledes. Løsning: Slet "(true)"
Avatar billede dex_dexter Nybegynder
12. august 2004 - 13:50 #24
nu fejler den ikke, men jeg får kun 0´er ud af systemet, også selvom der ingen er i filen
Avatar billede hrc Mester
12. august 2004 - 14:55 #25
Jeg synes efterhånden min tålmodighed bliver sat på lidt af en prøve...

Min første tanke: Hvorfor prøver du ikke at debugge programmet med Breakpoints (F5), Wathces (Ctrl-F5) samt F7 og F8-tasterne? Delphi har nok markedets bedste debugging-faciliteter - og de er lette at bruge.

Den anden tanke: Enten er filspecifikationen, den du beskrev øverst i dette spørgsmål, forkert, eller så laver du en anden fejl jeg ikke kender til. Det er dødsimpelt det her og min testfil indlæses aldeles problemfrit - både med version 1 og 2. Prøv at undersøge det.

Undskyld at jeg nu lyder bøs, men det er altså misbrug af min tid, når du ikke selv prøver at roder med problemet.
Avatar billede dex_dexter Nybegynder
16. august 2004 - 10:37 #26
du må undskylde jeg spørger lidt dumt igen, men jeg kan godt se hvordan du smider data ind i tsegmentdata, men har du noget docs til hvordan jeg får det ud igen, har læst koden igennem mange gange, uden held for at finde ud af hvordan det kan være at jeg kun får nuller ud
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