Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Pos, Copy usw.


Arbengie - Sa 12.06.10 13:35
Titel: Pos, Copy usw.
Hi ich veruche grad nen Programm zu schreiben, bei dem man in ein Edit einen String mit mehreren Werten rein einfügt (z.B. "WertA WertB WertC"). Diese sind alle durch eine Leerzeichen getrennt. Und in drei anderen Edit Felder sollen diese Werte dann alleine angezeigt werden.

Da habe ich bisher mit Copy, Pos gearbeitet, nur das Problem ist, dass es ja mehrere Leerzeichen gibt und Pos nur das Erste immer anerkennt...

Wie komm ich da weiter bzw. gibts da auch en ganz andere elegantere Möglichkeit?


Dude566 - Sa 12.06.10 13:38

Du könntest dein Konzept überlegen und vielleicht was sinnvolleres verwenden.

Du könntest aber auch nachdem du den 1. Wert herauskopiert hast diesen aus dem String löschen und dann den Rest weiterverarbeiten, wie ein Parser das auch macht.


Narses - Sa 12.06.10 13:42

Moin!

Wenig Aufwand: schau dir mal Suche in: Delphi-Forum, Delphi-Library TSTRINGLIST.COMMATEXT an. :idea: ;)

cu
Narses


ALF - Sa 12.06.10 13:47

user profile iconDude566 hat folgendes geschrieben Zum zitierten Posting springen:
Du könntest dein Konzept überlegen und vielleicht was sinnvolleres verwenden.

Dem schliesse ich mich an. Da Du hier von Werte schreibst, gehe ich davon aus das es Zahlenwerte sind.
Warum also so kompliziert machen wenn man gleich 3 Editfelder dafür nehmen kann :wink:
Zumal Du dies ja in 3 Editfelder übertragen tust.

Gruss Alf


Arbengie - Sa 12.06.10 13:52

Also man kopiert ne Zeile aus dem Internet und fügt die dort ein und das Programm soll das dann halt "zerteilen"

Das was man reinkopiert is konkret:

"24 Red County Timo87 0 % 17 L 1104 km/2054 km"

Ich brauche daraus für Editfeld einen Wert(für das Bsp.):
-24
-Red County
-Timo87
-0
-17
-1104


ALF - Sa 12.06.10 13:55

Das ist ja was anderes, als das was in Deinem Anfang steht!
Ansonsten siehe user profile iconNarses

Gruss Alf


Arbengie - Sa 12.06.10 14:03

Ja, aber da sind keine Kommata, wie man sie für TStingList.CommaText brauch


Dude566 - Sa 12.06.10 14:06

Schreibe den Text aus dem Edit in eine Variable vom Typ String und zerlege dann diesen nach deinen Wünschen mit Pos etc in weitere Variablen, die du dann in deinen Edits einfügen kannst.


ALF - Sa 12.06.10 14:17

user profile iconArbengie hat folgendes geschrieben Zum zitierten Posting springen:
Ja, aber da sind keine Kommata, wie man sie für TStingList.CommaText brauch

Dann solltest Du Dir mal die Hilfe durchlesen :!:
TStingList.Delimiter := Char, kann also (',') (' ') ('#') (':') sein usw.

Gruss Alf


Narses - Sa 12.06.10 14:28

Moin!

user profile iconArbengie hat folgendes geschrieben Zum zitierten Posting springen:
Ja, aber da sind keine Kommata, wie man sie für TStingList.CommaText brauch
Das ist auch nicht unbedingt wichtig, WhiteSpace zählt wie Komma. Einfach mal ausprobieren. :)

cu
Narses


Arbengie - Sa 12.06.10 14:58

Hab mal was versucht wie es in manchen Beispielen gemacht wird:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
Input.Text := '24  Red County  Timo87  0 %  17 L  1104 km/2054 km';

procedure TForm1.Button1Click(Sender: TObject);
var
  input: TStringList;
  s1: string;
begin
  input := TStringList.Create;
  input.CommaText := edtinput.Text;
  s1 := input.Values['24'];
  Edtoutput.Text := s1;
end;


Fehler: Nix passiert :P


Delete - Sa 12.06.10 15:13

