3

バックグラウンド

私は、開始点と閉鎖点ではなく、2 つの開始点を持つタグを一貫して含む多数の XML ファイルを継承しました。これらすべてのファイルをループして、不正な形式の XML を修正する必要があります。

これは、すべてのファイルでまったく同じタグである不適切な XML の簡単な例です。

<meals>
    <breakfast>
         Eggs and Toast
    </breakfast>
    <lunch>
         Salad and soup
    <lunch>
    <supper>
         Roast beef and potatoes
    </supper>
</meals>

<lunch>タグにはクロージャがないことに注意してください。これは、すべてのファイルで一貫しています。

質問

regexこれを修正するために C# を使用するのが最善でしょうか?

ファイル システムを繰り返し処理し、ドキュメントを XML または文字列オブジェクトに読み込む方法は既に知っているので、その部分に答える必要はありません。

ありがとう!

4

4 に答える 4

3

質問で示したように、壊れたXMLが比較的単純な場合は、いくつかの単純なロジックと基本的な正規表現を使用することができます。

    public static void Main(string[] args)
    {
        string broken = @"
<meals>
    <breakfast>
         Eggs and Toast
    </breakfast>
    <lunch>
         Salad and soup
    <lunch>
    <supper>
         Roast beef and potatoes
    </supper>
</meals>";

        var pattern1 = "(?<open><(?<tag>[a-z]+)>)([^<]+?)(\\k<open>)";
        var re1 = new Regex(pattern1, RegexOptions.Singleline);

        String work = broken;
        Match match = null;
        do
        {
            match = re1.Match(work);
            if (match.Success)
            {
                Console.WriteLine("Match at position {0}.", match.Index);
                var tag = match.Groups["tag"].ToString();

                Console.WriteLine("tag: {0}", tag.ToString());

                work = work.Substring(0, match.Index) +
                    match.Value.Substring(0, match.Value.Length - tag.Length -1) +
                    "/" +
                    work.Substring(match.Index + match.Value.Length - tag.Length -1);

                Console.WriteLine("fixed: {0}", work);
            }
        } while (match.Success);
    }

その正規表現は、.NET正規表現の「名前付き」キャプチャグループ機能を使用します。は?<open>、囲んでいる親によってキャプチャされたグループに「open」という名前でアクセスできることを示します。そのグループ化は、山形鋼を含む開始タグをキャプチャします。開始タグにxml属性がないことを前提としています。そのグループ内には、別の名前付きグループがあります。これは、「タグ」という名前を使用し、山かっこなしでタグ名自体をキャプチャします。

次に、正規表現は、間にある一連のテキスト()を遅延キャプチャし、(.+?)次に、後方参照で指定された別の「オープン」タグをキャプチャします。怠惰なキャプチャがそこにあるので、テキストに介在する可能性のあるオープンタグを丸呑みにすることはありません。

XMLは複数の改行にまたがる可能性があるため、が必要ですRegexOptions.Singleline

次に、ロジックはこの正規表現をループで適用し、一致したテキストを固定バージョン(有効なxmlと終了タグ)に置き換えます。固定XMLは、単純な文字列スライスで生成されます。

この正規表現は、次の場合には機能しません。

  • 開始タグにはXML属性があります
  • 奇妙な間隔があります-タグ名を囲む山かっこの間の空白
  • タグ名には、ダッシュや数字など、小文字のASCII文字以外のものが使用されます
  • 間にある文字列には山かっこが含まれます(CDATA内)

...しかし、このアプローチは引き続き機能します。少し調整する必要があります。

于 2012-04-06T03:57:43.203 に答える
2

状況が説明するのと同じくらい単純である場合(つまり、常に同じタグであり、常に1つしかない場合)、正規表現は少しやり過ぎだと思います。XMLファイルが比較的小さい場合(メガバイトではなくキロバイト)、すべてをメモリにロードし、文字列操作を使用して欠落しているスラッシュを挿入し、それを1日と呼ぶことができます。これは、正規表現を使用するよりもかなり効率的(高速)になります。ファイルが非常に大きい場合は、最初の<lunch>タグが見つかるまでファイルを1行ずつ読み取るように変更し、次のタグを探してそれに応じて変更することができます。開始するためのコードは次のとおりです。

var xml = File.ReadAllText( @"C:\Path\To\NaughtyXml.xml" );

var firstLunchIdx = xml.IndexOf( "<lunch>" );
var secondLunchIdx = xml.IndexOf( "<lunch>", firstLunchIdx+1 );

var correctedXml = xml.Substring( 0, secondLunchIdx + 1 ) + "/" +
xml.Substring( secondLunchIdx + 1 );

File.WriteAllText( @"C:\Path\To\CorrectedXml.xml", correctedXml );
于 2012-04-06T03:58:05.523 に答える
0

xml ファイル内の唯一の問題が表示されているものである場合は、Chesso の回答で十分です。実際、私のニーズの 80 ~ 90% が満たされていても、私はその方法を使用します。それ以外の場合は、手動で処理するか、特定の処理コードを記述することを選択できます。

ファイル構造が複雑で、説明したように単純ではない場合は、ファイルのコンテンツをトークンに分割できるテキストレクサーを検討する必要があると述べました。不規則性をチェックして修正するためのトークンのセマンティック分析はユーザーが行う必要がありますが、少なくともテキストの解析ははるかに簡単になります。C# での字句解析にリン​​クしている以下のいくつかのリソースを参照してください。

  1. http://blogs.msdn.com/b/drew/archive/2009/12/31/a-simple-lexer-in-c-that-uses-regular-expressions.aspx
  2. C# の貧乏人の「字句解析器」
  3. http://www.seclab.tuwien.ac.at/projects/cuplex/lex.htm
于 2012-04-06T04:08:06.723 に答える