0

多くの助けを借りて、次のコードを作成しました。

if (ofd.ShowDialog() == DialogResult.OK)
{
    using (StreamReader reader = new StreamReader(ofd.FileName, Encoding.GetEncoding("ISO-8859-1")))
    {
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.ConformanceLevel = ConformanceLevel.Fragment;
        using (XmlReader xreader = XmlReader.Create(reader, settings))
        {
            while (xreader.Read())
            {
                DoSomething();
            }
        }
    }

1 つだけ問題が残っています。それは、「xml ドキュメント」のいくつかの場所にアンパサンドがあることです。

<name>Me & You</name>

これは有効な XML フラグメントではなく、& でアンパサンドをエスケープする必要があることはわかっていますが、私のシナリオではドキュメントを変更することはできません。ドキュメントを変更せずに XML でこれを読み取る方法はありますか? StreamReader を変更して何か?

4

1 に答える 1

1

メソッドを継承しTextReaderてオーバーライドし、ReadLine必要に応じて行ごとに文字を置き換えます。新しいストリーム リーダーを に渡しますXmlReader。無効なエンティティは表示されません。

編集:XmlReaderをサポートしTextReaderていますが、メソッドを呼び出していないようですReadLine...代わりに、Stream直接サブクラス化してbyte[] buffer.

編集:以下は、一般的なストリームをフィルタリングし、アンパサンドをエンコードされたエンティティに置き換える必要があると私が信じているものです。ストリーム バイトで動作するため、エンコーディングの概念がなく、ASCII を前提としています。現時点ではアンパサンドのみを処理しますが、他のエンティティも同様にエンコードするのは簡単です。2.5 Gb ファイルで試してみたところ、処理時間は約 3% しか増加せず、メモリ負荷は影響を受けていないように見えました。そうは言っても、それは確かに十分にテストされていません。生成されるファイルが、さまざまなバッファー サイズの入力ファイルと同一であることを確認することを強くお勧めします。例えば:

using (var output = File.Open(@"out.xml", FileMode.Create))
using (var input = new AmpersandFilterText(File.Open(@"in.xml", FileMode.Open))
    input.CopyTo(output);

次に、ファイルのバイナリ比較を行います。

編集 #3: のメソッド シグネチャを読み違えてTextReaderXmlReaderを呼び出すだけRead(char[], int, int)です。とにかく、文字エンコーディングに依存しない実装については以下を参照してください。

class TestProgram
{
    static void Main(string[] args)
    {
        var stringReader = new MemoryStream(Encoding.ASCII.GetBytes("<name>Me & You</name>"));
        var textReader = new AmpersandFilterText(stringReader);

        XmlReaderSettings settings = new XmlReaderSettings();
        settings.ConformanceLevel = ConformanceLevel.Fragment;
        using (XmlReader xreader = XmlReader.Create(textReader, settings))
        {
            while (xreader.Read())
            {
                Console.WriteLine(xreader.Value);
            }
        }

        Console.ReadLine();
    }
}

class AmpersandFilterText : StreamReader
{
    public AmpersandFilterText(Stream stream)
        : base(stream)
    {
    }

    private StringBuilder sbBuffer = null;
    private int bufferOffset = 0;

    public override string ReadLine()
    {
        var rawLine = base.ReadLine();
        rawLine = rawLine.Replace("&", "&amp;");
        return rawLine;
    }

    public override int Read()
    {
        char cChar;
        if (sbBuffer != null)
        {
            cChar = sbBuffer[bufferOffset];
            bufferOffset++;
            evalBuffer();
            return cChar;
        }
        else
        {
            cChar = (char)base.Read();
            if (cChar == '&')
                sbBuffer = new StringBuilder("amp;");
            return cChar;
        }
    }

    public override int Read(char[] buffer, int index, int count)
    {
        int destOffset = 0;
        const string replacement = "&amp;";

        //exhaust our buffer first
        if (sbBuffer != null)
        {
            int bufferedToConsume = Math.Min(count, sbBuffer.Length - bufferOffset);
            sbBuffer.CopyTo(bufferOffset, buffer, index, bufferedToConsume);
            destOffset += bufferedToConsume;
            bufferOffset += bufferedToConsume;
            evalBuffer();
        }

        //then, if needed, read more data
        if (destOffset < count)
        {
            char[] newBuffer = new char[count - destOffset];
            var newRead = base.Read(newBuffer, 0, newBuffer.Length);

            //process new data and return directly
            int sourceOffset = 0;
            while (sourceOffset < newRead && destOffset < count)
            {
                char tChar = newBuffer[sourceOffset];
                if (tChar == '&')
                {
                    int replacementOffset = 0;
                    while (replacementOffset < replacement.Length && destOffset < count)
                    {
                        buffer[destOffset + index] = replacement[replacementOffset];
                        destOffset++;
                        replacementOffset++;
                    }

                    sourceOffset++;

                    //we did not copy the entire replacement
                    if (replacementOffset < replacement.Length)
                    {
                        //put the remainder in next time
                        sbBuffer = new StringBuilder();
                        sbBuffer.Append(replacement, replacementOffset, replacement.Length - replacementOffset);
                    }
                }
                else
                {
                    buffer[destOffset + index] = tChar;
                    destOffset++;
                    sourceOffset++;
                }
            }

            //we have unused data
            if (sourceOffset < newRead)
            {
                if (sbBuffer == null)
                    sbBuffer = new StringBuilder();

                //add data after replace
                for (; sourceOffset < newRead; sourceOffset++)
                {
                    char tChar = newBuffer[sourceOffset];
                    if (tChar == '&')
                        sbBuffer.Append(replacement);
                    else
                        sbBuffer.Append(tChar);
                }
            }
        }

        return destOffset;
    }

    public override int ReadBlock(char[] buffer, int index, int count)
    {
        return this.Read(buffer, index, count);
    }

    public override int Peek()
    {
        if (sbBuffer != null)
            return sbBuffer[bufferOffset];

        return base.Peek();
    }

    public override string ReadToEnd()
    {
        string cLine;
        StringBuilder sbTemp = new StringBuilder();
        while ((cLine = this.ReadLine()) != null)
            sbTemp.AppendLine(cLine);
        return sbTemp.ToString();
    }

    private void evalBuffer()
    {
        if (bufferOffset == sbBuffer.Length)
        {
            //TODO: check perf on sbBuffer.Clear
            sbBuffer = null;
            bufferOffset = 0;
        }
    }
}
于 2013-02-23T19:47:22.883 に答える