Avatar billede circadian Nybegynder
17. august 2010 - 15:27 Der er 21 kommentarer

Gemme Memo indhold i fil (sammen med andet)

Jeg har en masse Edit felter og et par Memo's i mit program. Jeg har lavet en feature der gør det muligt at gemme og loade indholdet af disse, ligesom normal open/save i alle andre programmer. Nu er jeg dog stødt på et problem.

Jeg har en Memo som består af tekst med en masse afsnit (dvs. der er linjeskift i denne tekst), og den skal selvfølgelig også kunne gemmes og hentes ind igen sammen med det andet.

// sådan gemmes:
Myini.WriteString('feedback','vip',FeedbackForm.Memo1.Text);

// sådan loades:
FeedbackForm.Memo1.Text := Myini.ReadString('feedback','vip','');

Det lader til at lige så snart man har en Memo med tekst hvori der findes linjeskift, så kan den ikke loades ordentligt igen. Fordi når jeg loader, så kommer kun den første linje med, og ikke hele teksten.

Jeg tænker måske at nogle af jer vil foreslå noget TStringsToText eller lignende, men jeg vil altså ikke til at gemme i en ny separat fil.
Avatar billede hrc Mester
17. august 2010 - 15:58 #1
Lad være med at gemme i en ini-fil. Fyld data i en stream og gem denne i registreringsdatabasen eller til en fil.

Brug evt. TWriter og TReader klasserne til at hjælpe med det.
Avatar billede hrc Mester
17. august 2010 - 15:59 #2
Gør evt. sådan her: http://www.eksperten.dk/spm/505203
Avatar billede stone Forsker
17. august 2010 - 16:04 #3
Linie skift er ikke noget problem bare du har styr på hvilken linie du skal hente eller gemmme i memoen.

Eksemplet her henter en tekst fra memo til Edit

Edit1.Text:= Memo1.Lines[0];
Edit2.Text:= Memo1.Lines[1]; //osv...
Avatar billede circadian Nybegynder
17. august 2010 - 17:05 #4
#3
Tror du misforstod mig lidt. For eksempel kan Memo'ens indhold se således ud:

"tekst tekst tekst tekst tekst tekst tekst tekst tekst tekst
tekst tekst tekst tekst tekst tekst tekst tekst

tekst tekst tekst tekst tekst tekst
tekst tekst tekst tekst"

Og når man loader den ind igen så er det kun første linje:
"tekst tekst tekst tekst tekst tekst tekst tekst tekst tekst"
der kommer ind i Memo'en igen.


#1
Hele mit programs gemmefunktion er opbygget efter ini princippet, så vil meget nødigt lave det om. Men hvis man virkelig ikke kan gemme en Memo's indhold på ini-måden, så er der vel ingen vej uden om. Det spørgsmål du foreslår i #2 virker ret uoverskueligt for mig, men jeg har kigget lidt på TWriter og TReader.

Jeg prøvede dette eksempel:
http://www.bsdg.org/swag/DELPHI/0081.PAS.html, dog uden det der "try opening a non existent file", da den gik helt bananas over det. Ser heller ikke formålet med det.
Anyway, jeg har udbygget det eksempel til at indeholde en Memo og 2 Edit felter. Jeg gemmer Edit felterne med:

Writer.WriteString(Edit1.Text);
Writer.WriteString(Edit2.Text);

Men det lader til at dette ikke virker:

Edit1.Text := Reader.ReadString;
Edit2.Text := Reader.ReadString;

Hvordan skal det gøres så? Kan ikke umiddelbart finde noget brugbart eksempel på nettet.
Avatar billede kroning Nybegynder
17. august 2010 - 18:56 #5
// sådan gemmes:
Myini.WriteString('feedback','vip',FeedbackForm.Memo1.Lines.CommaText);

// sådan loades:
FeedbackForm.Memo1.Lines.CommaText := Myini.ReadString('feedback','vip','');

Kik evt. også på Memo1.Lines.DelimitedText og Memo1.Lines.Delimiter
Avatar billede circadian Nybegynder
17. august 2010 - 19:50 #6
#5
Det ser ud til at virke - delvist.

Ved brug af CommaText:

Den gemmer det godt nok i min ini fil, og det ser helt korrekt ud, altså måden det er gemt på. Når jeg så loader, så ser noget af det lidt mærkeligt ud; i første afsnit skiftes der lige pludselig linje efter hvert ord, og det er ikke det hele gemte "materiale", der genindlæses. De andre afsnit der kommer ind, vises dog helt korrekt, bare lige på nær det første der.

Har også prøvet at sætte en anden delimiter ind, men det ser ikke ud til at virke.
Avatar billede kroning Nybegynder
17. august 2010 - 21:09 #7
Nu du siger det så kan jeg huske at CommaText ikke virker sammen med ini filer så derfor skal du bruge Memo1.Lines.DelimitedText og Memo1.Lines.Delimiter hvor du sætter Delimiter til et tegn andet end ", f.eks. ¤
Avatar billede circadian Nybegynder
17. august 2010 - 22:37 #8
#7

Kan man så ikke gøre dette:

// sådan gemmes:
FeedbackForm.Memo1.Lines.Delimiter := '¤';
Myini.WriteString('feedback','vip',FeedbackForm.Memo1.Lines.DelimitedText);

// sådan loades:
FeedbackForm.Memo1.Lines.DelimitedText := Myini.ReadString('feedback','vip','');

Forresten, så tror jeg da at ¤ kommer i stedet for komma, og ikke i stedet for ".
Avatar billede kroning Nybegynder
17. august 2010 - 23:31 #9
Ja måske skulle jeg lige tænke mig om inden jeg skriver noget nu har jeg kikket på lidt kode, problemet med ini filer og Memo opstår hvis det første tegn er ", det har jeg så løst på følgende måde:
  Memo1.Lines.Delimiter:=',';
  Memo1.Lines.QuoteChar:='¤';

altså erstattet " med ¤ og beholdt , som delimiter
Avatar billede preppydude Nybegynder
18. august 2010 - 20:21 #10
Jeg har lavet noget hurtigt, som jeg desværre ikke har mulighed for at teste lige nu - og det er som sagt lavet hurtigt, så der kan godt være nogle fejl/stavefejl o.l.

Anyway, har du JCL kan du gøre noget alá det her:

unit test;

uses
  Forms,
  JvSimpleXml,
  JclSimpleXml;

type
  TSaveData = class
    constructor Create(AOwner: TForm);
    destructor Destroy; override;
  private
    FForm: TForm;
    FXml: TJvSimpleXML;
    FSaveData: TJclSimpleXMLElem;
    function GetString(AItem: String): String;
    procedure SetValue(AItem: string; AValue: Variant);
  public
    // Loading/Saving
    procedure Load;
    procedure Save;
  end;

implementation

uses
  SysUtils,
  Classes,
  Variants;

constructor TSaveData.Create(AOwner: TForm);
begin
  Assert(Assigned(AOwner), 'TSaveData needs to be assigned a form owner');
  FForm := TForm(AOwner);

  if not (FileExists(ExtractFilePath(Application.ExeName)+'savedata.xml')) then
    with TStringList.Create do
    try
      SaveToFile(ExtractFilePath(Application.ExeName)+'savedata.xml');
    finally
      Free;
    end;

  FXml := TJvSimpleXML.Create(nil);
  FXml.LoadFromFile(ExtractFilePath(Application.ExeName)+'savedata.xml');
  FXml.Root.Name := 'data';
  FSaveData := FXml.Root;
end;

destructor TSaveData.Destroy;
begin
  inherited;
  FSaveData.Items.Sort;
  FXml.SaveToFile(ExtractFilePath(Application.ExeName)+'config.xml');
  FXml.Free;
end;

procedure TSaveData.Load;
var
  i: Integer;
begin
  for i := 0 to FForm.ComponentCount-1 do
  begin
    if (FForm.Components[i] is TEdit) then
      TEdit(FForm.Components[i]).Text := GetString(TEdit(FForm.Components[i]).Name)
    else if (FForm.Components[i] is TMemo) then
      TMemo(FForm.Components[i]).Text := GetString(TMemo(FForm.Components[i]).Name);
  end;
end;

procedure TSaveData.Save;
var
  i: Integer;
