繁体   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