簡體   English   中英

使用StreamReader讀取文件時讀取固定數量的字節

[英]Read a fixed number of bytes when reading file using StreamReader

我正在使用Mozilla字符集檢測器的端口來確定文件的編碼,然后使用該編碼來構造StreamReader。 到現在為止還挺好。

但是,我正在讀取的文件格式是一種奇怪的格式,因此有時需要跳過一些字節。 就是說,以其他方式編碼的文本文件將在其中嵌入一些原始字節。

我想以文本形式讀取流,直到擊中一些指示字節流跟隨的文本為止,然后我想讀取字節流,然后繼續以文本形式讀取。 做到這一點的最佳方法是什么(簡單性和性能之間的平衡)?

我不能依靠針對StreamReader的FileStream進行搜索(然后丟棄后者中的緩沖數據),因為我不知道到目前為止讀取字符時使用了多少字節。 我可能會放棄使用StreamReader並切換到使用字節和字符的並行數組的定制類,使用解碼器從前者填充字符和字符,並在每次字符讀取時通過使用編碼來跟蹤字節數組中的位置,以進行計算用於字符的字節數。 育。

為了進一步說明,文件具有以下格式:

[編碼字符] [嵌入式字節指示器+ len] [len字節] [編碼字符] ...

在一個零或一個或多個嵌入的字節塊為零且嵌入的char塊可以為任意長度的情況下。

因此,例如:

ABC:123:DEF:456:$ 0099 [0x00,0x01,0x02,... x 99] GHI:789:JKL:...

沒有行定界符。 我可能有由某些字符(在這種情況下為冒號)分隔的任意多個字段(ABC,123,...)。 這些字段可能在各種代碼頁中,包括UTF-8(不保證為單字節)。 當我命中$時,我知道接下來的4個字節包含一個長度(稱為n),接下來的n個字節將被原始讀取,而字節n + 1將是另一個文本字段(GHI)。

概念證明。 此類適用於UTF-16字符串數據和每個OP中的':'分隔符。 它期望二進制長度是一個4字節的低端二進制整數。 應該很容易地將其調整為(奇數)文件格式的更具體的細節。 例如,任何Decoder類都應放入ReadString()並“正常工作”。

要使用它,請使用Stream類構造它。 對於每個單獨的數據元素,調用ReportNextData(),它將告訴您下一個什么樣的數據,然后調用適當的Read *()方法。 對於二進制數據,請調用ReadBinaryLength(),然后調用ReadBinaryData()。

注意,ReadBinaryData()遵循流協定; 它不能保證返回所需的字節數,因此您可能需要多次調用它。 但是,如果您請求太多字節,它將拋出EndOfStreamException。

我使用此數據(十六進制格式)對其進行了測試:410042004300240A0000000102030405060708090024050000000504030201580059005A003A310032003300

這是:ABC $ [10] [1234567890] $ [5] [54321] XYZ:123

像這樣掃描數據:

OddFileReader.NextData nextData;

while ((nextData = reader.ReportNextData()) != OddFileReader.NextData.Eof)
{
    // Call appropriate Read*() here.
}

public class OddFileReader : IDisposable
{
    public enum NextData
    {
        Unknown,
        Eof,
        String,
        BinaryLength,
        BinaryData
    }

    private Stream source;
    private byte[] byteBuffer;
    private int bufferOffset;
    private int bufferEnd;
    private NextData nextData;
    private int binaryOffset;
    private int binaryEnd;
    private char[] characterBuffer;

    public OddFileReader(Stream source)
    {
        this.source = source;
    }

    public NextData ReportNextData()
    {
        if (nextData != NextData.Unknown)
        {
            return nextData;
        }

        if (!PopulateBufferIfNeeded(1))
        {
            return (nextData = NextData.Eof);
        }

        if (byteBuffer[bufferOffset] == '$')
        {
            return (nextData = NextData.BinaryLength);
        }
        else
        {
            return (nextData = NextData.String);
        }
    }

