[gelöst] String an bestimmter Stelle auf bestimmtes Zeichen

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Hartkern
Beiträge: 69
Registriert: Sa 5. Dez 2015, 20:03
OS, Lazarus, FPC: Win10 IDE 1.6
CPU-Target: 64Bit
Wohnort: Leipzig

[gelöst] String an bestimmter Stelle auf bestimmtes Zeichen

Beitrag von Hartkern »

Hallo,

ich versuche aus einer .obj Datei die Daten für ein 3d Mesh zu laden.
ich benötige die Anzahl der Zeilen die mit v, vn und f beginnen.
Ob das erste Zeichen i.o. ist funktioniert, jedoch wenn ich die 2. Stelle im String auf ein Zeichen prüfe, liefert das Programm mir unsinnige Werte..

Mein Ansatz:

Code: Alles auswählen

procedure TForm1.Liste;
var
  StringListe : TStringlist;
  z,i,j,t: integer;
  TempString : String[255];
  TeilString1 : String[13];
  TeilString2 : String[13];
  TeilString3 : String[13];
  zaehler_v :  integer;
  zaehler_vn : integer;
  zaehler_f : integer;
begin
  memo1.Clear;
  StringListe := TStringlist.Create;
  Stringliste.LoadFromFile('model\untitled.obj');
  I:=Stringliste.Count;
 
  //rausfinden wieviele v,vn und f vorhanden sind
  zaehler_v := 0;
  zaehler_vn := 0;
  zaehler_f := 0;
  For Z:=0 to I-1 do
      begin   //nur Vektoren ausfiltern
        TempString := Stringliste.Strings[z];
        If TempString[1] = 'v' then
           begin
            if TempString[2] =' ' then  inc(zaehler_v);
           end;
      end;
      begin   //nur VN ausfiltern
        TempString := Stringliste.Strings[z];
        If TempString[1] = 'v' then
            begin
              if TempString[2] ='n' then inc(zaehler_vn);
            end;
      end;
      begin   //nur f ausfiltern
        TempString := Stringliste.Strings[z];
        If TempString[1] = 'f' then
            begin
              if TempString[2] =' ' then inc(zaehler_f);
            end;
      end;
  memo1.Lines.Add(IntToStr(zaehler_v));
  memo1.Lines.Add(IntToStr(zaehler_vn));
  memo1.Lines.Add(IntToStr(zaehler_f));
 
 
  //Einlesen der Variablen
  For Z:=0 to I-1 do
      begin   //nur Vektoren ausfiltern
        TempString := Stringliste.Strings[z];
        If TempString[1] = 'v' then
            begin
              if TempString[2] =' ' then
              memo1.Lines.Add(Stringliste.Strings[z]);
                  begin //3 Variablen auslesen
                    //1. Variable
                    j:=2;
                    t:=0;
                    TeilString1:='                          ';
                    repeat
                      inc(j);
                      if Zeichenprobe(TempString,j)=true then
                         begin
                           inc(t);
                           TeilString1[t]:=TempString[j];
                         end;
                    until Zeichenprobe(TempString,j) = false;
                    Memo1.Lines.Add(TeilString1);
                    //2. Variable
                    t:=0;
                    TeilString2:='                          ';
                    repeat
                      inc(j);
                      if Zeichenprobe(TempString,j)=true then
                         begin
                           inc(t);
                           TeilString2[t]:=TempString[j];
                         end;
                    until Zeichenprobe(TempString,j) = false;
                    Memo1.Lines.Add(TeilString2);
                    //3. Variable
                    t:=0;
                    TeilString3:='                          ';
                    repeat
                      inc(j);
                      if Zeichenprobe(TempString,j)=true then
                         begin
                           inc(t);
                           TeilString3[t]:=TempString[j];
                         end;
                    until Zeichenprobe(TempString,j) = false;
                    Memo1.Lines.Add(TeilString3);
                  end;
            end;
        begin   //nur VN ausfiltern
        TempString := Stringliste.Strings[z];
        If TempString[1] = 'v' then
            begin
              if TempString[2] ='n' then
              memo1.Lines.Add(Stringliste.Strings[z]);
            end;
        end;
        begin   //nur f ausfiltern
        TempString := Stringliste.Strings[z];
        If TempString[1] = 'f' then
            begin
              if TempString[2] =' ' then
              memo1.Lines.Add(Stringliste.Strings[z]);
            end;
        end;
 
    end;
