代码之家  ›  专栏  ›  技术社区  ›  adeel825

用于从RTF字符串中提取文本的正则表达式

  •  33
  • adeel825  · 技术社区  · 16 年前

    我正在寻找从和RTF字符串中删除文本的方法,发现了以下正则表达式:

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

    但是,结果字符串有两个直角括号“}”

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

    之后: } can u send me info for the call pls }

    关于如何改进regex有什么想法吗?

    编辑: 像这样更复杂的字符串不起作用: {\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 }

    10 回复  |  直到 14 年前
        1
  •  64
  •   Gilson Filho Markus Jarderot    9 年前

    在RTF中,{和}标记一个组。可以嵌套组。\标记控制字的开头。控制字以空格或非字母字符结尾。控制字后面可以有一个数字参数,中间不能有任何分隔符。一些控制字还采用文本参数,以“;”分隔。这些控制词通常在它们自己的组中。

    我想我已经成功地建立了一个模式来处理大多数案件。

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

    但在您的模式上运行时,它会留下一些空间。


    通过 RTF specification (其中一些),我发现纯基于regex的脱衣舞者有很多陷阱。最明显的一点是,一些组应该被忽略(页眉、页脚等),而其他组应该被呈现(格式化)。

    我已经编写了一个Python脚本,它应该比上面的正则表达式工作得更好:

    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)
    

    它通过解析RTF代码来工作,并跳过指定了“目的地”的任何组以及所有“可忽略”的组( {\* 。。。 } )。我还添加了一些特殊字符的处理。

    要使它成为一个完整的解析器,还缺少很多功能,但对于简单的文档来说应该已经足够了。

    更新日期: 此url更新了此脚本以在Python 3上运行。x:

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

        2
  •  7
  •   John Chuckran    16 年前

    我以前用过这个,它对我有用:

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

    您可能需要修剪结果的末端,以消除剩余的额外空间。

        3
  •  7
  •   Steven King    14 年前

    到目前为止,除了使用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;
            }
        }
    

    编辑:添加了IsRichText方法。

        4
  •  4
  •   Community CDub    8 年前

    我用JavaScript制作了这个助手函数。到目前为止,对于我来说,这对于简单的RTF格式删除非常有效。

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

    值得注意的是:

    • 我稍微修改了 @马库斯·贾德罗特 在上面现在,它分两步删除新行末尾的斜杠,以避免更复杂的正则表达式。
    • .trim() 仅在较新的浏览器中受支持。如果您需要这些支持,请参见以下内容: Trim string in JavaScript?

    编辑:我已经更新了正则表达式,以解决自最初发布此内容以来发现的一些问题。我在一个项目中使用它,请在此处上下文中查看: https://github.com/chrismbarr/LyricConverter/blob/865f17613ee8f43fbeedeba900009051c0aa2826/scripts/parser.js#L26-L37

        5
  •  3
  •   jdearana    13 年前

    Regex永远不会百分之百地解决这个问题,您需要一个解析器。 在CodeProject中检查此实现(虽然是在C中): http://www.codeproject.com/Articles/27431/Writing-Your-Own-RTF-Converter

        6
  •  2
  •   Paige Ruten    16 年前

    根据 RegexPal ,这两个}是下面加粗的:

    {\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您能给我发送电话信息吗请 }

    我可以通过在正则表达式中添加加号来修复第一个大括号:

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

    为了固定末端的花括号,我做了如下操作:

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

    我不太了解RTF格式,所以这可能不会在所有情况下都适用,但它适用于您的示例。。。

        7
  •  2
  •   Ashley Medway Karthi    11 年前

    虽然贡献者较晚,但下面的正则表达式帮助我们使用了在DB中找到的RTF代码(我们通过SSRS在RDL中使用它)。

    这个表达式为我们的团队删除了它。虽然它可能只是解决我们特定的RTF,但它可能对某些人有帮助。尽管这种webby非常方便进行现场测试。

    http://regexpal.com/

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

    希望这有帮助, K

        8
  •  1
  •   adeel825    16 年前

    没有一个答案是足够的,所以我的解决方案是使用RichTextBox控件(是的,即使在非Winform应用程序中也是如此)从RTF中提取文本

        9
  •  1
  •   Malvineous    8 年前

    下面是一条Oracle SQL语句,它可以从Oracle字段中剥离RTF:

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

    这是为来自Windows富文本控件的数据而设计的,而不是RTF文件。 限制包括:

    • \{ \} 未替换为 { }
    • 页眉和页脚不是专门处理的
    • 图像和其他嵌入对象不是专门处理的(不知道如果遇到其中一个会发生什么!)

    它通过首先移除 \fcharset \colourtbl 标记,因为数据跟随它们直到 ; 已到达。然后删除所有 \xxx 标记(包括一个可选的尾随空格),后跟所有 { } 字符。这将处理最简单的RTF,例如从富格文本控件获得的内容。

        10
  •  1
  •   Grant Miller    6 年前

    以下解决方案允许您从RTF字符串中提取文本:

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