2008-10-09 12 views
29

szukałem sposobu, aby usunąć tekst i RTF ciąg i znalazłem następujące wyrażenia regularnego:Regular Expression do wydobywania tekstu z ciągiem RTF

({\\)(.+?)(})|(\\)(.+?)(\b) 

Jednak wynikowy ciąg ma dwie prawe kątowniki „} "

Przed:{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 MS Shell Dlg 2;}{\f1\fnil MS Shell Dlg 2;}} {\colortbl ;\red0\green0\blue0;} {\*\generator Msftedit 5.41.15.1507;}\viewkind4\uc1\pard\tx720\cf1\f0\fs20 can u send me info for the call pls\f1\par }

Po:} can u send me info for the call pls }

Jakieś przemyślenia na temat poprawienia wyrażeń regularnych?

Edit: Bardziej skomplikowany ciąg takie jak ten nie działa: {\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 MS Shell Dlg 2;}} {\colortbl ;\red0\green0\blue0;} {\*\generator Msftedit 5.41.15.1507;}\viewkind4\uc1\pard\tx720\cf1\f0\fs20 HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\test\\myapp\\Apps\\\{3423234-283B-43d2-BCE6-A324B84CC70E\}\par }

Odpowiedz

2

Według RegexPal, dwa} 's są te pogrubione poniżej:

{\ rtf1 \ ansi \ ansicpg1252 \ deff0 \ deflang1033 {\ fonttbl {\ f0 \ fnil \ fcharset0 MS Shell Dlg 2;} {\ f1 \ fnil MS Shell Dlg 2;} } {\ colortbl; \ red0 \ green0 \ blue0;} { \ generator Msftedit 5.41.15.1507;} \ viewkind4 \ uc1 \ pard \ tx720 \ cf1 \ f0 \ fs20 może wysłać mi informacje dotyczące połączenia pls \ f1 \ par }

udało mi się rozwiązać ten pierwszy nawias klamrowy dodając znak plus, aby regex:

({\\)(.+?)(}+)|(\\)(.+?)(\b) 
      ^
    plus sign added here 

oraz ustalenie nawias klamrowy na końcu zrobiłem to:

({\\)(.+?)(})|(\\)(.+?)(\b)|}$ 
          ^
     this checks if there is a curly brace at the end 

nie wiem format RTF bardzo dobrze, więc to może nie działać we wszystkich przypadkach, ale to działa na przykładzie ...

5

Użyłem tego wcześniej i pracował dla mnie:

\\\w+|\{.*?\}|} 

Prawdopodobnie będzie chciał przyciąć końce wyniku pozbyć się dodatkowych miejsc pozostało.

39

W RTF {i} oznacza grupę. Grupy można zagnieżdżać. \ zaznacza początek słowa kontrolnego. Słowa kontrolne kończą się spacją lub znakiem nie alfabetycznym. Słowo kontrolne może mieć następujący parametr numeryczny, bez żadnego ogranicznika pomiędzy. Niektóre słowa kontrolne również przyjmują parametry tekstowe oddzielone ";". Te słowa kontrolne są zwykle w ich własnych grupach.

Myślę, że udało mi się stworzyć wzór, który zajmuje się większością przypadków.

\{\*?\\[^{}]+}|[{}]|\\\n?[A-Za-z]+\n?(?:-?\d+)?[ ]? 

Zostawia jednak kilka spacji podczas pracy na twoim wzorze.


Idąc zagłębieniu RTF specification (niektóre z nich), widzę, że istnieje wiele pułapek dla usuwania czystego regex opartych. Najbardziej oczywiste jest to, że niektóre grupy powinny być ignorowane (nagłówki, stopki itd.), Podczas gdy inne powinny być renderowane (formatowanie).

Napisałem skrypt Pythona, który powinien pracować lepiej niż mój regex powyżej:

def striprtf(text): 
    pattern = re.compile(r"\\([a-z]{1,32})(-?\d{1,10})?[ ]?|\\'([0-9a-f]{2})|\\([^a-z])|([{}])|[\r\n]+|(.)", re.I) 
    # control words which specify a "destionation". 
    destinations = frozenset((
     'aftncn','aftnsep','aftnsepc','annotation','atnauthor','atndate','atnicn','atnid', 
     'atnparent','atnref','atntime','atrfend','atrfstart','author','background', 
     'bkmkend','bkmkstart','blipuid','buptim','category','colorschememapping', 
     'colortbl','comment','company','creatim','datafield','datastore','defchp','defpap', 
     'do','doccomm','docvar','dptxbxtext','ebcend','ebcstart','factoidname','falt', 
     'fchars','ffdeftext','ffentrymcr','ffexitmcr','ffformat','ffhelptext','ffl', 
     'ffname','ffstattext','field','file','filetbl','fldinst','fldrslt','fldtype', 
     'fname','fontemb','fontfile','fonttbl','footer','footerf','footerl','footerr', 
     'footnote','formfield','ftncn','ftnsep','ftnsepc','g','generator','gridtbl', 
     'header','headerf','headerl','headerr','hl','hlfr','hlinkbase','hlloc','hlsrc', 
     'hsv','htmltag','info','keycode','keywords','latentstyles','lchars','levelnumbers', 
     'leveltext','lfolevel','linkval','list','listlevel','listname','listoverride', 
     'listoverridetable','listpicture','liststylename','listtable','listtext', 
     'lsdlockedexcept','macc','maccPr','mailmerge','maln','malnScr','manager','margPr', 
     'mbar','mbarPr','mbaseJc','mbegChr','mborderBox','mborderBoxPr','mbox','mboxPr', 
     'mchr','mcount','mctrlPr','md','mdeg','mdegHide','mden','mdiff','mdPr','me', 
     'mendChr','meqArr','meqArrPr','mf','mfName','mfPr','mfunc','mfuncPr','mgroupChr', 
     'mgroupChrPr','mgrow','mhideBot','mhideLeft','mhideRight','mhideTop','mhtmltag', 
     'mlim','mlimloc','mlimlow','mlimlowPr','mlimupp','mlimuppPr','mm','mmaddfieldname', 
     'mmath','mmathPict','mmathPr','mmaxdist','mmc','mmcJc','mmconnectstr', 
     'mmconnectstrdata','mmcPr','mmcs','mmdatasource','mmheadersource','mmmailsubject', 
     'mmodso','mmodsofilter','mmodsofldmpdata','mmodsomappedname','mmodsoname', 
     'mmodsorecipdata','mmodsosort','mmodsosrc','mmodsotable','mmodsoudl', 
     'mmodsoudldata','mmodsouniquetag','mmPr','mmquery','mmr','mnary','mnaryPr', 
     'mnoBreak','mnum','mobjDist','moMath','moMathPara','moMathParaPr','mopEmu', 
     'mphant','mphantPr','mplcHide','mpos','mr','mrad','mradPr','mrPr','msepChr', 
     'mshow','mshp','msPre','msPrePr','msSub','msSubPr','msSubSup','msSubSupPr','msSup', 
     'msSupPr','mstrikeBLTR','mstrikeH','mstrikeTLBR','mstrikeV','msub','msubHide', 
     'msup','msupHide','mtransp','mtype','mvertJc','mvfmf','mvfml','mvtof','mvtol', 
     'mzeroAsc','mzeroDesc','mzeroWid','nesttableprops','nextfile','nonesttables', 
     'objalias','objclass','objdata','object','objname','objsect','objtime','oldcprops', 
     'oldpprops','oldsprops','oldtprops','oleclsid','operator','panose','password', 
     'passwordhash','pgp','pgptbl','picprop','pict','pn','pnseclvl','pntext','pntxta', 
     'pntxtb','printim','private','propname','protend','protstart','protusertbl','pxe', 
     'result','revtbl','revtim','rsidtbl','rxe','shp','shpgrp','shpinst', 
     'shppict','shprslt','shptxt','sn','sp','staticval','stylesheet','subject','sv', 
     'svb','tc','template','themedata','title','txe','ud','upr','userprops', 
     'wgrffmtfilter','windowcaption','writereservation','writereservhash','xe','xform', 
     'xmlattrname','xmlattrvalue','xmlclose','xmlname','xmlnstbl', 
     'xmlopen', 
    )) 
    # Translation of some special characters. 
    specialchars = { 
     'par': '\n', 
     'sect': '\n\n', 
     'page': '\n\n', 
     'line': '\n', 
     'tab': '\t', 
     'emdash': u'\u2014', 
     'endash': u'\u2013', 
     'emspace': u'\u2003', 
     'enspace': u'\u2002', 
     'qmspace': u'\u2005', 
     'bullet': u'\u2022', 
     'lquote': u'\u2018', 
     'rquote': u'\u2019', 
     'ldblquote': u'\201C', 
     'rdblquote': u'\u201D', 
    } 
    stack = [] 
    ignorable = False  # Whether this group (and all inside it) are "ignorable". 
    ucskip = 1    # Number of ASCII characters to skip after a unicode character. 
    curskip = 0    # Number of ASCII characters left to skip 
    out = []    # Output buffer. 
    for match in pattern.finditer(text): 
     word,arg,hex,char,brace,tchar = match.groups() 
     if brace: 
     curskip = 0 
     if brace == '{': 
      # Push state 
      stack.append((ucskip,ignorable)) 
     elif brace == '}': 
      # Pop state 
      ucskip,ignorable = stack.pop() 
     elif char: # \x (not a letter) 
     curskip = 0 
     if char == '~': 
      if not ignorable: 
       out.append(u'\xA0') 
     elif char in '{}\\': 
      if not ignorable: 
       out.append(char) 
     elif char == '*': 
      ignorable = True 
     elif word: # \foo 
     curskip = 0 
     if word in destinations: 
      ignorable = True 
     elif ignorable: 
      pass 
     elif word in specialchars: 
      out.append(specialchars[word]) 
     elif word == 'uc': 
      ucskip = int(arg) 
     elif word == 'u': 
      c = int(arg) 
      if c < 0: c += 0x10000 
      if c > 127: out.append(unichr(c)) 
      else: out.append(chr(c)) 
      curskip = ucskip 
     elif hex: # \'xx 
     if curskip > 0: 
      curskip -= 1 
     elif not ignorable: 
      c = int(hex,16) 
      if c > 127: out.append(unichr(c)) 
      else: out.append(chr(c)) 
     elif tchar: 
     if curskip > 0: 
      curskip -= 1 
     elif not ignorable: 
      out.append(tchar) 
    return ''.join(out) 

Działa poprzez analizowanie kodu RTF i omijając żadnej grupy, która ma „cel” określone, a wszystkie „ignorable "grupy ({\* ... }). Dodałem również obsługę niektórych znaków specjalnych.

Brakuje wielu funkcji, aby był to pełny analizator składni, ale powinien wystarczyć dla prostych dokumentów.

AKTUALIZACJA: Ten url mieć ten skrypt do uruchomienia na zaktualizowaną Python 3.x:

https://gist.github.com/gilsondev/7c1d2d753ddb522e7bc22511cfb08676

+0

Dobra odpowiedź. \ ~ to nie łamanie przestrzeni, więc nie powinno char == '~' dopisać u '\ u00a0? –

+0

Rzeczywiście. Zaktualizowano. Dzięki. –

+0

Cool. Myślę też (ale nie jestem pewien), że możesz zmienić ostatnią grupę na [^ {} \\] i po prostu jeść cały tekst na raz zamiast jednego char na raz. Myślę, że gdy parser czyta tekst, można bezpiecznie spożytkować wszystko do następnego metaznaku RTF, tj. Nawiasów klamrowych lub ukośników odwrotnych. –

1

Żadna z odpowiedzi były wystarczające, więc moje rozwiązanie było używać formantu RichTextBox (tak, nawet w non-Winform app), aby wyodrębnić tekst z RTF

1
  FareRule = Encoding.ASCII.GetString(FareRuleInfoRS.Data); 
       System.Windows.Forms.RichTextBox rtf = new System.Windows.Forms.RichTextBox(); 
       rtf.Rtf = FareRule; 
       FareRule = rtf.Text; 
+0

Uwaga: Nie będzie działać z częściowymi ciągami RTF. Formant RichTextBox wymaga pełnego dobrze ukształtowanego wejścia RTF – ProVega

6

tej pory nie znalazłem dobrej odpowiedzi na to albo inny niż za pomocą kontroli RichTextBox:

/// <summary> 
    /// Strip RichTextFormat from the string 
    /// </summary> 
    /// <param name="rtfString">The string to strip RTF from</param> 
    /// <returns>The string without RTF</returns> 
    public static string StripRTF(string rtfString) 
    { 
     string result = rtfString; 

     try 
     { 
      if (IsRichText(rtfString)) 
      { 
       // Put body into a RichTextBox so we can strip RTF 
       using (System.Windows.Forms.RichTextBox rtfTemp = new System.Windows.Forms.RichTextBox()) 
       { 
        rtfTemp.Rtf = rtfString; 
        result = rtfTemp.Text; 
       } 
      } 
      else 
      { 
       result = rtfString; 
      } 
     } 
     catch 
     { 
      throw; 
     } 

     return result; 
    } 

    /// <summary> 
    /// Checks testString for RichTextFormat 
    /// </summary> 
    /// <param name="testString">The string to check</param> 
    /// <returns>True if testString is in RichTextFormat</returns> 
    public static bool IsRichText(string testString) 
    { 
     if ((testString != null) && 
      (testString.Trim().StartsWith("{\\rtf"))) 
     { 
      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 

Edytuj: Dodano metodę IsRichText.

4

Wygląda na to, że używanie Richtextbox jest oficjalną odpowiedzią firmy Microsoft na ten problem!

3

Zrobiłem to funkcja pomocnika, aby zrobić to w JavaScript. Do tej pory to działa dobrze dla prostego usuwania formatowania RTF dla mnie.

function stripRtf(str){ 
    var basicRtfPattern = /\{\*?\\[^{}]+;}|[{}]|\\[A-Za-z]+\n?(?:-?\d+)?[ ]?/g; 
    var newLineSlashesPattern = /\\\n/g; 
    var ctrlCharPattern = /\n\\f[0-9]\s/g; 

    //Remove RTF Formatting, replace RTF new lines with real line breaks, and remove whitespace 
    return str 
     .replace(ctrlCharPattern, "") 
     .replace(basicRtfPattern, "") 
     .replace(newLineSlashesPattern, "\n") 
     .trim(); 
} 

odnotowania:

  • I nieco zmodyfikowany regex napisany przez @Markus Jarderot powyżej. Teraz usuwa ukośniki na końcu nowych linii w dwóch krokach, aby uniknąć bardziej złożonego wyrażenia regularnego.
  • .trim() jest obsługiwany tylko w nowszych przeglądarkach. Jeśli chcesz mieć wsparcie dla nich wtedy zobaczyć: Trim string in JavaScript?

EDIT: Zaktualizowaliśmy regex obejść pewne problemy Znalazłem od opublikowania tego pierwotnie. Używam tego w projekcie, zobaczyć go w kontekście tutaj: https://github.com/chrismbarr/LyricConverter/blob/865f17613ee8f43fbeedeba900009051c0aa2826/scripts/parser.js#L26-L37

2

późnej specjalista, ale poniżej regex pomógł nam z kodem RTF znaleźliśmy w naszym DB (używamy go wewnątrz RDL poprzez SSRS).

To wyrażenie usunęło go dla naszego zespołu. Chociaż może to tylko rozwiązać nasz konkretny RTF, może to być pomocna baza dla kogoś. Mimo że ta gra jest niesamowicie przydatna do testów na żywo.

http://regexpal.com/

{\*?\\.+(;})|\s?\\[A-Za-z0-9]+|\s?{\s?\\[A-Za-z0-9]+\s?|\s?}\s? 

Nadzieja to pomaga, K

0

Oto oświadczenie Oracle SQL, które mogą pozbawić RTF z pola Oracle:

SELECT REGEXP_REPLACE(
    REGEXP_REPLACE(
     CONTENT, 
     '\\(fcharset|colortbl)[^;]+;', '' 
    ), 
    '(\\[^ ]+ ?)|[{}]', '' 
) TEXT 
FROM EXAMPLE WHERE CONTENT LIKE '{\rtf%'; 

ten jest przeznaczony dla danych z systemu Windows kontrola tekstu sformatowanego, a nie pliki RTF. Ograniczenia są:

  • \{ i \} nie są zastępowane { i }
  • nagłówki i stopki nie są obsługiwane specjalnie
  • obrazy i inne osadzone obiekty nie są traktowane specjalnie (nie ma pojęcia, co się stanie, jeśli jeden z nich napotkano!)

Działa poprzez usunięcie znaczników \fcharset i \colourtbl, które są specjalne, ponieważ dane podążają za nimi, dopóki nie zostanie osiągnięty ;. Następnie usuwa wszystkie znaczniki \xxx (w tym jeden opcjonalny końcowy obszar), a następnie wszystkie znaki { i }. Obsługuje najprostszy format RTF, taki jak to, co można uzyskać dzięki kontroli tekstu sformatowanego.

Powiązane problemy