Laden...

Parser für BBcode?

Erstellt von Golo Roden vor 18 Jahren Letzter Beitrag vor 17 Jahren 11.758 Views
Golo Roden Themenstarter:in
4.207 Beiträge seit 2003
vor 18 Jahren
Parser für BBcode?

Hallo,

kennt jemand eine Komponente für .NET, die mir einen Parser für BBcode bereitstellt? Sprich, ich rufe sie mit einem String auf, der entsprechenden Code enthält, und erhalte entsprechendes HTML zurück.

Wäre schön, wenn jemand so etwas kennt ...

Viele Grüße,

der Eisbär

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo Der Eisbär,

BBCode in HTML umzusetzen ist doch fast ein Job für RegEx, BB ist ja in den meisten Fällen schon so gut wie HTML (z.B. ** => <b>). Das einzige, was man von Hand machen muss, ist die Klammerung prüfen.

herbivore

Golo Roden Themenstarter:in
4.207 Beiträge seit 2003
vor 18 Jahren

Hi herbivore,

joa, so weit ist das sicher kein Problem ... ich möchte aber auch prüfen, ob ein Dokument gültig ist, sprich nicht so Sachen wie ungültig verschachtelte Tags enthält, dass jedes öffnende auch ein schließendes Tag hat, ...

Und da wird's dann schon komplizierter ... oder?

Viele Grüße,

der Eisbär

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo Der Eisbär,

nö, würde ich nicht sagen. Bei BB ist es ja gerade erlaubt, nicht korrekt zu schachteln. Deshalb wurde auch das ** im letzten Artikel (und auch das in diesem 🙂 nur deshalb wie von mir beabsichtlich dargestellt, weil ich eben das schließende Tag weggelassen habe.

herbivore

Golo Roden Themenstarter:in
4.207 Beiträge seit 2003
vor 18 Jahren

Hallo,

okay, dann sollte ich vielleicht noch dazu sagen, dass die Ausgabe in XHTML erfolgen soll und zumindest DAS valide sein muss 😉.

Viele Grüße,

der Eisbär

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo Der Eisbär,

schon klar, genau das habe ich auch mit dem "Das einzige, was man von Hand machen muss, ist die Klammerung prüfen" gemeint. Ich sehe schon ein, dass es das eine Ecke komplizierter macht, als einfach n RegEx.Replace drüberlaufen zu lassen. Wäre aber mal ein schönes Einsatzgebiet für die Klasse Stack, die man sonst nie braucht. 🙂

herbivore

Golo Roden Themenstarter:in
4.207 Beiträge seit 2003
vor 18 Jahren

Hallo herbivore,

okay, dann reden wir doch vom gleichen 🙂 .... hmmm, also wie gesagt, ne fertige Komponente dafür wäre schick, weil eigentlich würde ich mich gerne um das Coden von so nem Parser drumrumdrücken ... andererseits wäre es natürlich mal eine Herausforderung 😉.

Da ich nicht sehr viel Ahnung von diesem Thema habe - wie geht man prinzipiell an so eine Analyse eines Textes ran, um die Tags und deren Attribute und deren Verschachtelung zu ermitteln?

Viele Grüße,

der Eisbär

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo Der Eisbär,

ich würde per RegEx.Match/NextMatch-Schleife mit dem Pattern (?<bbtag>[\s*(?<close>/?)\s*(?<tagname>)[a-zA-Z]+)(?<attribute>[^]]+?)\s*])) über den Text laufen und und gucken, ob m.Groups["tagname"] ein gültiger Tagname ist. Wenn ja und wenn m.Groups["close"] leer ist, kommt das Tag auf den Stack. Wenn m.Groups["close"] nicht leer ist, guckst du, ob das oberste Tag (case insensitive) mit m.Groups["tagname"] gleich ist. Wenn ja, hast du ein Tag-Paar gefunden. Wenn nicht kannst du entweder das schließende Tag verwerfen oder den Stack solange abräumen, bis du das passende öffnende Tag gefunden hast.

Das ist jetzt so runtergeschrieben und muss sicherlich im Detail korrigert werden.

Von den gefundenen paarigen Tags kennst du die ja dank der Match-Klasse die Position und kannst an dieser gezielt substituieren, wobei du noch ggf. m.Groups["attribute"] berücksichtigen musst.

Naja, so in der Art müsste es gehen.

herbivore

-
885 Beiträge seit 2004
vor 18 Jahren

Der Eisbär:

Ich meine sowas schonmal gesehen zu haben bei codeproject.

Offtopic:
herbivore verdienst du eigentlich auch Geld bzw. schläfst du auch mal? Woher dein ganzes Wissen? Egal in welchem Thread ich schaue, du redest immer mit und lieferst Klasse Beiträge - Fazination pur. neidisch bin

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo -acid-,