Und wo wird die Stringliste wieder freigegeben? Wo ist der Ressourcenschuttzblock? Wo ist die Fehlerbehandlung?


jaenicke - Sa 12.06.10 15:17

Values bringt dir hier wenig, da du keine Zeilen wie "24=Mein Wert" hast. Du brauchst input[0], input[1], ...

Wobei hier ein manuelles Vorgehen sinnvoller wäre. Denn automatisch bekommt man das doppelte Leerzeichen nicht als Trennzeichen hin.


Bergmann89 - Sa 12.06.10 16:01

Hey,

ich Werf mal PosEx [http://delphi.about.com/library/rtl/blrtlPosEx.htm] in den Raum...

MfG Bergmann


jaenicke - Sa 12.06.10 17:08

Mal nen ganz schnell hingezimmerter Versuch, sollte klappen, hab grad kein Delphi zum Testen da (jetzt erst Recht mit Pointern :mrgreen:):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
type
  TExampleData = record
    Age, Group, Name, Progress, L, Position: string;
    procedure LoadFromString(const Value: string);
  end;

procedure TExampleData.LoadFromString(const Value: string);
var
  CurPos, CurValue, NextPos: PChar;
  CurData: PString;
  Count: Integer;
begin
  CurPos := @Value[1];
  CurValue := CurPos;
  CurData := @Age;
  while CurPos^ <> #0 do
  begin
    NextPos := PChar(Integer(CurPos) + SizeOf(Char));
    if ((CurPos^ = ' 'and (NextPos^ = ' ')) or (NextPos^ = #0then
    begin
      Count := Integer(CurPos) - Integer(CurValue);
      if NextPos^ = #0 then
        Inc(Count);
      CurData^ := Copy(CurValue, 1, Count);
      Inc(CurData);
      Inc(CurPos);
      CurValue := CurPos;
      Inc(CurValue);
    end;
    Inc(CurPos);
  end;
end;

var
  ExampleRecord: TExampleData;
begin
  ExampleRecord.LoadFromString(edtInput.Text);
  memResult.Lines.Add(ExampleRecord.Age);
  memResult.Lines.Add(ExampleRecord.Group);
  memResult.Lines.Add(ExampleRecord.Name);
  memResult.Lines.Add(ExampleRecord.Progress);
  memResult.Lines.Add(ExampleRecord.L);
  memResult.Lines.Add(ExampleRecord.Position);
Sollte ab Delphi 2006 / Turbo Delphi so gehen.


delfiphan - Sa 12.06.10 18:49

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
(...) ganz schnell (...) hingezimmerter Versuch (...) hab grad kein Delphi zum Testen (...) Pointern

:bawling:

Arbengie, hast du Einfluss auf das Eingabeformat? Ich würde dir raten, ein Standardformat wie CSV zu verwenden. Felder mit zwei Leerzeichen zu trennen, weil die Felder selbst Leerzeichen haben klingt für mich nicht nach einem robusten Design. Falls du keinen Einfluss hast; im Internet solltest du eine geeignete Stringsplitter oder Explode Routine finden. Du bist bestimmt nicht der erste ;)


jaenicke - So 13.06.10 08:41

So, ich habe es noch getestet, funktioniert natürlich auch. Hier noch eine zweite Variante:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
type
  TExampleData = record
    Age: Integer;
    Group, Name: string;
    Progress, L, Position: Integer;
    procedure LoadFromString(const Value: string);
    procedure LoadFromString2(const Value: string);
  end;


procedure TExampleData.LoadFromString(const Value: string);
var
  CurPos, CurValue, NextPos, BlankPos: PChar;
  CurData: Pointer;
  Count, Index: Integer;
begin
  CurPos := @Value[1];
  CurValue := CurPos;
  CurData := @Age;
  Index := 0;
  BlankPos := nil;
  while CurPos^ <> #0 do
  begin
    if (CurPos^ = ' 'and not Assigned(BlankPos) then
      BlankPos := CurPos;
    NextPos := PChar(Integer(CurPos) + SizeOf(Char));
    if ((CurPos^ = ' 'and (NextPos^ = ' ')) or (NextPos^ = #0then
    begin
      Count := Integer(CurPos) - Integer(CurValue);
      if NextPos^ = #0 then
        Inc(Count);
      case Index of
        0: TryStrToInt(Copy(CurValue, 1, Count), PInteger(CurData)^);
        12: PString(CurData)^ := Copy(CurValue, 1, Count);
        3..5: TryStrToInt(Copy(CurValue, 1, Integer(BlankPos) - Integer(CurValue)), PInteger(CurData)^);
      end;
      Inc(PLongWord(CurData));
      Inc(CurPos);
      CurValue := CurPos;
      Inc(CurValue);
      Inc(Index);
      BlankPos := nil;
    end;
    Inc(CurPos);
  end;
end;
Und ohne Pointerspielerei die "normale" Version für den Recordzugriff:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
procedure TExampleData.LoadFromString(const Value: string);
var
  CurPos, CurValue, NextPos, BlankPos: PChar;
  Count, Index: Integer;
begin
  CurPos := @Value[1];
  CurValue := CurPos;
  Index := 0;
  BlankPos := nil;
  while CurPos^ <> #0 do
  begin
    if (CurPos^ = ' 'and not Assigned(BlankPos) then
      BlankPos := CurPos;
    NextPos := PChar(Integer(CurPos) + SizeOf(Char));
    if ((CurPos^ = ' 'and (NextPos^ = ' ')) or (NextPos^ = #0then
    begin
      Count := Integer(CurPos) - Integer(CurValue);
      if NextPos^ = #0 then
        Inc(Count);
      case Index of
        0: TryStrToInt(Copy(CurValue, 1, Count), Age);
        1: Group := Copy(CurValue, 1, Count);
        2: Name := Copy(CurValue, 1, Count);
        3: TryStrToInt(Copy(CurValue, 1, Integer(BlankPos) - Integer(CurValue)), Progress);
        4: TryStrToInt(Copy(CurValue, 1, Integer(BlankPos) - Integer(CurValue)), L);
        5: TryStrToInt(Copy(CurValue, 1, Integer(BlankPos) - Integer(CurValue)), Position);
      end;
      Inc(CurPos);
      CurValue := CurPos;
      Inc(CurValue);
      Inc(Index);
      BlankPos := nil;
    end;
    Inc(CurPos);
  end;
end;
Wichtig: Fehlerbehandlung fehlt in beiden noch, denn Umwandlungsfehler werden zwar abgefangen, aber nicht behandelt.

user profile icondelfiphan hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
(...) ganz schnell (...) hingezimmerter Versuch (...) hab grad kein Delphi zum Testen (...) Pointern

:bawling:
Ich habe es mir schon gut genug überlegt was da passiert. ;-)


delfiphan - So 13.06.10 09:40

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Wichtig: Fehlerbehandlung fehlt in beiden noch, denn Umwandlungsfehler werden zwar abgefangen, aber nicht behandelt.

Der Code gefällt mir auch aus einigen anderen Gründen nicht. Es fehlt die Validierung des Inputs; habe ich Range Checking eingeschaltet, geht's bei einem leeren String schief; ist Overflow Checking eingeschaltet, geht deine Integer Arithmetik bei Pointern vielleicht schief, wenn mit PEFlags $20 kompiliert. Ganz nebenbei sollte das Umwandeln von Pointer zu Integer tabu sein (wenn schon dann Cardinal oder UINT_PTR). Ungeachtet von Range Checking kann ich mit dem Code leicht Memory überschreiben, indem ich zu viele Werte übergebe. Dein Code ist stark platformabhängig und macht Annahmen über die interne Struktur von Strings. Kein Separation of Concern vorhanden, deine Funktion macht alles gleichzeitig und ist daher recht unübersichtlich und nicht wiederverwendbar (bei zwei ähnlichen Funktionen müsstest du das ganze mehr oder weniger 1:1 kopieren und modifizieren; wenn du einen Bug findest musst du es an zwei Orten fixen; wenn man "zwei Leerzeichen" durch "Tab" ersetzt, musst ggf. alles neu überlegen und umschreiben). Ich finde es übrigens besser, Fehler als solche zu schmeissen, statt stillschweigend mit TryStrToInt zu ignorieren.