    public string ReadString()
    {
        ReportNextData();

        if (nextData == NextData.Eof)
        {
            throw new EndOfStreamException();
        }
        else if (nextData != NextData.String)
        {
            throw new InvalidOperationException("Attempt to read non-string data as string");
        }

        if (characterBuffer == null)
        {
            characterBuffer = new char[1];
        }

        StringBuilder stringBuilder = new StringBuilder();
        Decoder decoder = Encoding.Unicode.GetDecoder();

        while (nextData == NextData.String)
        {
            byte b = byteBuffer[bufferOffset];

            if (b == '$')
            {
                nextData = NextData.BinaryLength;

                break;
            }
            else if (b == ':')
            {
                nextData = NextData.Unknown;
                bufferOffset++;

                break;
            }
            else
            {
                if (decoder.GetChars(byteBuffer, bufferOffset++, 1, characterBuffer, 0) == 1)
                {
                    stringBuilder.Append(characterBuffer[0]);
                }

                if (bufferOffset == bufferEnd && !PopulateBufferIfNeeded(1))
                {
                    nextData = NextData.Eof;

                    break;
                }
            }
        }

        return stringBuilder.ToString();
    }

    public int ReadBinaryLength()
    {
        ReportNextData();

        if (nextData == NextData.Eof)
        {
            throw new EndOfStreamException();
        }
        else if (nextData != NextData.BinaryLength)
        {
            throw new InvalidOperationException("Attempt to read non-binary-length data as binary length");
        }

        bufferOffset++;

        if (!PopulateBufferIfNeeded(sizeof(Int32)))
        {
            nextData = NextData.Eof;

            throw new EndOfStreamException();
        }

        binaryEnd = BitConverter.ToInt32(byteBuffer, bufferOffset);
        binaryOffset = 0;
        bufferOffset += sizeof(Int32);
        nextData = NextData.BinaryData;

        return binaryEnd;
    }

    public int ReadBinaryData(byte[] buffer, int offset, int count)
    {
        ReportNextData();

        if (nextData == NextData.Eof)
        {
            throw new EndOfStreamException();
        }
        else if (nextData != NextData.BinaryData)
        {
            throw new InvalidOperationException("Attempt to read non-binary data as binary data");
        }

        if (count > binaryEnd - binaryOffset)
        {
            throw new EndOfStreamException();
        }

        int bytesRead;

        if (bufferOffset < bufferEnd)
        {
            bytesRead = Math.Min(count, bufferEnd - bufferOffset);

            Array.Copy(byteBuffer, bufferOffset, buffer, offset, bytesRead);
            bufferOffset += bytesRead;
        }
        else if (count < byteBuffer.Length)
        {
            if (!PopulateBufferIfNeeded(1))
            {
                throw new EndOfStreamException();
            }

            bytesRead = Math.Min(count, bufferEnd - bufferOffset);

            Array.Copy(byteBuffer, bufferOffset, buffer, offset, bytesRead);
            bufferOffset += bytesRead;
        }
        else
        {
            bytesRead = source.Read(buffer, offset, count);
        }

        binaryOffset += bytesRead;

        if (binaryOffset == binaryEnd)
        {
            nextData = NextData.Unknown;
        }

        return bytesRead;
    }

    private bool PopulateBufferIfNeeded(int minimumBytes)
    {
        if (byteBuffer == null)
        {
            byteBuffer = new byte[8192];
        }

        if (bufferEnd - bufferOffset < minimumBytes)
        {
            int shiftCount = bufferEnd - bufferOffset;

            if (shiftCount > 0)
            {
                Array.Copy(byteBuffer, bufferOffset, byteBuffer, 0, shiftCount);
            }

            bufferOffset = 0;
            bufferEnd = shiftCount;

            while (bufferEnd - bufferOffset < minimumBytes)
            {
                int bytesRead = source.Read(byteBuffer, bufferEnd, byteBuffer.Length - bufferEnd);

                if (bytesRead == 0)
                {
                    return false;
                }

                bufferEnd += bytesRead;
            }
        }

        return true;
    }

    public void Dispose()
    {
        Stream source = this.source;

        this.source = null;

        if (source != null)
        {
            source.Dispose();
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM