簡體   English   中英

如何從C#中的混合xml /二進制文件的標頭讀取XML數據

[英]How to read XML data from the header of a mixed xml/binary file in C#

我的任務是為具有以下規范的文件格式編寫閱讀器:

  1. 第一部分是帶有元數據的純XML(utf-8);
  2. 最后一部分是16位值的流(二進制)。
  3. 這兩個部分由一個值29字節分隔(ASCII表中的組分隔符)。

我看到兩種讀取文件xml部分的方法。 第一個是逐字節構建字符串,直到找到分隔符。

另一種是使用一些庫來解析xml並自動檢測格式正確的xml的結尾。

問題是:在XML中最后一個結束標記之后,是否有任何.NET庫會自動停止?

(或者,有人可以建議一種更明智的方式來讀取這種文件格式嗎?)


更新:根據彼得·杜尼奧(Peter Duniho)的回答,進行了一些細微修改,我最終做到了這一點(盡管沒有經過完全的單元測試,但它仍然有效)。

        int position = 0;
        MemoryStream ms;

        using (FileStream fs = File.OpenRead("file.xml"))
        using (ms = new MemoryStream())
        {
            int current;
            while ((current = fs.ReadByte()) > 0)
            {
                position++;

                if (current == 29)
                    break;

                ms.WriteByte((byte)current);
            }
        }

        var xmlheader = new XmlDocument();
        xmlheader.LoadXml(Encoding.UTF8.GetString(ms.ToArray()));

雖然“讀到結束標簽”聽起來很吸引人,但是您需要有一個解析器,該解析器最終不會緩沖所有數據。

我會將所有數據讀取到byte[] ,然后在其中搜索分隔符-然后可以將二進制數據拆分為兩個,並適當地解析每個部分。 我將完全以二進制形式工作,而不涉及任何字符串-您可以使用new MemoryStrem(byte[], int, int)為每個部分創建一個MemoryStream ,然后將其傳遞給XML解析器以及最終的解析器。 這樣,您無需擔心處理UTF-8,也不必擔心XML的更高版本是否不使用UTF-8,等等。

所以像這樣:

byte[] allData = File.ReadAllBytes(filename);
int separatorIndex = Array.IndexOf(allData, (byte) 29);
if (separatorIndex == -1)
{
    // throw an exception or whatever
}
var xmlStream = new MemoryStream(allData, 0, separatorIndex);
var lastPartStream = new MemoryStream(
      allData, separatorIndex + 1, allData.Length - separatorIndex - 1);

根據您提供的信息,僅搜索具有值29的字節就可以了,因為XML是UTF8,只有文件中存在字符代碼點29的情況下,才出現值29的字節。 現在,我猜它可能存在,但這會令人驚訝,因為它在ASCII值的控制字符范圍內。

根據XML 1.0規范:

字符:: =#x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] / *任何Unicode字符,但代理塊FFFE和FFFF除外。 * /

盡管注釋暗示29在XML文件中將是有效的代碼點(因為它本身是有效的Unicode字符),但我認為實際的語法是規范性的。 也就是說,它特別排除了代碼點32以下的字符,但制表符,換行符和回車符除外,因此29 不是有效的XML字符(就像Jon Skeet所說的那樣)。

就是說,如果沒有完整的輸入說明,就不能排除這種可能性。 因此,如果您真的想安全起見,則必須繼續解析XML,以期為根元素找到合適的結束標記。 然后,您可以搜索字節29(因為在結束標記后可能會有空白),以標識二進制數據的起始位置。

(注意:索要一個庫是“題外話”。但是,您可能可以使用XmlReader來執行此操作,因為它是迭代運行的;即,您可以在達到最終關閉標記之后以及之前終止其操作。它開始抱怨發現無效的XML,但這取決於XmlReader可能進行的緩沖;如果它緩沖了結束標記之后的其他數據,則基礎流的位置將超過29個字節,這使查找變得更加困難坦白地說,僅搜索29字節似乎是一種方法。

您可以像這樣在標頭中搜索29個字節(警告:瀏覽器代碼...未編譯,未經測試):

MemoryStream xmlStream = new MemoryStream();

using (FileStream stream = File.OpenRead(path))
{
    int offset = 0, bytesRead = 0;

    // arbitrary size...whatever you think is reasonable would be fine
    byte[] buffer = new byte[1024];

    while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
    {
        bool found = false;

        for (int i = 0; i < bytesRead; i++)
        {
            if (buffer[i] == 29)
            {
                offset += i;
                found = true;
                xmlStream.Write(buffer, 0, i - 1);
                break;
            }
        }

        if (found)
        {
            break;
        }

        offset += bytesRead;
        xmlStream.Write(buffer, 0, bytesRead);
    }

    if (bytesRead > 0)
    {
        // found byte 29 at offset "offset"

        xmlStream.Position = 0;

        // pass "xmlStream" object to your preferred XML-parsing API to
        // parse the XML, or just return it or "xmlStream.ToArray()" as
        // appropriate to the caller to let the caller deal with it.
    }
    else
    {
        // byte 29 not found!
    }
}

編輯:

我已經更新了上面的代碼示例,將其寫入MemoryStream對象,以便一旦找到字節29的值,就可以准備進行XML解析的所有流。 當然,如果您確實需要,我敢肯定您可以自己添加。 無論如何,很明顯,無論有無此功能,您都可以修改代碼以適合您的需求。

(搜索時寫入MemoryStream存在明顯的危險:如果您未找到字節29的值,則會在內存中看到整個文件的完整副本,建議您這樣做寧願避免。但是考慮到這是錯誤的情況,那可能沒問題)。

暫無
暫無

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

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