begin
  for i := 0 to FForm.ComponentCount-1 do
  begin
    if (FForm.Components[i] is TEdit) then
      SetValue(TEdit(FForm.Components[i]).Name, TEdit(FForm.Components[i]).Text)
    else if (FForm.Components[i] is TMemo) then
      SetValue(TMemo(FForm.Components[i]).Name, TMemo(FForm.Components[i]).Text)
  end;
end;

procedure TSaveData.SetValue(AItem: string; AValue: Variant);
var
  Idx: Integer;
begin
  Idx := FSaveData.Items.IndexOf(AItem);
  if (Idx>=0) then
    FSaveData.Items.Item[Idx].Value := VarToStr(AValue)
  else
    FSaveData.Items.Add(AItem, VarToStr(AValue));
end;

function TSaveData.GetString(AItem: string): String;
var
  Idx: Integer;
begin
  Idx := FSaveData.Items.IndexOf(AItem);
  if (Idx>=0) then
    Result := FSaveData.Items.Item[Idx].Value
  else
    Result := '';
end;

end.
Avatar billede preppydude Nybegynder
18. august 2010 - 20:22 #11
... og der var så den første. "config.xml" skal rettes til "savedata.xml" i Destroy funktionen.
Avatar billede hrc Mester
19. august 2010 - 09:12 #12
Og så skal inherited flyttes nederst i destructor (jeg plejer at putte den ind i en try-finally, men det bestemmer man jo selv.

destructor TSaveData.Destroy;
begin
  try
    FSaveData.Items.Sort;
    FXml.SaveToFile(ExtractFilePath(Application.ExeName)+'config.xml');
    FXml.Free;
  finally
    inherited;
  end;
end;
Avatar billede preppydude Nybegynder
19. august 2010 - 09:33 #13
Nemlig. :) Om man lave en try/finally eller smidder det nederst tror jeg er lige fedt.
Avatar billede hrc Mester
19. august 2010 - 09:48 #14
preppydude: Jeg vil bare sikre mig, at den nedarvede destructor kaldes uanset om frigivelse af andre ting i klassen måske fejler. Så meget som muligt skal frigives. Derfor propper jeg det altid ind i try-finally - som en af de ting jeg bare gør.
Avatar billede hrc Mester
19. august 2010 - 09:53 #15
Hvad nu hvis din SaveToFile fejlede? Så blev inherited ikke kaldt og så havde du en destructor der slet ikke ryddede op. Med en try-finally, så fejlede objektet godt nok og FXMml blev heller ikke frigivet, men resten gjorde. Skulle det være helt sikkert var løsningen sådan her:

destructor TSaveData.Destroy;
begin
  try
    try
      FSaveData.Items.Sort;
      FXml.SaveToFile(ExtractFilePath(Application.ExeName)+'config.xml');
    finally
      FXml.Free;
    end;
  finally
    inherited;
  end;
end;

.. men det er selv jeg ved at synes er for meget. På den anden side bliver objektet nu ryddet helt af vejen selvom disken måtte være skrivbeskyttet.
Avatar billede preppydude Nybegynder
19. august 2010 - 09:53 #16
Forstår dig udemærket godt. Har selv mine vaner med ting jeg altid gør.

Tror faktisk jeg vil tage det til mig og bruge sådan et statement fremover også. Ja, det vil jeg.
Avatar billede circadian Nybegynder
19. august 2010 - 18:36 #17
#9
Det virker! Dog loader den ikke hele teksten ind, det er som om der er en eller anden form for limit på. Jeg har kigget i ini-filen, og den gemmer ganske vist hele teksten, men den loader altså ikke den hele ind igen.

preppydude - tak for dit bidrag her, men eftersom jeg forholdsvis ny i Delphi verdenen forstår jeg overhovedet intet af din kode :)
Avatar billede preppydude Nybegynder
20. august 2010 - 00:31 #18
Jeg kan godt give dig et fungerende eksempel på hvordan det virker, hvis du altså vil have det. :)
Avatar billede circadian Nybegynder
22. august 2010 - 16:46 #19
#18
Jo tak, det må du da gerne :)
Avatar billede circadian Nybegynder
01. september 2010 - 16:56 #20
#18
Please :)
Avatar billede preppydude Nybegynder
02. september 2010 - 12:45 #21
Ah, sorry, jeg havde da glemt alt om det her. Laver et så snart jeg kommer hjem! ;)
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