end;                               


Meine Beispieldatei hat folgenden Inhalt

Code: Alles auswählen

# Blender v2.77 (sub 0) OBJ File: ''
# http://www.blender.org
mtllib untitled.mtl
o Cube
v 1.850858 0.425167 -0.321171
v 1.850858 0.425167 1.678829
v -0.149142 0.425167 1.678829
v -0.149141 0.425167 -0.321172
v 1.850859 2.425167 -0.321171
v 1.850858 2.425167 1.678829
v -0.149142 2.425167 1.678828
v -0.149141 2.425167 -0.321171
vn 0.0000 -1.0000 -0.0000
vn 0.0000 1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn 0.0000 0.0000 -1.0000
usemtl Material
s off
f 1//1 2//1 3//1 4//1
f 5//2 8//2 7//2 6//2
f 1//3 5//3 6//3 2//3
f 2//4 6//4 7//4 3//4
f 3//5 7//5 8//5 4//5
f 5//6 1//6 4//6 8//6
 
Zuletzt geändert von Hartkern am Fr 10. Jun 2016, 17:46, insgesamt 1-mal geändert.

wp_xyz
Beiträge: 4895
Registriert: Fr 8. Apr 2011, 09:01

Re: String an bestimmter Stelle auf bestimmtes Zeichen prüfe

Beitrag von wp_xyz »

In der 1. Schleife steht zu Beginn:

Code: Alles auswählen

  For Z:=0 to I-1 do
      begin   //nur Vektoren ausfiltern
        TempString := Stringliste.Strings[z];
        If TempString[1] = 'v' then
           begin
            if TempString[2] =' ' then  inc(zaehler_v);
           end;
      end;                     // ???
      begin   //nur VN ausfiltern
      ...

Das "end" in der mit //??? markierten Zeile gehört zu dem ersten "begin", das wiederum zum "do" der "for"-Schleife gehört. Das heißt die Schleife ist hier zu Ende! Ich glaube nicht, dass du das so willst. Entferne dieses "end" und stelle ansonsten alle begin-end Blöcke richtig, so dass sie innerhalb der Schleife ausgeführt werden.

In der zweiten Schleife, etwas über der Mitte, ähnliches:

Code: Alles auswählen

  //Einlesen der Variablen
  For Z:=0 to I-1 do
      begin   //nur Vektoren ausfiltern
        TempString := Stringliste.Strings[z];
        If TempString[1] = 'v' then
            begin
              if TempString[2] =' ' then                       // ???
              memo1.Lines.Add(Stringliste.Strings[z]);         // ???
                  begin //3 Variablen auslesen
                    //1. Variable
                    j:=2;

So wie das hier steht, wird, wenn TempString[2] = '' ist lediglich memo1.Lines.Add(...) ausgeführt. Ich nehme aber an, dass auch der folgende Code des "begin"-Block hier mit reingehört. Das heißt: das "begin" ist eine Zeile zu tief, es gehört unmittelbar direkt hinter das "then" (von "if TempString[2] = '' then"), oder in die folgende Zeile, auf jeden Fall VOR "memo1.Lines.Add".

Manche finden das pingelig, aber wenn du auf Einrückungen achtest, siehst du solche Fehler auf einen Blick.

Code: Alles auswählen

procedure blabla;
begin
  for i := 1 to n do begin
    // irgendwas
  end// gehört zu for --> steht unter for
 
  for i:=1 to n do begin
    if i = 2 then begin
      // irgendwas
    else begin  // else gehört zu if --> steht unter if
      // irgendwas anderes
    end// gehört zu if/else --> steht unter if/else
  end// gehört zu for --> steht unter for
