Autor Beitrag
Jiss
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Fr 06.11.09 01:04 
Hi,

ich lese aus einer Text-Datei mehrere Zeilen aus, die wie folgt aufgebaut sind:

ausblenden Quelltext
1:
2:
3:
BEZ   :STUNDE   (E/Ex/A/Ax/M/Mx/SM/SMx/T/Z/TN/B2/B8/B16/ANZ)  (I/O)     EW  I
BEZ   :MINUTE   (E/Ex/A/Ax/M/Mx/SM/SMx/T/Z/TN/B2/B8/B16/ANZ)  (I/O)     MW  I
BEZ   :ZEIT-H   (E/Ex/A/Ax/M/Mx/SM/SMx/T/Z/TN/B2/B8/B16/ANZ)  (I/O)     MW  O


Die Zeilen sind also immer nach Schema F aufgebaut.
Da ich aber nicht nur solche Zeilen auslesen möchte suche ich nach einer Funktion, die unter C als Scanf bekannt ist.
Damit ist es ja möglich einen formatierten String zu zerlegen und einzelne Teile des String zu speichern.

Beispiel:
scanf('%d hallo %f', var_zahl, var_float);

Für ne Zeile: "12345 hallo 123,45". In den Variablen werden also die Zahlen gespeichert.

Die Format-Funktion macht das ja genau andersrum. Nämlich die Werte von Variablen in einen String einfügen und diesen ausgeben. Gibts da auch ne Funktion, die genau andersrum funktioniert?
Es handelt sich bei mir nur um die Verarbeitung von String und keinen Zahlen.
Sicher kann ich einfach ab der so und so vielten Stelle die Werte mit Copy rauskopieren, aber so ne Scanf-Funktion wäre da einiges eleganter, um das Programm übersichtlich zu gestalten.

Gruß
Jörg
Xentar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2077
Erhaltene Danke: 2

Win XP
Delphi 5 Ent., Delphi 2007 Prof
BeitragVerfasst: Fr 06.11.09 10:23 
Für Delphi ist mir solch eine Funktion nicht bekannt.
Aber such mal nach regular expressions (RegEx), vielleicht helfen die dir weiter?

_________________
PROGRAMMER: A device for converting coffee into software.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 06.11.09 10:54 
Hallo und :welcome: !

Direkt gibt es das nicht. Wenn die Trennzeichen aber immer die Leerzeichen sind, dann kannst du auch mit TStringList arbeiten, genauer gesagt mit Delimiter und DelimitedText zum Beispiel.

Eine Alternative wären zwar die bereits genannten reguläre Ausdrücke. Das gibt es auch für Delphi. Das wäre aber in diesem Fall mit Boden-Luft-Raketen auf Spatzen geschossen. :mrgreen:

// EDIT 2:
Am besten geht es wohl wie auch die diversen Splitfunktionen so:
ausblenden volle Höhe 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 SplitEx(const AText: string; ADelim: Char; ATarget: TStringList); overload;
var
  CurTarget, CurChar: PChar;
