1

F# と FParsec を使用してマルチパート MIME パーサーを開発しています。私は繰り返し開発しているので、これは非常に洗練されていない、もろいコードです。これは、私の最初の問題を解決するだけです。赤、緑、リファクタリング。

文字列ではなくストリームを解析する必要があるため、実際にループに陥っています。その制約を考えると、私の理解では、パーサーを再帰的に呼び出す必要があります。それを行う方法は、少なくとも私がこれまで進めてきた方法では、私の理解を超えています.

namespace MultipartMIMEParser

open FParsec
open System.IO

type private Post = { contentType : string
                    ; boundary    : string
                    ; subtype     : string
                    ; content     : string }

type MParser (s:Stream) =
  let ($) f x = f x
  let ascii = System.Text.Encoding.ASCII
  let str cs = System.String.Concat (cs:char list)
  let q = "\""
  let qP = pstring q
  let pSemicolon = pstring ";"
  let manyNoDoubleQuote = many $ noneOf q
  let enquoted = between qP qP manyNoDoubleQuote |>> str
  let skip = skipStringCI
  let pContentType = skip "content-type: "
                     >>. manyTill anyChar (attempt $ preturn () .>> pSemicolon)
                     |>> str
  let pBoundary = skip " boundary=" >>. enquoted
  let pSubtype = opt $ pSemicolon >>. skip " type=" >>. enquoted
  let pContent = many anyChar |>> str // TODO: The content parser needs to recurse on the stream.
  let pStream = pipe4 pContentType pBoundary pSubtype pContent
                      $ fun c b t s -> { contentType=c; boundary=b; subtype=t; content=s }
  let result s = match runParserOnStream pStream () "" s ascii with
                 | Success (r,_,_) -> r
                 | Failure (e,_,_) -> failwith (sprintf "%A" e)
  let r = result s
  member p.ContentType = r.contentType
  member p.Boundary = r.boundary
  member p.ContentSubtype = r.subtype
  member p.Content = r.content

POST の例の最初の行は次のとおりです。

content-type: Multipart/related; boundary="RN-Http-Body-Boundary"; type="multipart/related"

ファイル内の 1 行にまたがります。コンテンツのさらにサブパートには、content-type複数行にわたる値が含まれているため、パーサーを再利用する場合は、パーサーを改良する必要があることはわかっています。

どういうわけか、ストリームの残りを適切な境界で分割し、投稿のコンテンツの複数の部分を返すことができるようpContentに、(文字列?) の結果を呼び出す必要があります。pBoundaryヘッダーとコンテンツ (明らかに文字列以外のものである必要があります) を含む投稿。私の頭がクラクラします。このコードはすでに複雑すぎて、1 行を解析できません。

洞察と知恵に感謝します!

4

1 に答える 1

2

これは、正しい方向に進むための断片です。

パーサーに同じ基本型の何かを吐き出させます。私は、この目的のために F# の識別共用体を使用することを好みます。本当に値を Post タイプにプッシュする必要がある場合は、返された AST ツリーをたどってください。それが私がそれにアプローチする方法です。

#if INTERACTIVE
#r"""..\..\FParsecCS.dll"""    // ... edit path as appropriate to bin/debug, etc.
#r"""..\..\FParsec.dll"""
#endif

let packet = @"content-type: Multipart/related; boundary=""RN-Http-Body-Boundary""; type=""multipart/related""

--RN-Http-Body-Boundary
Message-ID: <25845033.1160080657073.JavaMail.webmethods@exshaw>
Mime-Version: 1.0
Content-Type: multipart/related; type=""application/xml"";
  boundary=""----=_Part_235_11184805.1160080657052""

------=_Part_235_11184805.1160080657052
Content-Type: Application/XML
Content-Transfer-Encoding: binary
Content-Location: RN-Preamble
Content-ID: <1430586.1160080657050.JavaMail.webmethods@exshaw>"

//XML document begins here...

type AST =
| Document of AST list
| Header of AST list
/// ie. Content-Type is the tag, and it consists of a list of key value pairs
| Tag of string * AST list  
| KeyValue of string * string
| Body of string

上記の AST DU は、他の質問で投稿したサンプル データの最初のパスを表すことができます。それよりも細かくすることもできますが、通常は単純な方が優れています。つまり、あなたの例の最終的な目的地は Post タイプであり、単純なパターン マッチングでそれを達成できます。

于 2014-11-12T11:39:11.790 に答える