Espressione regolare per l’estrazione di testo da una stringa RTF

Stavo cercando un modo per rimuovere il testo da e la stringa RTF e ho trovato il seguente regex:

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

Tuttavia la stringa risultante ha due parentesi angolari “}”

Prima: {\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 }

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

Qualche idea su come migliorare la regex?

Modifica: una stringa più complicata come questa non funziona: {\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 }

    In RTF, {e} indica un gruppo. I gruppi possono essere annidati. \ segna l’inizio di una parola di controllo. Le parole di controllo terminano con uno spazio o un carattere non alfabetico. Una parola di controllo può avere un parametro numerico che segue, senza alcun delimitatore in mezzo. Alcune parole di controllo accettano anche parametri di testo, separati da “;”. Quelle parole di controllo sono di solito nei loro gruppi.

    Penso di essere riuscito a creare uno schema che si occupa della maggior parte dei casi.

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

    Tuttavia lascia alcuni spazi quando viene eseguito sul tuo modello.


    Passando attraverso le specifiche RTF (alcune delle quali), vedo che ci sono molte insidie ​​per gli estrattori puri basati su regex. Il più ovvio è che alcuni gruppi dovrebbero essere ignorati (intestazioni, piè di pagina, ecc.), Mentre altri dovrebbero essere resi (formattazione).

    Ho scritto uno script Python che dovrebbe funzionare meglio della mia regex di cui sopra:

     def striprtf(text): pattern = re.compile(r"\\([az]{1,32})(-?\d{1,10})?[ ]?|\\'([0-9a-f]{2})|\\([^az])|([{}])|[\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) 

    Funziona analizzando il codice RTF e saltando tutti i gruppi che hanno una “destinazione” specificata e tutti i gruppi “ignorabili” ( {\*} ). Ho anche aggiunto la gestione di alcuni caratteri speciali.

    Ci sono molte funzionalità mancanti per rendere questo un parser completo, ma dovrebbero essere sufficienti per documenti semplici.

    AGGIORNATO: questo URL ha aggiornato questo script per l’esecuzione su Python 3.x:

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

    Finora, non abbiamo trovato una buona risposta a questo, oltre all’utilizzo di un controllo RichTextBox:

      ///  /// Strip RichTextFormat from the string ///  /// The string to strip RTF from /// The string without RTF 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; } ///  /// Checks testString for RichTextFormat ///  /// The string to check /// True if testString is in RichTextFormat public static bool IsRichText(string testString) { if ((testString != null) && (testString.Trim().StartsWith("{\\rtf"))) { return true; } else { return false; } } 

    Modifica: Aggiunto il metodo IsRichText.

    L’ho usato prima e ha funzionato per me:

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

    Probabilmente vorrai tagliare le estremità del risultato per eliminare gli spazi extra rimasti.

    Regex non risolverà mai al 100% questo problema, hai bisogno di un parser. Controlla questa implementazione in CodeProject (è in C #): http://www.codeproject.com/Articles/27431/Writing-Your-Own-RTF-Converter

    Ho fatto questa funzione di supporto per farlo in JavaScript. Finora questo ha funzionato bene per la semplice rimozione della formattazione RTF per me.

     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(); } 

    Di nota:

    • Ho leggermente modificato la regex scritta da @Markus Jarderot sopra. Ora rimuove le barre alla fine delle nuove linee in due passaggi per evitare una regex più complessa.
    • .trim() è supportato solo nei nuovi browser. Se hai bisogno di supporto per questi allora vedi questo: Trim stringa in JavaScript?

    EDIT: ho aggiornato la regex per aggirare alcuni problemi che ho trovato dal post di questo in origine. Sto usando questo in un progetto, lo vedo nel contesto qui: https://github.com/chrismbarr/LyricConverter/blob/865f17613ee8f43fbeedeba900009051c0aa2826/scripts/parser.js#L26-L37

    Secondo RegexPal , i due} sono quelli in grassetto qui sotto:

    {\ 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 può inviarmi informazioni per la chiamata pls \ f1 \ par }

    Sono stato in grado di correggere la prima parentesi graffa aggiungendo un segno più alla regex:

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

    E per sistemare la parentesi graffa alla fine, l’ho fatto:

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

    Non conosco il formato RTF molto bene, quindi potrebbe non funzionare in tutti i casi, ma funziona sul tuo esempio …

    Contributo tardivo, ma la regex di seguito ci ha aiutato con il codice RTF che abbiamo trovato nel nostro DB (lo stiamo utilizzando all’interno di un RDL tramite SSRS).

    Questa espressione lo ha rimosso per la nostra squadra. Anche se potrebbe risolvere solo il nostro RTF specifico, potrebbe essere una base utile per qualcuno. Anche se questo webby è incredibilmente utile per i test dal vivo.

    http://regexpal.com/

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

    Spero che questo aiuti, K

    Nessuna delle risposte era sufficiente, quindi la mia soluzione era utilizzare il controllo RichTextBox (sì, anche in un’applicazione non Winform) per estrarre il testo da RTF

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

    Ecco un’istruzione Oracle SQL in grado di rimuovere RTF da un campo Oracle:

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

    Questo è progettato per i dati da controlli rich text di Windows, non da file RTF. Le limitazioni sono:

    • \{ e \} non vengono sostituiti con { e }
    • Intestazioni e piè di pagina non sono gestiti appositamente
    • Le immagini e altri oggetti incorporati non vengono gestiti in modo specifico (non si sa cosa succederà se uno di questi si incontra!)

    Funziona rimuovendo prima i \fcharset e \colourtbl , che sono speciali perché i dati li seguono fino a quando ; è raggiunto. Quindi rimuove tutti i tag \xxx (compreso un singolo spazio finale opzionale), seguito da tutti i caratteri { e } . Questo gestisce la maggior parte degli RTF semplici come quello che ottieni dal controllo rich text.