2008-10-09 25 views
29

que estaba buscando una manera de eliminar el texto desde y cadena RTF y me encontré con la siguiente expresión regular:expresión regular para extraer texto de una cadena RTF

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

Sin embargo, la cadena resultante tiene dos soportes en ángulo recto "} "

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

Después:} can u send me info for the call pls }

¿Alguna idea sobre cómo mejorar la expresión regular?

Editar: Una cadena más complicado como este no funciona: {\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 }

Respuesta

2

Según RegexPal, los dos} 's son los que en negrita a continuación:

{\ 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 me puede enviar información para la llamada pls \ f1 \ par }

pude fijar la primera llave de cierre mediante la adición de un signo más a la expresión regular:

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

Y para fijar la llave de cierre al final, lo hice:

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

no sé el formato RTF muy bien por lo que este podría no funcionar en todos los casos, pero funciona en su ejemplo ...

5

He usado esto antes y ha funcionado para mí:

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

Es probable que desee para recortar los extremos del resultado para deshacerse de los espacios extra sobrante.

39

En RTF, {y} marca un grupo. Los grupos pueden ser anidados. \ marca el comienzo de una palabra de control. Las palabras de control terminan con un espacio o un carácter no alfabético. Una palabra de control puede tener un parámetro numérico siguiente, sin ningún delimitador en el medio. Algunas palabras de control también toman parámetros de texto, separados por ';'. Esas palabras de control generalmente están en sus propios grupos.

Creo que he logrado hacer un patrón que se ocupa de la mayoría de los casos.

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

Sin embargo, deja algunos espacios cuando se ejecuta en su patrón.


Ir a través del RTF specification (algunos de ellos), veo que hay una gran cantidad de trampas para los decapantes basados ​​en expresiones regulares puro. El más obvio es que algunos grupos se deben ignorar (encabezados, pies de página, etc.), mientras que otros se deben representar (formatear).

He escrito un script en Python que debería funcionar mejor que mi expresión regular anterior:

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) 

Funciona mediante el análisis del código RTF y saltarse ningún grupo que tiene un "destino" se especifica, y toda "ignorable "grupos ({\* ... }). También agregué el manejo de algunos personajes especiales.

Faltan muchas características para hacer que este sea un analizador completo, pero debería ser suficiente para documentos simples.

ACTUALIZADO: Esta URL tienen esta secuencia de comandos actualizada para funcionar en Python 3.x:

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

+0

Nice answer. \ ~ es el espacio sin interrupciones, por lo que no debería char == '~' append u '\ u00a0? –

+0

De hecho. Actualizado. Gracias. –

+0

Cool. También creo (pero no estoy seguro) que puede cambiar el último grupo a [^ {} \\] y simplemente comer todo el texto a la vez en lugar de un char a la vez. Creo que una vez que el analizador está leyendo el texto, es seguro consumir todo hasta el siguiente metacarácter RTF, es decir, llaves o barra diagonal inversa. –

1

Ninguna de las respuestas fueron suficientes, por lo que mi solución fue usar el control RichTextBox (sí, incluso en una aplicación no Winform) para extraer texto de 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

Nota: Esto no funcionará con cadenas RTF parciales. El control RichTextBox requiere una entrada RTF completa y bien formada – ProVega

6

hasta ahora, no hemos encontrado una buena respuesta a esta ya sea, con excepción de usar un control 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; 
     } 
    } 

Editar: Método de IsRichText añadido.

4

Parece que usar el Richtextbox es la respuesta oficial de Microsoft para este problema.

3

Hice esta función auxiliar para hacer esto en JavaScript. Hasta ahora, esto ha funcionado bien para la eliminación de formato RTF simple para mí.

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

De Nota:

  • he modificado ligeramente la expresión regular escrito por @Markus Jarderot anteriormente. Ahora elimina las barras al final de las nuevas líneas en dos pasos para evitar una expresión regular más compleja.
  • .trim() solo es compatible con los navegadores más nuevos. Si es necesario tener soporte para estos entonces ver esto: Trim string in JavaScript?

EDIT: He actualizado la expresión regular para evitar algunos problemas que he encontrado desde la publicación de este principio. Estoy usando esto en un proyecto, véalo en contexto aquí: https://github.com/chrismbarr/LyricConverter/blob/865f17613ee8f43fbeedeba900009051c0aa2826/scripts/parser.js#L26-L37

2

Contribuyente tardío pero la siguiente expresión regular nos ayudó con el código RTF que encontramos en nuestra base de datos (lo estamos usando en una RDL a través de SSRS).

Esta expresión lo quitó para nuestro equipo. Aunque puede resolver nuestro RTF específico, puede ser una base útil para alguien. Aunque este webby es increíblemente útil para las pruebas en vivo.

http://regexpal.com/

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

Espero que esto ayude, K

0

He aquí un comunicado de Oracle SQL que se puede pelar RTF de un campo de Oracle:

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

Esto está diseñado para datos desde Windows controles de texto enriquecido, no archivos RTF. Las limitaciones son:

  • \{ y \} no se sustituyen con { y }
  • encabezados y pies de página no se manejan especialmente
  • imágenes y otros objetos incrustados no se manejan de manera especial (ni idea de lo que sucederá si uno de estos se encuentra!)

Funciona al quitar primero las etiquetas \fcharset y \colourtbl, que son especial porque los datos los siguen hasta que se alcanza ;. A continuación, elimina todas las etiquetas \xxx (incluido un único espacio posterior opcional), seguido de todos los caracteres { y }. Esto maneja el RTF más simple, como lo que obtienes del control de texto enriquecido.

Cuestiones relacionadas