web-dev-qa-db-fra.com

Expression régulière pour extraire du texte d'une chaîne RTF

Je cherchais un moyen de supprimer du texte de la chaîne RTF et j'ai trouvé l'expression régulière suivante:

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

Cependant, la chaîne résultante a deux crochets à angle droit "}"

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

Après:} can u send me info for the call pls }

Avez-vous des réflexions sur la façon d'améliorer l'expression régulière?

Edit: Une chaîne plus compliquée comme celle-ci ne fonctionne pas: {\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 }

38
adeel825

Dans RTF, {et} marque un groupe. Les groupes peuvent être imbriqués.\marque le début d'un mot de contrôle. Les mots de contrôle se terminent par un espace ou un caractère non alphabétique. Un mot de contrôle peut avoir un paramètre numérique suivant, sans aucun délimiteur entre les deux. Certains mots de contrôle prennent également des paramètres de texte, séparés par ";". Ces mots de contrôle sont généralement dans leurs propres groupes.

Je pense que j'ai réussi à créer un modèle qui prend en charge la plupart des cas.

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

Il laisse cependant quelques espaces lorsqu'il est exécuté sur votre modèle.


En passant par spécification RTF (une partie), je vois qu'il y a beaucoup d'embûches pour les décapants basés sur des regex purs. Le plus évident est que certains groupes doivent être ignorés (en-têtes, pieds de page, etc.), tandis que d'autres doivent être rendus (formatage).

J'ai écrit un script Python qui devrait fonctionner mieux que mon expression régulière ci-dessus:

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)

Il fonctionne en analysant le code RTF, et en ignorant tous les groupes ayant une "destination" spécifiée, et tous les groupes "ignorables" ({\*...}). J'ai également ajouté la gestion de certains caractères spéciaux.

Il y a beaucoup de fonctionnalités manquantes pour en faire un analyseur complet, mais cela devrait suffire pour les documents simples.

MISE À JOUR: Cette URL a ce script mis à jour pour s'exécuter sur Python 3.x:

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

58
Markus Jarderot

Jusqu'à présent, nous n'avons pas non plus trouvé de bonne réponse à cela, à part utiliser un contrôle 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;
        }
    }

Edit: Ajout de la méthode IsRichText.

7
Steven King

Je l'ai utilisé auparavant et cela a fonctionné pour moi:

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

Vous voudrez probablement couper les extrémités du résultat pour vous débarrasser des espaces supplémentaires restants.

6
John Chuckran

J'ai créé cette fonction d'aide pour le faire en JavaScript. Jusqu'à présent, cela a bien fonctionné pour la suppression de formatage simple RTF pour moi.

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

À noter:

  • J'ai légèrement modifié l'expression régulière écrite par @ Markus Jarderot ci-dessus. Il supprime désormais les barres obliques à la fin des nouvelles lignes en deux étapes pour éviter une expression régulière plus complexe.
  • .trim() n'est pris en charge que dans les nouveaux navigateurs. Si vous avez besoin d'un support pour ceux-ci, voyez ceci: Trim string in JavaScript?

EDIT: J'ai mis à jour l'expression régulière pour contourner certains problèmes que j'ai trouvés depuis la publication de ce message à l'origine. J'utilise ceci dans un projet, voyez-le en contexte ici: https://github.com/chrismbarr/LyricConverter/blob/865f17613ee8f43fbeedeba900009051c0aa2826/scripts/parser.js#L26-L37

4
CBarr

Regex ne résoudra jamais ce problème à 100%, vous avez besoin d'un analyseur. Vérifiez cette implémentation dans CodeProject (c'est en C # cependant): http://www.codeproject.com/Articles/27431/Writing-Your-Own-RTF-Converter

3
juanjo.arana

Contributeur tardif mais l'expression régulière ci-dessous nous a aidé avec le code RTF que nous avons trouvé dans notre base de données (nous l'utilisons dans un RDL via SSRS).

Cette expression l'a supprimée pour notre équipe. Bien qu'il puisse simplement résoudre notre RTF spécifique, il peut être une base utile pour quelqu'un. Bien que ce site Web soit incroyablement pratique pour les tests en direct.

http://regexpal.com/

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

J'espère que cela aide, K

2
KevHun

Selon RegexPal , les deux} sont ceux en gras ci-dessous:

{\ 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 pouvez-vous m'envoyer des informations pour l'appel pls\f1\par }

J'ai pu réparer la première accolade en ajoutant un signe plus à l'expression régulière:

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

Et pour fixer l'accolade bouclée à la fin, j'ai fait ceci:

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

Je ne connais pas très bien le format RTF donc cela pourrait ne pas fonctionner dans tous les cas, mais cela fonctionne sur votre exemple ...

2
Jeremy Ruten

La solution suivante vous permet d'extraire du texte d'une chaîne RTF:

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

Aucune des réponses n'était suffisante, donc ma solution était d'utiliser le contrôle RichTextBox (oui, même dans une application non Winform) pour extraire le texte de RTF

1
adeel825

Voici une instruction Oracle SQL qui peut supprimer RTF d'un champ Oracle:

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

Il est conçu pour les données des contrôles de texte enrichi de Windows, pas pour les fichiers RTF. Les limitations sont les suivantes:

  • \{ et \} ne sont pas remplacés par { et }
  • Les en-têtes et pieds de page ne sont pas traités spécialement
  • Les images et autres objets intégrés ne sont pas traités spécialement (aucune idée de ce qui se passera si l'un d'eux est rencontré!)

Cela fonctionne en supprimant d'abord le \fcharset et \colourtbl tags, qui sont spéciaux car les données les suivent jusqu'à ; est atteint. Ensuite, il supprime tous les \xxx balises (y compris un seul espace de fin facultatif), suivies de toutes les { et } personnages. Cela gère la plus simple RTF comme ce que vous obtenez du contrôle de texte riche.

1
Malvineous