Edytor znaczników formatu tekstu - problem z Regex

0

Chciałbym dostosować do swoich potrzeb lub stworzyć edytor znaczników formatu tekstu tak jak jest to możliwe dla komponentu FormatedLabel od DevExpress:
screenshot-20210521200214.png
screenshot-20210521200231.png
Problem polega na tym, że moje znaczniki nie są BBCode tylko używają nawiasów ostrych < > zamiast kwadratowych jak w przykładzie i trochę inaczej zapisują kolor czcionki.
Swoich znaczników nie zmienię, ponieważ pochodzą z gotowego rozwiązania.
Na początku zacząłem przerabiać edytor od DevExpress i było całkiem nieźle do momentu konwertowania pomiędzy widokami. Ingerowanie w ich kod głębiej nie ma sensu bo to multum roboty ale w ostateczności może jeszcze raz do tego usiądę.
Potrzebne mi są jedynie 4 znaczniki: pogrubienie, kursywa, podkreślenie i kolor czcionki. Chciałem stworzyć swój edytor na wzór tego powyższego ale mam problem ze znalezieniem fragmentów tekstu ze znacznikami, który mam formatować. Chciałem ugryźć to regexami ale nie pracuje z nimi na co dzień więc mam problem.
Załóżmy, że mam taki tekst ze znacznikami:

początek <b> pogrubienie1 </b> =  <b> pobrubienie2 </b> coś na końcu

Potrzebuję wyciągnąć teraz z tego pary znaczników

<b> </b>

żeby odpowiednio pogrubić widok w RTF.
Napisałem takie wyrażenie:

([^<>]*)(<b>)([^<>]*)(</b>)([^<>]*)

które teoretycznie dla danego przykładu działa ok ale przy zagnieżdżonych innych znacznikach będzie problem. Mam 2 dopasowania i mogę wyciągnąć wszystko i zmienić to co mi jest potrzebne.
Problem w tym, że przy błędach w tekście wyrażenie pomija część tekstu, czyli przy konwertowaniu ucięło by mi informacje. Przykład błędnego tekstu, który nie zostanie prawidłowo rozpoznany przez moje wyrażenie:

początek nie jest łapany <b> pierwszy znacznik bez domknięcia <b> pogrubienie1 </b> =  <b> pobrubienie2 </b> coś na końcu

Przy dodatkowych znacznikach jest podobnie:

<b> <i>pogrubienie i kursywa nie łapane</i> </b> =  <b> pobrubienie2 </b> coś na końcu

Zmodyfikowałem wyrażenie na takie:

([^<>]*)(<b>|<i>)([^<>]*)(</b>|</i>)([^<>]*)

ale dalej w pierwszej części nie łapie mi pogrubienia, tylko kursywę.
Z tym też jest problem na początku:

<b> prawa < lewa </b> =  <b> pobrubienie2 </b> coś na końcu

Wyrażenia testuję na https://regex101.com/
Czy ktoś byłby w stanie mi pomóc z tymi regexami?
Alternatywnie może można to zrobić inaczej?

2

Kiedyś stworzyłem sobie formatowalną etykietę, która wspierała znaczniki HTML — podział tekstu na nagłówki i paragrafy, wsparcie tekstu zwykłego, pogrubionego, pochylonego i przekreślonego, a także obsługa klikalnych linków (jedno- lub wielowierszowych). Co prawda zestaw znaczników i funkcji nieco inny niż Twój, ale podobieństwo dość wyraźne.

Aby przerobić goły tekst na informacje wymagane do wyrenderowania etykiety, treść traktowałem jako jeden ciąg znaków i analizowałem go znak po znaku, wyrzucając do listy kolejne tokeny (słowo lub znacznik). Kolejnym krokiem było obliczenie pozycji każdego słowa na ekranie, zgodnie z kolejnością tokenów w liście. Na koniec wyznaczało się obszary linków, czyli miejsca, w których kursor miał się na łapkę zmienić. Ostatni krok to renderowanie, czyli iteracja po liście tokenów z kompletnymi danymi — jeśli tokenem było słowo, to renderowało się tekst, a jeśli znacznik, to zmieniało styl fontu itp.

Tak więc sugeruję wywalić wyrażenia regularne i parsować tekst w ww. sposób. Znak po znaku, wyciągać tokeny i wrzucać na listę. Czyli zrobić sobie po prostu zwyczajny parser i podejść do formatowalnego tekstu jak do kodu źródłowego.

0

Liczyłem, że da radę zrobić to "wygodniej". Spróbuję chyba najpierw pogrzebać w kodzie DevExpress bo mimo wszystko wydaje mi się, że będzie z tym mniej pracy niż pisać wszystko od początku. Jeśli jednak nie uda się tamtego przerobić to chyba Twoja propozycja będzie jedynym wyjściem.

1

Sugeruję użyć takich wzorców:
(<b>)(.*?)(<\/b>)
(<i>)(.*?)(<\/i>)
(<u>)(.*?)(<\/u>)
I potraktować po kolei osobno dla każdego znacznika do podmiany.

0

Wiedziałem, że przekombinowałem. Wzorce @Freja Draco rozwiązują mój problem z Regexem. Dzięki!
W między czasie udało mi się prawie przerobić kod, który obstawiałem, że będzie sprawiał problemy. Potestuję i wybiorę ostateczne rozwiązanie do mojego problemu.

2

Nie no spoko, tylko ciekaw jestem co zrobisz, jak zechcesz dodać obsługę innych znaczników. Będziesz wydłużał regexy w nieskończoność? Przyjdzie czas, że dojdą kolejne znaczniki i Twoje rozwiązanie zacznie Ci mocno ciążyć. A parsowanie tekstu i rozkładanie go na tokeny to raptem dwie pętle i opcjonalne kopiowanie fragmentów tekstu. :D

Nie chcę Cię zmuszać do niczego, ale pamiętaj, że regexy to nie jest jedyne rozwiązanie. Przy czym jeśli zależy Ci jedynie na zamianie jednych znaczników na inne (nie bawiłem się RichEditami i nie wiem, jakich znaczników używają), to spokojnie możesz użyć funkcji StringReplace z flagą rfReplaceAll.

0

Nowych znaczników nie będzie bo to adaptacja do rozwiązania zewnętrznego, które nie przewiduje dalszego rozwoju w tym kierunku. A jeśli coś by się miało w tym zmienić to nie w ciągu najbliższych kilku lat.
Z RichEditem nie chodzi o znaczniki bo tam formatowanie jest za pomocą stylowania:

Rtf.SelAttributes2.Style := Rtf.SelAttributes2.Style + [fsBold]

Ale żeby to zmienić to muszę wydobyć tekst ze znaczników, znaczniki wywalić i sformatować wnętrze.
Tak jak podawałem przykłady powyżej StringReplace w tym przypadku nie byłoby dobrym rozwiązaniem (zakładając że chodziło by tylko o podmianę tagów), przykład:

znacznik <b> pogrubi zaznaczony obszar <b> pogrubienie1 </b> =  <b> pobrubienie2 </b> 

W tym przypadku pierwszy znacznik nie powinien i nie może zostać zmieniony.
Obecnie przerobiłem kod DevExpress i chyba na tym pozostanę.

1 użytkowników online, w tym zalogowanych: 0, gości: 1