begin
  CurChar := PChar(AText);
  CurTarget := CurChar;
  while (CurChar^ <> #0or (CurTarget <> nildo
  begin
    if (CurChar^ = ADelim) or (CurChar^ = #0then
    begin
      if CurTarget <> nil then
      begin
        ATarget.Add(Copy(CurTarget, 1, Integer(CurChar) - Integer(CurTarget)));
        CurTarget := nil;
      end;
    end
    else if CurTarget = nil then
      CurTarget := PChar(CurChar);
    Inc(CurChar);
  end;
end;

procedure TForm235.Button1Click(Sender: TObject);
var
  ExampleString: string;
  ExampleList: TStringList;
begin
  ExampleString := 'BEZ   :STUNDE   (E/Ex/A/Ax/M/Mx/SM/SMx/T/Z/TN/B2/B8/B16/ANZ)  (I/O)     EW  I';
  ExampleList := TStringList.Create;
  try
    SplitEx(ExampleString, ' ', ExampleList);
    ShowMessage(ExampleList.Text);
  finally
    ExampleList.Free;
  end;
end;


Andere Alternativen: (EDIT: direktes Beispiel siehe unten)
ausblenden volle Höhe 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:
procedure ScanF(const AText, ADelim: string; ATarget: array of PChar);
var
  CurTargetIndex, CurTargetLength: Integer;
  CurTarget, CurChar, CurDelim: PChar;
begin
  CurTargetIndex := 0;
  CurChar := PChar(AText);
  CurDelim := PChar(ADelim);
  CurTarget := CurChar;
  while (CurChar^ <> #0or (CurTarget <> nildo
  begin
    if (CurChar^ = CurDelim^) or (CurChar^ = #0then
    begin
      if CurTarget <> nil then
      begin
        CurTargetLength := Integer(CurChar) - Integer(CurTarget);
        CopyMemory(ATarget[CurTargetIndex], CurTarget, Succ(CurTargetLength));
        ATarget[CurTargetIndex][CurTargetLength] := #0;
        Inc(CurTargetIndex);
        CurTarget := nil;
      end;
      Inc(CurDelim);
    end
    else if CurTarget = nil then
    begin
      CurDelim := PChar(ADelim);
      CurTarget := PChar(CurChar);
    end;
    Inc(CurChar);
  end;
end;

procedure TForm235.Button1Click(Sender: TObject);
var
  a, b: string;
begin
  SetLength(a, 255); // Statt 255 real dann die Gesamtlänge des Strings z.B.
  SetLength(b, 255);
  ScanF('abc01abf''01', [PChar(a), PChar(b)]);
  SetLength(a, StrLen(PChar(a)));
  SetLength(b, StrLen(PChar(b)));
  ShowMessage(a + #13#10 + b);
end;
Für eine allgemeine Funktion sollte man noch prüfen, ob ATarget überhaupt lang genug ist usw., aber das sollte in diesem Fall ja nicht notwendig sein.
In deinem Fall musst du noch die unterschiedliche Anzahl von Leerzeichen beachten. Aber das lässt sich ja einbauen. Zum Beispiel, dass eine beliebige Anzahl Trennzeichen erkannt wird oder sowas.

// EDIT:
Funktionierendes Beispiel für das aktuelle Beispiel:
ausblenden volle Höhe 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:
47:
48:
49:
procedure ScanF(const AText: string; ADelim: Char; ATarget: array of PChar);
var
  CurTargetIndex, CurTargetLength: Integer;
  CurTarget, CurChar: PChar;
begin
  CurTargetIndex := 0;
  CurChar := PChar(AText);
  CurTarget := CurChar;
  while (CurChar^ <> #0or (CurTarget <> nildo
  begin
    if (CurChar^ = ADelim) or (CurChar^ = #0then
    begin
      if CurTarget <> nil then
      begin
        if CurTargetIndex >= Length(ATarget) then
          raise Exception.Create('Not enough space in array for ScanF(''' + AText + ''')');
        CurTargetLength := Integer(CurChar) - Integer(CurTarget);
        CopyMemory(ATarget[CurTargetIndex], CurTarget, Succ(CurTargetLength));
        ATarget[CurTargetIndex][CurTargetLength] := #0;
        Inc(CurTargetIndex);
        CurTarget := nil;
      end;
    end
    else if CurTarget = nil then
      CurTarget := PChar(CurChar);
    Inc(CurChar);
  end;
end;

procedure TForm235.Button1Click(Sender: TObject);
var
  ExampleString, a, b, c, d, e, f: string;
begin
  ExampleString := 'BEZ   :STUNDE   (E/Ex/A/Ax/M/Mx/SM/SMx/T/Z/TN/B2/B8/B16/ANZ)  (I/O)     EW  I';
  SetLength(a, Length(ExampleString));
  SetLength(b, Length(ExampleString));
  SetLength(c, Length(ExampleString));
  SetLength(d, Length(ExampleString));
  SetLength(e, Length(ExampleString));
  SetLength(f, Length(ExampleString));
  ScanF(ExampleString, ' ', [PChar(a), PChar(b), PChar(c), PChar(d), PChar(e), PChar(f)]);
  SetLength(a, StrLen(PChar(a)));
  SetLength(b, StrLen(PChar(b)));
  SetLength(c, StrLen(PChar(c)));
  SetLength(d, StrLen(PChar(d)));
  SetLength(e, StrLen(PChar(e)));
  SetLength(f, StrLen(PChar(f)));
  ShowMessage(a + #13#10 + b + #13#10 + c + #13#10 + d + #13#10 + e + #13#10 + f);
end;
Die Umwandlung in Zahlen oder so muss zwar im Gegensatz zum Original-ScanF noch erfolgen, aber das wird in Delphi kaum sinnvoll anders gehen. Auch wenn es theoretisch mit varianten Arrays möglich wäre.


Zuletzt bearbeitet von jaenicke am Sa 07.11.09 07:38, insgesamt 1-mal bearbeitet
Jiss Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Fr 06.11.09 23:38 
Vielen Dank jaenicke. Das werde ich gleich nächste Woche ausprobieren.