![]() |
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: |
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 |
AW: QuoteText parsen?
Oder StringReplace oder reguläre Ausdrücke (müssten aber unter Delphi 2007 nachgerüstet werden IIRC).
|
AW: QuoteText parsen?
Zitat:
Delphi-Quellcode:
type
TWordInfo = record Value: string; Style: TFontStyles; Color: TColor; procedure Draw(Canvas: TCanvas; MMX, MMY: double); end; TWordLine = class .. end; |
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. |
AW: QuoteText parsen?
|
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:
UPDATE
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. So mit der kleinen Änderung (CommandStack) können jetzt auch verschachtelte Commands verarbeitet werden. So z.B.
Code:
Ist dann das gleiche Ergebnis:
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 Zitat:
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 |
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:
|
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:
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:
und gehe den Code auf dem Papier durch, dann sollte auch die Erkenntnis kommen :)
a[b]c[d]e[/b]f[/d]g
|
AW: QuoteText parsen?
Meinst Du
![]() 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. |
AW: QuoteText parsen?
Zitat:
Der Scanner erkennt meist mit Hilfe eines Automaten (->State machine) die einzelnen Token. Die Token sind hier: CHARS, BEGIN-GROUP, GROUPNAME, END-GROUP. Der Parser prüft nun, ob die vom Scanner gelieferten Token der zu prüfenden Grammatik entsprechen. Hier z.B.:
Code:
Während des Erkennens kann ein Syntaxbaum erstellt werden. Dieser Syntaxbaum kann Grundlage für einen Compiler sein, der die einzelnen Knoten des Baumes in die Zielsprache übersetzt. Hier wäre die Zielsprache eine Liste von 'TTextPart' Einträgen. Eine andere Zielsprache könnte 'RTF' sein. Oder 'DOCX', oder z.B. formatierter Quelltext etc.
BBText ::= <Literals> [<BBText>]
Literals ::= CHARS | <Group> Group ::= <BeginGroup> <BBText> <EndGroup> BeginGroup ::= BEGIN-GROUP GROUPNAME EndGroup ::= END-GROUP GROUPNAME Viele Parser spannen den Baum nicht mehr explizit auf (obwohl damit Codepfade analysiert werden können, Stichwort: Die Variable 'X' ist wahrscheinlich nicht initialisiert, Rückgabewert undefiniert etc.), sondern spucken direkt das Compilat aus. Das alles hat Sir Rufo in die Klasse gepackt, alles andere wäre ein wenig oversized. Und da die Sprache so einfach ist, spannt er einen eindimensionalen Baum auf: Einen Stack. Übrigens: Es gibt Programme (Compilergeneratoren), denen gibt man die Grammatik der Zielsprache sowie reguläre Ausdrücke zum Erkennen der Token und die spucken dann einen fertigen und garantiert fehlerfreien Parser aus. Füttert man den Generator noch mit Übersetzungsregeln, hat man einen Compiler. |
AW: QuoteText parsen?
Liste der Anhänge anzeigen (Anzahl: 1)
Ich hab' den Parser jetzt doch (lieber) in "BjoerkStyle" gemacht. Tut. In diesem Zusammenhang ist bei mir eine andere Frage aufgetaucht. Ist TStringList.Text nicht dasselbe wie das Ganze in einen String aufsummiert? (Siehe auch FBBCParser.LineOut(PaintBox.Canvas, FSL.Text, ..))
|
AW: QuoteText parsen?
Zitat:
Gruß K-H |
AW: QuoteText parsen?
Ok. Thanx. Das war ja das: StringReplace(FSL.Text, sLineBreak, '', [rfReplaceAll]) :oops:
Die LinesOut (Strings) funktioniert übrigens nicht richtig. Die hab ich nochmal überarbeitet… |
AW: QuoteText parsen?
Hier kannst du dir vllt auch noch was abschauen??
![]() |
AW: QuoteText parsen?
Liste der Anhänge anzeigen (Anzahl: 1)
Ok. Thanx. Ich denke aber ich habs jetzt soweit. Etwas optimiert und mit korrekter LinesOut. Man kann übrigens auch von außen (FBBCParser.List.Add..) oder auch im constructor weitere BBCodes setzen. Die werden dann automatisch berücksichtigt.
|
AW: QuoteText parsen?
Cool ist, wenn der Parser noch sowas kann :
"F[sub]d[/sub]" "N/mm[sup]2[/sup]" :thumb: |
AW: QuoteText parsen?
Parser können das, da es problemlos in die Syntax passt, aber es muß das Ziel auch diese Art ver Formatierung können und es benötigt bei der Auswertung des geparsten Textes noch die Funktion, das entsprechend zu übergeben.
|
AW: QuoteText parsen?
Gut. Machen kann man viel..
[SqrtSign] [Picture]c:\...\.jpg[/Picture] |
AW: QuoteText parsen?
Zitat:
Code:
[font="Arial"]blabla[/font]
text [image="c:\...\foo.jpg"] text |
AW: QuoteText parsen?
Die Syntax ist aber soweit ich verstanden habe vorgegeben (BBCode).
|
AW: QuoteText parsen?
Zitat:
|
AW: QuoteText parsen?
BBCParser, das klingt für mich nach "BBCode-Parser".
|
AW: QuoteText parsen?
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:
Deshalb hab ich's so gemacht.
Delphi-Quellcode:
FBBCParser := TBBCParser.Create;
FBBCParser.List.AddFont('_Delphi', 'Courier New', 10, clNavy, [fsBold]); FBBCParser.List.AddPicture('ATHENA', 'C:\Delphi2007\Abel2007\Sonstige\ATHENA.BMP'); FSL := TStringList.Create; FSL.Add('Test'); FSL.Add('[_Delphi]inherited[/_Delphi]'); FSL.Add('f[fsSub]d[/fsSub] = 10 N/mm[fsSup]2[/fsSup]'); FSL.Add('Picture: [ATHENA]0,5[/ATHENA] very nice '); // 0,5 = Zoom (StrechDraw); FSL.Add('Hier geht''''s weiter'); |
AW: QuoteText parsen?
:thumb:
|
AW: QuoteText parsen?
Zitat:
Ich finde die Stelle schon mal nicht. Bitte erleuchte mich ... ... denn dann müssten wir den TE auch darauf hinweisen, dass
Code:
nicht so wirklich BBCode ist.
[clRed]in rot[/clRed]
|
AW: QuoteText parsen?
In
![]() |
AW: QuoteText parsen?
Seh' ich auch so. Siehe z.B. auch Wikipedia: BBCode ist nicht offiziell reglementiert. Anyway. Für mich ist das auf alle Fälle ein "richtiger" BBCode Parser. :wink: Die BBCodes werden vorher beim Parser angemeldet und können dann (kumulativ) verwendet werden. Ich hab' zum Beispiel eine an Delphi TFont class angelehnte Syntax verwendet AddFontStyle('fsBold', [fsBold]), die kann aber auch völlig andere sein AddFontStyle('b', [fsBold]). Und, man kann z.B. die Konstanten cFirst und cLast in < bzw. > ändern. Dann hätte man <b>Text</b>.
|
AW: QuoteText parsen?
Liste der Anhänge anzeigen (Anzahl: 1)
Wollte jetzt noch Blocksatz einbauen. Mach ich da was falsch? Der Unterschied zu rechtsbündig ist ziemlich groß?
Delphi-Quellcode:
procedure TBBCItem.JustifiedTextOut(Canvas: TCanvas; MMX, MMY, ppMM: double); // Blocksatz;
var TextMMWidth, DeltaMMX: double; I: integer; SL: TStringList; begin SL := TStringList.Create; try TextMMWidth := Canvas.TextWidth(Text) / ppMM; SplitString(Text, SL); if SL.Count > 1 then DeltaMMX := Max((GetTextAlign.MMWidth - TextMMWidth) / (SL.Count - 1), 0) else DeltaMMX := 0; for I := 0 to SL.Count - 1 do begin Canvas.TextOut(Round(MMX * ppMM), Round(MMY * ppMM), SL[I]); MMX := MMX + Canvas.TextWidth(SL[I]) / ppMM + DeltaMMX; end; finally SL.Free; end; end; |
AW: QuoteText parsen?
Auf die Schnelle gefunden, allerdings nur überflogen (Screenshots sehen aber gut aus):
![]() |
AW: QuoteText parsen?
Ok. Schau ich mir an. Vielleicht nochmal zu meinem Ansatz. Ich versteh ja wenn das mal 1 oder 2 Pixel Unterschied sind (ist bei Word ja auch) aber so ??? Ich splitte den String in eine Wortliste (übrigens mit deiner Splitspring) und drucke dann Wort für Wort. Das TextWidth jeweils dazu addiert zuzüglich der Länge Delta?
Code:
========== Bezugsbreite ==========
Test(__Delta__)Test(__Delta__)Test |
AW: QuoteText parsen?
Ich habe Deinen Code jetzt nicht bis ins Detail nachvollzogen, aber ein paar Gedanken dazu: aufzuteilende Breite = Anzeigebreite - Textbreite. Textbreite muss man aber erst einmal definieren, soll sie mit oder ohne die Leerzeichen sein? Ohne Leerzeichen könnte dazu führen, dass im ungünstigsten Fall der Zwischenraum kleiner als die Breite eines Leerzeichens wird, nicht so schön. Im anderen Fall muss man daran denken, außer beim letzten Wort bei der Anzeige noch ein Leerzeichen anzuhängen, wenn man die Stringliste abarbeitet.
|
AW: QuoteText parsen?
Zitat:
Gruß K-H |
AW: QuoteText parsen?
Zitat:
Zitat:
Zitat:
Edit: Stimmt übrigens. Mega Thanx!! Aber wieso?
Delphi-Quellcode:
MMX := MMX + Canvas.TextWidth(SL[I] + #32) / ppMM + DeltaMMX;
|
AW: QuoteText parsen?
![]()
HTML-Code:
hat beim Rendern ein feste Breite und an der Stelle wird nicht umgebrochen. Alle anderen Leerzeichen können in der Breite variieren um beim Blocksatz den Text bündig zu präsentieren.
<p>
Ich bin ein Text in einem kurzen nichtssagendem Absatz. </p>
Code:
|----------------------|
Ich...bin..ein_Text...in einem.............kurzen nichtssagendem.Absatz. |
AW: QuoteText parsen?
Zitat:
Code:
(die _ sollen die Leerzeichen darstellen). Angenommen, es ergibt eine Textbreite von 50 Pixeln, die Ausgabebreite beträgt 122 Pixel, dann ist der aufzuteilende Abstand 72 Pixel. 3 Wortabstände (Leerzeichen), das macht dann 24 Pixel zusätzlichen Abstand zwischen 2 Worten. Du hast jetzt den Satz gesplittet, das ergibt
Ich_bin_ein_Satz.
Code:
Gibst Du das nun mit jeweils 24 Pixeln Zwischenabstand wieder aus, kommt dabei
Ich
bin ein Satz.
Code:
(X = 24 Pixel Abstand) heraus. Das ist aber zu kurz, da die Breite der nun weggefallenen Leerzeichen fehlt, das muss wieder ergänzt werden:
IchXbinXeinXSatz.
Code:
Deswegen entweder Leerzeichen an die einzelnen Worte wieder anhängen oder gleich die Textbreite ohne Leerzeichen errechnen (oder die Textbreite eines Leerzeichens einmalig ermitteln und auf den Abstand aufschlagen) ;)
Ich_Xbin_Xein_XSatz.
|
AW: QuoteText parsen?
Achso. Die SplitString schmeißt ja den Delim (hier #32) raus. Dann isses klar. Danke! :oops: :thumb:
|
AW: QuoteText parsen?
Zitat:
Ist das gleiche aber IBM war ja schon immer ein wenig anders. Gruß K-H |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:17 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz