Delphi-PRAXiS
Seite 1 von 4  1 23     Letzte » 

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   QuoteText parsen? (https://www.delphipraxis.net/182623-quotetext-parsen.html)

Bjoerk 5. Nov 2014 17:41

Delphi-Version: 2007

QuoteText parsen?
 
Das ist [clRed]ein[/clRed][fsBold]Test[/fsBold].

Wie soll ich das denn [clBlue][fsBold]machen[/clBlue][/fsBold]?

Hat jemand eine Idee wie man sowas elegant splitten kann (Pseudocode reicht völlig)? :gruebel:

haentschman 5. Nov 2014 17:56

AW: QuoteText parsen?
 
Moin...

Du möchtest quasi den Text ohne die "[...]"? Dann eine function gebaut welche genau diese Blöcke durch '' ersetzt.
Stichworte:
Copy
PosEx

DeddyH 5. Nov 2014 18:02

AW: QuoteText parsen?
 
Oder StringReplace oder reguläre Ausdrücke (müssten aber unter Delphi 2007 nachgerüstet werden IIRC).

Bjoerk 5. Nov 2014 18:13

AW: QuoteText parsen?
 
Zitat:

Zitat von haentschman (Beitrag 1278809)
Moin...

Du möchtest quasi den Text ohne die "[...]"? Dann eine function gebaut welche genau diese Blöcke durch '' ersetzt.
Stichworte:
Copy
PosEx

Fast. Das ganze soll gedruckt werden. Die Quotes verstehen sich als Canvas.Font Attribute.

Delphi-Quellcode:
type
  TWordInfo = record
    Value: string;
    Style: TFontStyles;
    Color: TColor;
    procedure Draw(Canvas: TCanvas; MMX, MMY: double);
  end;

  TWordLine = class
    ..
  end;

himitsu 5. Nov 2014 18:16

AW: QuoteText parsen?
 
Es gibt bestimmt irgendwo einen BBCode-Parser, welcher das in RTF umwandelt, was fertig formatiert gedruckt werden könnte.


Ansonsten gehst du halt hin:
* Text suchen, bis zum nächsten [ (bei [ ohne Zugehörigkeit zu einem SteuerCode einen Fehler werfen oder es als Text betrachten und weitersuchen)
* den Text drucken
* X-Position um Textbreite verschieben
* das zwischen [ und ] suchen/auswerten
* Font entsprechen umschalten
* von vorne beginnen, bis der Text zu Ende ist

Wenn es nicht zu viele Steuerbefehle sind und man die mit einem Bit speichern könnte, dann könnte man vorher die Befehle durch jeweils ein Zeichen ersetzen und hat dann beim Drucken weniger Aufwand das zu parsen (z.b. #1=fett #2=nicht mehr fett #3= (bis #31, außer #8, #10 und #13 und in Unicode gibt es ein paar nette freie User-Bereiche)
* nächstes Steuerzeichen ode Textende suchen
* Text davor drucken und X verschieben
* Font umschalten
* von vorne beginnen

Man kann das auch gerne in einen Baum zerlegen (mit Text/Steuerbefehlen) und den Baum dann abarbeiten, falls man auch auf Rekursionen reagieren will ... ineinander verschachtelte Befehle, bzw man verwendet eine Referenzzählung für das Aktivieren/Deaktivieren, bzw. eine Queue.

p80286 5. Nov 2014 18:21

AW: QuoteText parsen?
 
Delphi-Referenz durchsuchenAnsiReplaceText

Gruß
K-H

Oh Mann vergiß es

Sir Rufo 5. Nov 2014 19:23

AW: QuoteText parsen?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Nur so zum Starten ein kleiner Parser (als State-Machine) quick und dirty.
Der kann auf jeden Fall das hier produzieren
Anhang 42107
Delphi-Quellcode:
unit Parser.BBCode;

interface

uses
  Vcl.Graphics, System.SysUtils, System.Generics.Collections;

type
  TTextPart = class
  private
    FFont: TFont;
    FText: string;
  public
    constructor Create( AFont: TFont; const AText: string );
    property Font: TFont read FFont;
    property Text: string read FText;
  end;

  TBBCodeParser = class
  private type
    TState = procedure( AChar: Char ) of object;
  private
    FParts: TList<TTextPart>;
    FCommandStack: TList<string>;
    FDefaultFont: TFont;
    FState: TState;
    FFont: TFont;
    FTextBuffer: TStringBuilder;
    FCommandBuffer: TStringBuilder;
    procedure InitParser;
    procedure ParseText( AChar: Char );
    procedure ParseCommand( AChar: Char );
    procedure ParseCloseCommand( AChar: Char );
    procedure HandleTextBuffer;
    procedure HandleCommandBuffer( Closing: Boolean = False );
  public
    constructor Create( ADefaultFont: TFont );
    destructor Destroy; override;

    procedure Parse( AText: string );
    property Parts: TList<TTextPart> read FParts;
  end;

implementation

uses
  System.StrUtils;

{ TBBCodeParser }

constructor TBBCodeParser.Create( ADefaultFont: TFont );
begin
  inherited Create;
  FDefaultFont := ADefaultFont;
  FFont := TFont.Create;
  FTextBuffer := TStringBuilder.Create;
  FCommandBuffer := TStringBuilder.Create;
  FParts := TObjectList<TTextPart>.Create;
  FCommandStack := TList<string>.Create;
end;

destructor TBBCodeParser.Destroy;
begin
  FFont.Free;
  FTextBuffer.Free;
  FCommandBuffer.Free;
  FParts.Free;
  FCommandStack.Free;
  inherited;
end;

procedure TBBCodeParser.HandleCommandBuffer( Closing: Boolean );
const
  FontStyle: array [TFontStyle] of string = ( 'fsBold', 'fsItalic', 'fsUnderline', 'fsStrikeOut' );
var
  LCommand: string;
  LIdx: Integer;
begin
  LCommand := FCommandBuffer.ToString;
  FCommandBuffer.Clear;

  if Closing
  then
    begin
      // von hinten aus dem Stack nehmen
      for LIdx := FCommandStack.Count - 1 downto 0 do
        if FCommandStack[LIdx] = LCommand
        then
          begin
            FCommandStack.Delete( LIdx );
            Break;
          end;
    end
  else
    // Einfach an den Stack anhängen
    FCommandStack.Add( LCommand );

  // Font einstellen
  FFont.Assign( FDefaultFont );

  for LCommand in FCommandStack do
    begin
      LIdx := IndexText( LCommand, FontStyle );
      if LIdx >= 0
      then
        FFont.Style := FFont.Style + [TFontStyle( LIdx )];

      if LCommand.StartsWith( 'cl', True )
      then
        FFont.Color := StringToColor( LCommand );
    end;
end;

procedure TBBCodeParser.HandleTextBuffer;
begin
  if FTextBuffer.Length > 0
  then
    begin
      FParts.Add( TTextPart.Create( FFont, FTextBuffer.ToString ) );
      FTextBuffer.Clear;
    end;
end;

procedure TBBCodeParser.InitParser;
begin
  FFont.Assign( FDefaultFont );
  FTextBuffer.Clear;
  FCommandBuffer.Clear;
  FCommandStack.Clear;
  FParts.Clear;
  FState := ParseText;
end;

procedure TBBCodeParser.Parse( AText: string );
var
  LChar: Char;
begin
  InitParser;
  for LChar in AText do
    FState( LChar );
  HandleTextBuffer;
end;

procedure TBBCodeParser.ParseCloseCommand( AChar: Char );
begin
  case AChar of
    ']':
      begin
        HandleTextBuffer;
        HandleCommandBuffer( True );
        FState := ParseText;
      end;
  else
    FCommandBuffer.Append( AChar );
  end;
end;

procedure TBBCodeParser.ParseCommand( AChar: Char );
begin
  case AChar of
    ']':
      begin
        HandleTextBuffer;
        HandleCommandBuffer( False );
        FState := ParseText;
      end;
    '/':
      FState := ParseCloseCommand;
  else
    FCommandBuffer.Append( AChar );
  end;
end;

procedure TBBCodeParser.ParseText( AChar: Char );
begin
  case AChar of
    '[':
      FState := ParseCommand;
  else
    FTextBuffer.Append( AChar );
  end;
end;

{ TTextPart }

constructor TTextPart.Create( AFont: TFont; const AText: string );
begin
  inherited Create;
  FFont := TFont.Create;
  FFont.Assign( AFont );
  FText := AText;
end;

end.
UPDATE
So mit der kleinen Änderung (CommandStack) können jetzt auch verschachtelte Commands verarbeitet werden.
So z.B.
Code:
normal [clRed]rot [clGreen]grün [/clGreen]wieder rot [/clRed]normal
normal [fsBold]fett [fsItalic]fett-kursiv [fsStrikeOut]fett-kursiv-durchgestrichen [fsUnderline]fett-kursiv-durchgestrichen-unterstrichen [/fsBold]kursiv-durchgestrichen-unterstrichen [/fsItalic]durchgestrichen-unterstrichen [/fsStrikeOut]unterstrichen [/fsUnderline]normal
Ist dann das gleiche Ergebnis:
Zitat:

normal rot grün wieder rot normal
normal fett fett-kursiv fett-kursiv-durchgestrichen fett-kursiv-durchgestrichen-unterstrichen kursiv-durchgestrichen-unterstrichen durchgestrichen-unterstrichen unterstrichen normal
wie hier mit den BBCodes
Code:
normal [COLOR="Red"]rot [COLOR="Green"]grün [/COLOR]wieder rot [/COLOR]normal
normal [B]fett [I]fett-kursiv [S]fett-kursiv-durchgestrichen [U]fett-kursiv-durchgestrichen-unterstrichen [/U][/S][/I][/B][I][S][U]kursiv-durchgestrichen-unterstrichen [/U][/S][/I][S][U]durchgestrichen-unterstrichen [/U][/S][U]unterstrichen [/U]normal

Bjoerk 5. Nov 2014 21:52

AW: QuoteText parsen?
 
Thanx. Wie immer genial deine Posts. Aber, den Code hab ich fast Null verstanden. Und Generics und TStringBuilder hab ich nich.. :oops:

Sir Rufo 5. Nov 2014 22:33

AW: QuoteText parsen?
 
Das du das nicht hast weiß ich, aber du willst ja auch etwas selber machen ;)

Eigentlich sind diese State-Machines als Parser super simpel zu erstellen:
  • Ein Parser geht Zeichen für Zeichen durch den String -> Wir brauchen eine Methode die einen Char entgegennimmt. Da aber je nach Status eine andere Methode benötigt wird brauchen wir erst mal so einen Methodentyp
    Delphi-Quellcode:
    TState = procedure(AChar:Char) of object;
  • Die erste Methode ist ganz simpel, es wird einfach jedes Zeichen an den
    Delphi-Quellcode:
    TextBuffer
    gehängt (das kann ein einfacher String sein, ein
    Delphi-Quellcode:
    TStringBuilder
    ist dafür nur schneller, mehr nicht). Nennen wir die einfach mal
    Delphi-Quellcode:
    ParseText
    . Trifft man hier nun auf das Zeichen
    Delphi-Quellcode:
    '['
    , dann kommt jetzt ein Kommando. Also müssen wir ab nun das aus einer anderen Sicht betrachten, denn der Status hat sich geändert. Wir parsen jetzt ein Kommando, also eine weitere Methode
    Delphi-Quellcode:
    ParseCommand
  • In
    Delphi-Quellcode:
    parseCommand
    werden alle eingehenden Zeichen an den
    Delphi-Quellcode:
    CommandBuffer
    gehängt, bis wir auf das Zeichen
    Delphi-Quellcode:
    ']'
    treffen.
  • Für weitere Stati verfährt man genauso und baut diese Schritt für Schritt auf.
Eigentlich ist es ein ganz stupides Abarbeiten der Vorgaben. Interessant wird dann der Teil, wo die Commands in den CommandStack ein- und ausgetragen werden und darüber der aktuelle Font "errechnet" wird.

Wichtig: Vor jeder Änderung durch ein Command muss der aktuelle TextBuffer mit dem aktuellen Font gespeichert werden (wenn der TextBuffer leer ist, dann brauchen wir auch nichts speichern).

Nimm dir am Besten einen kurzen Text
Code:
a[b]c[d]e[/b]f[/d]g
und gehe den Code auf dem Papier durch, dann sollte auch die Erkenntnis kommen :)

Jens01 5. Nov 2014 23:18

AW: QuoteText parsen?
 
Meinst Du das?
Das Ding hatte ich auch noch ein Stück bei mir erweitert, da ich es aber nicht mehr benötigte, habe ich es weggetan und finde es gerade nicht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:23 Uhr.
Seite 1 von 4  1 23     Letzte » 

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz