web-dev-qa-db-ja.com

RTF文字列からテキストを抽出するための正規表現

And RTF stringからテキストを削除する方法を探していたところ、次の正規表現が見つかりました。

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

ただし、結果の文字列には2つの直角山かっこ「}」があります

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

正規表現を改善する方法について何か考えはありますか?

編集:このようなより複雑な文字列は機能しません:{\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 }

36
adeel825

RTFでは、{と}はグループをマークします。グループはネストできます。 \は、制御ワードの始まりを示します。制御語は、スペースまたはアルファベット以外の文字で終わります。コントロールWordには、間に区切り文字を入れずに、数値パラメーターを後に付けることができます。一部の制御ワードは、「;」で区切られたテキストパラメーターも使用します。これらの制御ワードは通常、独自のグループに含まれます。

私は、ほとんどのケースを処理するパターンを作成できたと思います。

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

ただし、パターンで実行すると、いくつかのスペースが残ります。


RTF仕様 (その一部)に進むと、純粋な正規表現ベースのストリッパーには落とし穴がたくさんあることがわかります。最も明白なものは、いくつかのグループ(ヘッダー、フッターなど)を無視し、他のグループをレンダリング(フォーマット)することです。

上記の正規表現よりもうまく動作するはずの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コードを解析し、「宛先」が指定されているグループとすべての「無視可能な」グループ({\*...})。また、いくつかの特殊文字の処理も追加しました。

これを完全なパーサーにするための多くの機能が欠落していますが、単純なドキュメントには十分なはずです。

UPDATED:このURLは、Python 3.x:

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

54
Markus Jarderot

これまでのところ、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メソッドを追加しました。

7
Steven King

私はこれを以前に使用しましたが、私にとってはうまくいきました:

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

おそらく、結果の両端をトリミングして、余分なスペースを取り除いてください。

6
John Chuckran

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

Of注:

  • 上記の@ Markus Jarderotによって書かれた正規表現を少し変更しました。複雑な正規表現を避けるために、2行で改行の最後のスラッシュを削除するようになりました。
  • .trim()は、新しいブラウザーでのみサポートされています。これらのサポートが必要な場合は、次を参照してください。 JavaScriptの文字列をトリムしますか?

編集:これを元々投稿してから発見したいくつかの問題を回避するために正規表現を更新しました。私はこれをプロジェクトで使用しています、コンテキストでそれを参照してください: https://github.com/chrismbarr/LyricConverter/blob/865f17613ee8f43fbeedeba900009051c0aa2826/scripts/parser.js#L26-L37

4
CBarr

Regexがこの問題を100%解決することはありません。パーサーが必要です。 CodeProjectでこの実装を確認します(C#にあります): http://www.codeproject.com/Articles/27431/Writing-Your-Own-RTF-Converter

3
juanjo.arana

後期の貢献者ですが、以下の正規表現は、DBで見つけたRTFコード(SSRSを介してRDL内で使用しています)に役立ちました。

この式により、チームから削除されました。特定のRTFを解決するだけの場合もありますが、誰かにとって有用な基盤になる可能性があります。ただし、このwebbyはライブテストには非常に便利です。

http://regexpal.com/

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

これがお役に立てば幸いです、K

2
KevHun

RegexPal によると、2つの}は以下の太字で示しています。

{\ 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通話に関する情報を送信できますかpls\f1\par}

正規表現にプラス記号を追加することで、最初の波括弧を修正できました。

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

そして、最後に中括弧を修正するために、私はこれをしました:

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

RTF形式がよくわからないので、これはすべての場合に機能するわけではありませんが、あなたの例では機能します...

2
Jeremy Ruten

次のソリューションでは、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

答えがどれも十分ではなかったため、私の解決策は、RichTextBoxコントロールを使用して(はい、Winform以外のアプリでも)RTFからテキストを抽出することでした

1
adeel825

OracleフィールドからRTFを除去できるOracle SQLステートメントを次に示します。

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

これは、Windowsリッチテキストコントロールからのデータ用に設計されており、RTFファイルではありません。制限は次のとおりです。

  • \{および\}は、{および}に置き換えられません
  • ヘッダーとフッターは特別に処理されません
  • 画像やその他の埋め込みオブジェクトは特別に処理されません(これらのいずれかに遭遇するとどうなるかわかりません!)

最初に\fcharsetおよび\colourtblタグを削除することで機能します。これらのタグは、;に達するまでデータが続くため特別です。次に、すべての\xxxタグ(単一のオプションの末尾スペースを含む)を削除し、その後にすべての{および}文字を削除します。これは、リッチテキストコントロールから取得するものなど、最も単純なRTFを処理します。

0
Malvineous