end;
 
// Oder: Oft wird das "begin" auch in eine eigene Zeile gesetzt:
 
procedure blabla;
begin
  for i := 1 to n do
  begin
    // irgendwas
  end
 
  for i:=1 to n do
  begin
    if i = 2 then
    begin
      // irgendwas
    end
    else
    begin
      // irgendwas anderes
    end;
  end
end;

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: String an bestimmter Stelle auf bestimmtes Zeichen prüfe

Beitrag von Mathias »

Code: Alles auswählen

      begin   //nur Vektoren ausfiltern
        TempString := Stringliste.Strings[z];
        If TempString[1] = 'v' then
           begin
            if TempString[2] =' ' then  inc(zaehler_v);
           end;
      end;     

So was geht viel einfacher:

Code: Alles auswählen

      begin   //nur Vektoren ausfiltern
        if Pos('v ',  Stringliste.Strings[z]) = 1 then inc(zaehler_v);
      end;     


Das ist sowieso gefährlich

Code: Alles auswählen

If TempString[1] = 'v'

Wehe es handelt sich dabei um eine Leerzeile, Leerstring, bekommst einen SIGSEV.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: String an bestimmter Stelle auf bestimmtes Zeichen prüfe

Beitrag von Mathias »

Noch ein Tip, für eine Ablauf, um OBJ-Files einzulesen.

Ich würde 4 StringListen machen.
In die erste kommt die komplette OBJ-Datei.
Nachher würde ich die Überprüfung von den f, v, vn machen, und dies in die anderen 3 Stringlisten schreiben.

Somit kannst du, wen du die f zerlegt hast, direkt auf die anderen Stringlisten zugreifen und in die VertexArray schreiben.
Die Zahlen entsprechen der direkt auf die Position der StringListen.

Code: Alles auswählen

var
  SL, vSL, vnSL, fSL: TStringList;
  i: integer;
 
  VertexArray: array of glVertex3f;
begin
  SL := TStringList.Create;
  fSL := TStringList.Create;
  vSL := TStringList.Create;
  vnSL := TStringList.Create;
  SL.LoadFromFile('test.obj');
  for i := 0 to SL.Count - 1 do begin
    if Pos('f ', SL[i]) = 1 then begin
      fSL.Add(SL[i]);
    end;
    if Pos('v ', SL[i]) = 1 then begin
      vSL.Add(SL[i]);
    end;
    if Pos('vn ', SL[i]) = 1 then begin
      vnSL.Add(SL[i]);
    end;
  end;
 
  SetLength(VertexArray, fSL.Count);
  for i := 0 to fSL.Count - 1 do begin
    // Die einzelnen Zeilen zerlegen.
    // Und in VertexArray schreiben.
  end;
 
  SL.Free;
  fSL.Free;
  vSL.Free;
  vnSL.Free;
end
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Hartkern
Beiträge: 69
Registriert: Sa 5. Dez 2015, 20:03
OS, Lazarus, FPC: Win10 IDE 1.6
CPU-Target: 64Bit
Wohnort: Leipzig

Re: String an bestimmter Stelle auf bestimmtes Zeichen prüfe

Beitrag von Hartkern »

Vielen Dank euch beiden...!!

Ich achte zwar verstärkt auf meine Begin und End aber scheinbar doch nicht genug.

Pos war das geheime Druidenwissen was ich brauchte.

Danke Matze für deine Lösung...das auslesen der einzelnen Variabeln je Zeile klappt mittlerweile wunderbar.

Hartkern
Beiträge: 69
Registriert: Sa 5. Dez 2015, 20:03
OS, Lazarus, FPC: Win10 IDE 1.6
CPU-Target: 64Bit
Wohnort: Leipzig

Re: String an bestimmter Stelle auf bestimmtes Zeichen prüfe

Beitrag von Hartkern »

zu meiner Zeichenprobe ansich selbst...

Code: Alles auswählen

function TForm1.Zeichenprobe(S: String; Index: Integer): boolean;
begin
  result:=false;
  if Index=0 then exit;
  If S[Index]='0' then result:=true;
  If S[Index]='1' then result:=true;
  If S[Index]='2' then result:=true;
  If S[Index]='3' then result:=true;
  If S[Index]='4' then result:=true;
  If S[Index]='5' then result:=true;
  If S[Index]='6' then result:=true;
  If S[Index]='7' then result:=true;
  If S[Index]='8' then result:=true;
  If S[Index]='9' then result:=true;
  If S[Index]='0' then result:=true;
  If S[Index]='.' then result:=true;
  If S[Index]='-' then result:=true;
 
end;     


Ich bin froh das es so funktioniert, jedoch sagt mir mein Bauch das dies nicht die Alpha Lösung ist....Set of Char wäre mir noch in den Sinn gekommen.
Gibts es da Verbesserungsvorschläge? will ja was lernen :lol:

wp_xyz
Beiträge: 4895
Registriert: Fr 8. Apr 2011, 09:01

Re: String an bestimmter Stelle auf bestimmtes Zeichen prüfe

Beitrag von wp_xyz »

Set of char ist schon ganz gut! Aber was noch fehlt, ist, ob Index nicht außerhalb des Strings liegt. Also:

Code: Alles auswählen

function TForm1.Zeichenprobe(S: String; Index: Integer): Boolean;
begin
  if (Index < 1) or (Index > Length(s) then
    Result := false
  else
    Result := S[Index] in ['0'..'9', '.', '-', '+'];
end;

Hartkern
Beiträge: 69
Registriert: Sa 5. Dez 2015, 20:03
OS, Lazarus, FPC: Win10 IDE 1.6
CPU-Target: 64Bit
Wohnort: Leipzig

Re: String an bestimmter Stelle auf bestimmtes Zeichen prüfe

Beitrag von Hartkern »

ahhh

Weltklasse!! danke dir!

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: [gelöst] String an bestimmter Stelle auf bestimmtes Zeic

Beitrag von Mathias »

Sehe ich das richtig, das du diese Function brauchst, um die Strings "v xxx xxx xxx", etc. zu zerlegen ?
Wen da so ist, gibt es von fpc sehr elegante Lösungen.

ZB. so:

Code: Alles auswählen

  function StrToVec3(const ins: string): TVector3f;
  var
    i: integer;
  begin
    tmpSL.Delimiter := ' ';
    tmpSL.DelimitedText := ins;
 
    if tmpSL.Count < 3 then begin
      ShowMessage('x' + tmpSL.DelimitedText + 'x');
      ShowMessage('Fehler: Anz. Vectoren <> 3');
      tmpSL.DelimitedText := '0.0 0.0 0.0';
    end;
 
    for i := 0 to 2 do begin
      Result[i] := StrToFloat(tmpSL[i]);
    end;
  end

Oder ab fpc 3.1 gibt es String.Splitt.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Hartkern
Beiträge: 69
Registriert: Sa 5. Dez 2015, 20:03
OS, Lazarus, FPC: Win10 IDE 1.6
CPU-Target: 64Bit
Wohnort: Leipzig

Re: [gelöst] String an bestimmter Stelle auf bestimmtes Zeic

Beitrag von Hartkern »

Ähhmm ja...

Das es natürlich wieder was dafür gibt war mir unbekannt 8) .

Ich hab mich bisher ehrlich gesagt nicht damit beschäftigt irgendwelche Variablen aus Strings herauszufinden..

Nun brauchte ich es doch, und bastel mir daher nebenbei eine für mich persönliche Stringunit...

Ich bin mir jedoch nicht schlüssig was für ne Fpc Ich nutze.. ich weiss lediglich das ich lazarus ide 1.6 benutze

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: [gelöst] String an bestimmter Stelle auf bestimmtes Zeic

Beitrag von Mathias »

Ich bin mir jedoch nicht schlüssig was für ne Fpc Ich nutze..

Bei den normalen Installation hat man fpc 3.0.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten