単純なXMLパーサーを最初から作成/作成するにはどうすればよいですか?
コードサンプルではなく、英語の簡略化された基本的な手順を知りたいのですが。
優れたパーサーはどのように設計されていますか?正規表現をパーサーで使用するべきではないことは理解していますが、XMLの解析における正規表現の役割はどのくらいですか?
使用する推奨データ構造は何ですか?リンクリストを使用して、ノード、属性、および値を格納および取得する必要がありますか?
Dプログラミング言語でXMLパーサーを作成できるように、XMLパーサーの作成方法を学びたいです。
パーサーの書き方がわからない場合は、読む必要があります。コンパイラー作成に関する本(AhoやUllmannなど、30年または40年前に作成された最高の本の多く)を入手して、字句解析と構文解析の章を調べてください。 XMLは基本的に違いはありませんが、語彙フェーズと文法フェーズが一部の言語ほど明確に分離されていない点が異なります。
警告の一言ですが、完全に準拠したXMLパーサーを作成する場合、ほとんどのXMLユーザーではないパラメーターエンティティなどを処理する仕様のあいまいなコーナーでEdgeケースを正しく取得するために90%の労力が費やされます。に気づいています。
パーサーとノードリストには違いがあります。パーサーは、プレーンテキストのXMLを大量に取得し、そこにあるノードを判別しようとする部分です。次に、ノードを保存する内部構造があります。その構造の上のレイヤーに、DOMであるドキュメントオブジェクトモデルがあります。これは、XMLドキュメントを構成するネストされたノードの構造です。パーサーは、ノードを作成するための汎用DOMインターフェースを知っているだけで十分です。
私はこのためのパーサーとして正規表現を使用しません。最良のことは、文字列charをcharごとにトラバースし、取得したものが取得すべきものと一致するかどうかを確認することだと思います。
しかし、既存のXMLパーサーを使用してみませんか?データのエンコードには多くの可能性があります。多くの例外。そして、パーサーがそれらすべてを管理しない場合、XMLパーサーのタイトルの価値はほとんどありません。
イベントベースのパーサーの場合、ユーザーはいくつかの関数(startNode(name,attrs)
、endNode(name)
およびsomeText(txt)
をインターフェース経由で渡す可能性があります)を渡し、必要に応じてそれらを呼び出す必要があります。ファイルの上
パーサーにはwhileループがあり、<
までの読み取りと>
までの読み取りを交互に繰り返し、パラメータータイプへの適切な変換を行います。
void parse(EventParser p, File file){
string str;
while((str = file.readln('<')).length !=0){
//not using a rewritable buffer to take advantage of slicing
//but it's a quick conversion to a implementation with a rewritable buffer though
if(str.length>1)p.someText(str.chomp('<'));
str = file.readln('>');
str = str.chomp('>');
//split str in name and attrs
auto parts = str.split();
string name = parts[0];
string[string] attrs;
foreach(attribute;parts[1..$]){
auto splitAtrr = attribute.split("=");
attrs[splitAtrr[0]] = splitAtrr[1];
}
if(str[0] == '/')p.endNode(name);
else {
p.startNode(name,attrs);
if(str[str.length-1]=='/')p.endNode(name);//self closing tag
}
}
}
イベントベースのパーサーの上にDOMパーサーを構築でき、各ノードに必要な基本機能はgetChildrenおよびgetParent getNameおよびgetAttributesです(構築時のセッター付き;))
上記のメソッドを持つdomパーサーのオブジェクト:
class DOMEventParser : EventParser{
DOMNode current = new RootNode();
overrides void startNode(string name,string[string] attrs){
DOMNode tmp = new ElementNode(current,name,attrs);
current.appendChild(tmp);
current = tmp;
}
overrides void endNode(string name){
asser(name == current.name);
current = current.parent;
}
overrides void someText(string txt){
current.appendChild(new TextNode(txt));
}
}
解析が終了すると、ルートノードはDOMツリーのルートになります
注:xmlの正確さを保証するために検証コードをそこに入れませんでした
編集:属性の解析にはバグがあり、空白で分割するのではなく、正規表現を使用することをお勧めします
パーサーは、入力言語のニーズに適合する必要があります。あなたの場合、単純なXMLです。 XMLについて最初に知っておくべきことは、XMLはコンテキストフリーであり、あいまいさはなく、すべてが2つのトークンで囲まれていることです。これがXMLを有名にする理由です。つまり、解析が簡単です。最後に、XMLは常にツリー構造で単純に表されます。前述のように、XMLを解析してその間にコードを実行するか、XMLを解析してツリーを生成し、このツリーに従ってコードを実行することができます。
Dは、XMLパーサーを非常に簡単に作成するための非常に興味深い方法を提供します。次に例を示します。
doc.onStartTag["pointlight"] = (ElementParser xml)
{
debug writefln("Parsing pointlight element");
auto l = new DistantLight(to!int(xml.tag.attr["x"]),
to!int(xml.tag.attr["y"]),
to!int(xml.tag.attr["z"]),
to!ubyte(xml.tag.attr["red"]),
to!ubyte(xml.tag.attr["green"]),
to!ubyte(xml.tag.attr["blue"]));
lights ~= l;
xml.parse();
};
DはJavaとかなり密接に関連しているので、おそらく [〜#〜] antlr [〜#〜] でXMLパーサーを生成します(おそらくXMLが存在するため [〜#〜] ebnf [〜#〜] ANTLRの文法はすでに使用されているため、これらを使用することができます)。次に、生成されたJavaパーサーコードをDに変換することもできます。少なくとも、あなたは出発点であり、それからあなたは特にDのためにコードを最適化することを試みることにいくらかの努力をすることができます...
少なくともANTLRは、多くの人が考えているほど難しくはありません。私はそれについて何も知らなかった後、 ANTLRのこの素晴らしいスクリーンキャストのセット の3-4を見ることから始めました。
ところで、私は ANTLRWorks を操作するのが簡単です(スクリーンキャストで使用されるEclipseプラグインとは対照的ですが、スクリーンキャストのコンテンツが適用されます)。
私の0.02cだけです。
ドキュメントの最初の要素はプロローグでなければなりません。これは、xmlバージョン、エンコーディング、ファイルがスタンドアロンであるかどうか、そしておそらく他のいくつかのものを示します。プロローグは<?
で始まります。
プロローグの後に、メタデータ付きのタグがあります。コメント、Doctype、要素定義などの特別なタグは、<!
で始まる必要があります。処理命令は<?
で始まります。 <!DOCTYPE
タグは、dtdスタイルのxmlドキュメントで<!ELEMENT
および<!ATTLIST
タグを持つことができるため、ここにタグをネストすることができます。詳細は Wikipedia を参照してください。徹底的な例。
トップレベルの要素は1つだけである必要があります。 <!
または<?
が前に付いていないのはこれだけです。トップレベル要素の後にメタデータタグがさらにある場合があります。それらを最初に処理します。
明示的な解析の場合:最初にタグを特定します。すべてのタグは<
で始まります。次に、タグの種類とそのクロージャがどのように見えるかを決定します。 <!--
はコメントタグであり、末尾以外の場所に--
を含めることはできません。 <?
は?>
で終わります。 <!
は>
で終わります。繰り返します:<!DOCTYPE
は、そのクロージャの前にタグをネストできます。また、知らないタグがネストされている場合もあります。
タグを見つけたら、その終了タグを見つけたいと思うでしょう。タグが最初に自己終了するかどうかを確認してください。それ以外の場合は、その閉鎖を見つけます。
データ構造の場合:私は、各要素がノードであり、各ノードがインデックス付き/マップされたサブ要素のリストを持つツリー構造をお勧めします。
明らかに、完全なパーサーにはさらに多くの調査が必要になります。これであなたが始めるのに十分だと思います。