danke für die Blumen. Falls deine Fragen nicht nur rhetorisch gemeint waren: Im moment arbeite ich gerade nine-to-five, deshalb schreibe ich momentan auch tagsüber fast gar nichts. Geld gibt es dafür auch (nicht fürs Beiträge schreiben - das wäre fein - sondern fürs Arbeiten). Aber im Prinzip versuche ich nicht mehr als 3-4 Monate im Jahr zu arbeiten. Und ja, ich schlafe täglich (oder besser nächtlich), habe ich mir noch nicht abgewöhnen können. Zu guter Letzt: Mein Wissen kommt aus etlichen Jahren Beschäftigung mit Informatik. Am meisten lernt man übringens, wenn man so früh wie möglich beginnt, Anderen etwas beizubringen.

herbivore

PS: @Eisbär: Hast du mit meinem Entwurf etwas anfangen können?

D
462 Beiträge seit 2005
vor 18 Jahren

Hallo!

Da ich mich gerade in RegEx einarbeite und ein nützliches Tool dafür gefunden habe, poste ich mal den Link: The RegEx Coach

Damit kann man reguläre Ausdrücke, in Echtzeit austesten, also ohne vorher was kompilieren zu müssen. Bin mir zwar nicht sicher, ob die Ausdrücke dann genauso in .NET verwendet werden können, weil ich nicht weis, ob reguläre Ausdrücke standardisiert sind?!

mfg DeveloperX

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo DeveloperX,

die Basics von RE sind über die verschiedenen Sprachen und Systeme hinweg relativ gleich. Aber im Detail gibt es dann schon jede Menge Unterschiede.

herbivore

PS: Mit On-the-fly Regex-Tester: Regex-Lab gibt es ein Tool, das die Regex-Syntax von .NET verwendet.

1.274 Beiträge seit 2005
vor 18 Jahren
auch was in Deutsch

Hei hab auch was zu Regular Expressions, auf Deutsch

ich poste mal den URL, weils gerade dazu passt.

http://www.sql-und-xml.de/regex/index.html

"Das Problem kennen ist wichtiger, als die Lösung zu finden, denn die genaue Darstellung des Problems führt automatisch zur richtigen Lösung." Albert Einstein

E
171 Beiträge seit 2004
vor 18 Jahren

@herbivore
na wow muss mich auch gerade damit rumschlagen!
will lieber in meiner ADS weiter Programmieren X(

also cooler post nur meine While ist endlos naja whatever!
das ganze sieht so aus!

"[bold]bla1 bla1[/bold]cdata[bold]bla2 bla2[/bold]";
regExpression = [bold]([^]]+)[/bold]
result =
m.Groups.Count 0--[bold]bla2 bla2[/bold]
m.Groups.Count 1--bla2 bla2

was ich hier vermisse ist den zweiten treffer er wertet mir nur den ersten vorkommen aus aber nicht den zweiten! mit NextMatch() bekomme ich den zweiten treffer! aber wie soll ich das schreiben habe kein Beispiel gefunden!

versucht habe ich es mit While(m.Success) eine endlosschleife kam raus. kann mir das jemand weiterhelfen ?

Voran danke,
Elron

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo elron,


m = Regex.Match (...);
while(m.Success) {
   // ...
   m = m.NextMatch ();
}

Allerdings bekommt du mit deinem Pattern Probleme mit geschachtelten Tags, denn

[bold]bla1 [italic]bla1[/italic][/bold]

würde ja wegen der ']' zwischen den bold-Tags nicht matchen, abgesehen davon, dass ']' auch so im Text auftauchen kann:

[bold]siehe [1][/bold]

herbivore

E
171 Beiträge seit 2004
vor 18 Jahren

@herbivore
danke für dein Post jetzt läuft es man da hätte ich auch selber drauf kommen können! ähmm du hast recht mit deinem Beispiel ich hoffe das ich den RegA.
noch anpassen kann..

Gruß
Elron

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo elron,

mit

[bold](.*?)[/bold]

oder vielleicht noch besser

[bold](([[]+)([(?!/bold))*([[]+))[/bold]

solltest du zu einem [bold] das nächste, schließende [/bold] finden (ungetestet).

Allerdings gibt es auch hier ein Problem. Bei

[bold]bla1[bold]bla2[/bold]bla2[/bold]

wird auf

[bold]bla1[bold]bla2[/bold]

gematcht.

Deshalb funktioniert mein Vorschlag oben auch mit einer Kombination von RegEx (für einzelne Tags) und Stack.

herbivore

E
171 Beiträge seit 2004
vor 18 Jahren

@herbivore
ja das Porb. habe ich auch bei meinem RegEx.
Habe es erstmal so umgesetzt!
z.B.
string:

"Hallo [em]Mustermann[/em] wie geht es [-b]dir?[/-b] Ich hoffe [-b][em] gut?[/em][/-b]" ("-" wegen konvertierung) auf myCSharp.de


public string FinalQuadCode(string FindQuadCode)
    {
        Dictionary<string, string> myDic = new Dictionary<string, string>();
        myDic.Add("Bold", @"\[b\](?<content>.*?)\[\/b\]");
        myDic.Add("Cursive", @"\[em\](?<content>.*?)\[\/em\]");
        
        foreach (KeyValuePair<string, string> myEn in myDic)
        {
            Match m = null;
            Regex r = null;

            r = new Regex(myEn.Value);
            m = r.Match(FindQuadCode);
            if (m.Success)
            {
                while (m.Success) 
                {
                    if (myEn.Key == "Bold")
                    {
                        FindQuadCode = FindQuadCode.Replace(m.Groups[0].Value, "<b>" + m.Groups["content"] + "</b>");
                    }
                    if(myEn.Key == "Cursive")
                    {
                        FindQuadCode = FindQuadCode.Replace(m.Groups[0].Value, "<em>" + m.Groups["content"] + "</em>");
                    }
                    m = m.NextMatch();
                }
            }
            else
            {
                
            }
        }
        return FindQuadCode;
    }

gut sicherlich kann man es noch um längen besser schreiben oder anpassen!
was meinst du?

Oder so ist es noch besser!


public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		private void button1_Click(object sender, EventArgs e)
		{
			this.textBoxOut.Text = ConvertString(this.textBoxIn.Text);
		}

		private static Regex rex = new Regex("\\[(?<tag>[^\\]]*)\\](?<innerText>.*?)\\[\\/\\k<tag>]", RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Singleline);

		string ConvertString(string value)
		{
			StringBuilder convertedString = new StringBuilder(value.Length);
			int lastMatchLastIndex = -1;
			foreach (Match m in rex.Matches(value))
			{
				// Anfang/Letzter Index bis Match übernehmen
				if (lastMatchLastIndex == -1) lastMatchLastIndex = 0;
				convertedString.Append(value.Substring(lastMatchLastIndex, (m.Index - lastMatchLastIndex)));

				string tag = m.Groups["tag"].Value.ToLower();
				string innerText = m.Groups["innerText"].Value;
				lastMatchLastIndex = m.Index + m.Length;
				
				switch (tag)
				{
					case "b":
					case "i":
					case "u":
						// Gültiger Tag
						convertedString.Append("<" + tag + ">");
						convertedString.Append(ConvertString(innerText));
						convertedString.Append("</" + tag + ">");
						break;
					
					default:
						// Ungültiger Tag, nur Inhalt übernehmen
						convertedString.Append(ConvertString(innerText));
						break;
				}
			}

			if (lastMatchLastIndex == -1)
			{
				// Nichts gefunden, alles kopieren
				convertedString.Append(value);
			}
			else
			{
				// restliche Zeichen übernehmen
				convertedString.Append(value.Substring(lastMatchLastIndex));
			}
			return convertedString.ToString();
		}
	}

Gruß
Elron

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo elron,

naja, der Code bzw. die Regex haben eben die schon benannten Schwächen, für die oben ich eine Lösungsmöglichkeit aufgezeigt hatte.

Der Ansatz mit dem Dictionary gefällt mich nicht schlecht. Da er allerdings nicht ausreicht, weil du drei Informationen benötigst, und er dich für die dritte Information zu einem if in der Schleife zwingt, verliert er allerdings deutlich an Eleganz.

Ich denke du erreichst das gleiche einfacher mit:


Regex.Replace (FindQuadCode, "[b](?<content>.*?)[/b]", "<b>${content}</b>");

Ansonsten ist mit noch aufgefallen, dass du b und em mixt. Zu b gehört i und zu em gehört strong.

HTH

herbivore

E
171 Beiträge seit 2004
vor 18 Jahren

@herbivore
jo das sieht doch schon besser aus!
danke dir für deine hilfe!

Gruß
Elron

D
462 Beiträge seit 2005
vor 18 Jahren

Hallo!

Auf der Suche nach BBCode-Parsern bin ich nun fündig geworden und habe ein (anscheinend) gutes Tutorial gefunden. Ist allerdings auf PHP basierend, dürfte jedoch nicht so schwer sein, es umzuschreiben.

zonik´s tutorials

mfg

A
196 Beiträge seit 2005
vor 17 Jahren

Irgendwie verstehe ich die Geschichte mit RegEx nicht richtig X(

Wäre nett wenn du mal ein kleines Beispiel machen könntest 👍

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo alf468,

ich meine das nicht böse, ich bin nur etwas verwundert:

Was meinst du mit Beispiel? Ich habe einen Algorithmus beschrieben. Es stehen sogar die wesentlichen Codeelemente drin. Was willst du mehr? Soll das jetzt ausprogrammieren oder wie stellst du dir das vor?

herbivore

A
196 Beiträge seit 2005
vor 17 Jahren

Sicher du hast das schon klar beschrieben aber irgendwie geht das bei mir nicht so richtig.

string str = " [bold] jjkljk [/bold]";
            string patt = @" (?<bbtag>\[\s*(?<close>)\\?\s*(?<tagname>)[a-zA-Z]+)(?<attribute>[^\]]+?)\s*\] ";
            Regex r = new Regex(patt, RegexOptions.IgnoreCase);

            Match m = r.Match(str);
            while (m.Success)
            {
                Console.WriteLine(m.Groups["tagname"].ToString());
                m = m.NextMatch();
            }
            Console.ReadLine();

Es wird da schon nix ausgegeben 🤔

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo alf468,

hm, naja, da hat der Pattern halt noch nicht ganz gestimmt. Das war ja nur ein Beispiel (also genau was du wolltest 😉. An der Stelle sollte der ganz normale Prozess der Fehlersuche beginnen.

Ich mache das immer so, dass ich den Pattern verkürze, bis er matcht. Dann verlängere ich ihn wieder, bis er nicht mehr matcht. An dem Teil, den ich im letzten Schritt hinzugefügt habe, muss also ein Fehler sein, also korrigieren. Und dann wieder verlängern. Das Ganze solange bis es passt.

Verkürzen kann man natürlich nicht an beliebigen Stellen und man muss darauf achten, dass die Klammerung immer noch passt.

@"(?<bbtag>[\s*(?<close>/?)\s*(?<tagname>[a-zA-Z]+)\s*(?<attribute>[^]]?)\s])"

herbivore

A
196 Beiträge seit 2005
vor 17 Jahren

Wenn ich das habe kann ich ja mit string.Replace(..) die Tags umwandeln.
Aber ich möchte ganz gerne auch die Code Tags verwenden und in innerhalb von Code darf der BBCode nicht umgewandelt werden.
Wie kann ich da am besten vorgehen ?

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo alf468,

sehe nicht, wo du da String-Replace brauchst. Alles was du brauchst sollte mit m.Index, m.Length und String.Substring gehen.

herbivore

A
196 Beiträge seit 2005
vor 17 Jahren

Aber dann müsste ich den Index einmal verändern und alle weiteren Tags würden nicht mehr passen.

Wenn das Programm in der m.Success Schleife ist und bei einem gefundenen Paar eine Änderung an dem original String vorgenommen wird stimmt doch der Index für die anderen Tags nicht mehr.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo alf468,

wieso müsstest du den Index verändern? Und warum sollte es notwendig sein, den Original-String zu verändern? Du parst den Original-String in der Schleife und per m.Index, m.Length und String.Substring (zusammen mit StringBulder) baust du das Ergebnis zusammen. Wenn du innerhalb von

 keine Ersetzungen durchführen willst, zwingt dich ja keiner.

herbivore
A
196 Beiträge seit 2005
vor 17 Jahren

Kann sein das ich mich jetzt total dumm anstelle aber irgendwie verstehe ich das nicht.
Ich habe den Index und die Länge vom Start- und Endtag. Damit kann ich ohne Probleme die Tags umwandeln aber wie bekomme ich das wieder in den Text ?

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo alf468,

mit m.Index und m.Length von zwei aufeinanderfolgenden Matches, kannst du nicht nur das Tag, sondern auch den Text zwischen den Tags ausschneiden ... und eben wieder zusammenbasteln. Wie ich schon sagte: Der Original-String wird zu keinen Moment verändert. Ist alles ganz einfach.

herbivore

A
196 Beiträge seit 2005
vor 17 Jahren

Ich muss das ganze aber doch nach dem Umwandeln wieder in den Text einfügen.
In dem Moment verändere ich doch den Index

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo alf468,

wir scheinen aneinander vorbei zu reden. Jedenfalls ist alles ganz einfach. Ich würde mal darauf bauen, dass du selber eine Lösung findest.

herbivore

A
196 Beiträge seit 2005
vor 17 Jahren

Ich verzweifle da noch dran.

mit m.Index und m.Length von zwei aufeinanderfolgenden Matches, kannst du nicht nur das Tag, sondern auch den Text zwischen den Tags ausschneiden ... und eben wieder zusammenbasteln.

Dies kann ich doch nur machen wenn ich vorher alle nicht geschlossene bzw. nicht geöffnete Tags entfernt habe.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo alf468,

ich verstehe echt das Problem nicht ganz, sorry. Du kannst doch auch erst den ganzen Original-String durchgehen, dir alle Positionen und Längen (also Quasi alle "Schnitt"-positionen) merken und erst anschließen alle Aktionen durchführen. Dann kannst du alle unnötigen Schnitte rechtzeitig aussortieren.

herbivore