(C#) 如何修改现有 XML 文件中的属性值而不加载或重写整个文件?

2024-01-30

我正在 XmlWriter 和 Linq2Xml 的帮助下制作一些巨大的 XML 文件(几个 GB)。 该文件的类型为:

<Table recCount="" recLength="">
<Rec recId="1">..</Rec>
<Rec recId="2">..</Rec>
..
<Rec recId="n">..</Rec>
</Table>

我不知道的价值观Table's recCount and 记录长度属性,直到我写完所有内部Rec节点,所以我必须在最后向这些属性写入值。

现在我正在写所有的内心Rec节点到临时文件,计算Table的属性值,并将所有内容按照我上面显示的方式写入结果文件。 (从临时文件中复制所有内容Rec nodes)

我想知道是否有一种方法可以修改这些属性的值,而无需将内容写入另一个文件(就像我现在所做的那样)或将整个文档加载到内存中(由于这些文件的大小,这显然是不可能的)?


大量注释的代码。基本思想是在第一遍中我们编写:

<?xml version="1.0" encoding="utf-8"?>
<Table recCount="$1" recLength="$2">
<!--Reserved space:++++++++++++++++-->
<Rec...

然后我们回到文件的开头并重写前三行:

<?xml version="1.0" encoding="utf-8"?>
<Table recCount="1000" recLength="150">
<!--Reserved space:#############-->

这里重要的“技巧”是您不能“插入”文件,只能覆盖它。所以我们为数字“保留”一些空间(Reserved space:#############.评论。我们可以有很多方法来做到这一点......例如,在第一遍中我们可以:

<Table recCount="              " recLength="          ">

然后(xml合法但丑陋):

<Table recCount="1000          " recLength="150       ">

或者我们可以附加空格after the >表的:

<Table recCount="" recLength="">                   

(有20个空格after the >)

Then:

<Table recCount="1000" recLength="150">            

(现在还有13个位子after the >)

或者我们可以简单地添加空格而不用<!-- -->在新线上...

代码:

int maxRecCountLength = 10; // int.MaxValue.ToString().Length
int maxRecLengthLength = 10; // int.MaxValue.ToString().Length
int tokenLength = 4; // 4 == $1 + $2, see below what $1 and $2 are
// Note that the reserved space will be in the form +++++++++++++++++++

string reservedSpace = new string('+', maxRecCountLength + maxRecLengthLength - tokenLength); 

// You have to manually open the FileStream
using (var fs = new FileStream("out.xml", FileMode.Create))

// and add a StreamWriter on top of it
using (var sw = new StreamWriter(fs, Encoding.UTF8, 4096, true))
{
    // Here you write on your StreamWriter however you want.
    // Note that recCount and recLength have a placeholder $1 and $2.
    int recCount = 0;
    int maxRecLength = 0;

    using (var xw = XmlWriter.Create(sw))
    {
        xw.WriteWhitespace("\r\n");
        xw.WriteStartElement("Table");
        xw.WriteAttributeString("recCount", "$1");
        xw.WriteAttributeString("recLength", "$2");

        // You have to add some white space that will be 
        // partially replaced by the recCount and recLength value
        xw.WriteWhitespace("\r\n");
        xw.WriteComment("Reserved space:" + reservedSpace);

        // <--------- BEGIN YOUR CODE
        for (int i = 0; i < 100; i++)
        {
            xw.WriteWhitespace("\r\n");
            xw.WriteStartElement("Rec");

            string str = string.Format("Some number: {0}", i);
            if (str.Length > maxRecLength)
            {
                maxRecLength = str.Length;
            }
            xw.WriteValue(str);

            recCount++;

            xw.WriteEndElement();
        }
        // <--------- END YOUR CODE

        xw.WriteWhitespace("\r\n");
        xw.WriteEndElement();
    }

    sw.Flush();

    // Now we read the first lines to modify them (normally we will
    // read three lines, the xml header, the <Table element and the
    // <-- Reserved space:
    fs.Position = 0;

    var lines = new List<string>();

    using (var sr = new StreamReader(fs, sw.Encoding, false, 4096, true))
    {
        while (true)
        {
            string str = sr.ReadLine();
            lines.Add(str);

            if (str.StartsWith("<Table"))
            {
                // We read the next line, the comment line
                str = sr.ReadLine();
                lines.Add(str);
                break;
            }
        }
    }

    string strCount = XmlConvert.ToString(recCount);
    string strMaxRecLength = XmlConvert.ToString(maxRecLength);

    // We do some replaces for the tokens
    int oldLen = lines[lines.Count - 2].Length;
    lines[lines.Count - 2] = lines[lines.Count - 2].Replace("=\"$1\"", string.Format("=\"{0}\"", strCount));
    lines[lines.Count - 2] = lines[lines.Count - 2].Replace("=\"$2\"", string.Format("=\"{0}\"", strMaxRecLength));
    int newLen = lines[lines.Count - 2].Length;

    // Remove spaces from reserved whitespace
    lines[lines.Count - 1] = lines[lines.Count - 1].Replace(":" + reservedSpace, ":" + new string('#', reservedSpace.Length - newLen + oldLen));

    // We move back to just after the UTF8/UTF16 preamble
    fs.Position = sw.Encoding.GetPreamble().Length;

    // And we rewrite the lines
    foreach (string str in lines)
    {
        sw.Write(str);
        sw.Write("\r\n");
    }
}

较慢的 .NET 3.5 方式

在 .NET 3.5 中StreamReader/StreamWriter想要关闭基地FileStream,所以我必须多次重新打开该文件。这有点慢。

int maxRecCountLength = 10; // int.MaxValue.ToString().Length
int maxRecLengthLength = 10; // int.MaxValue.ToString().Length
int tokenLength = 4; // 4 == $1 + $2, see below what $1 and $2 are
                        // Note that the reserved space will be in the form +++++++++++++++++++

string reservedSpace = new string('+', maxRecCountLength + maxRecLengthLength - tokenLength);
string fileName = "out.xml";

int recCount = 0;
int maxRecLength = 0;

using (var sw = new StreamWriter(fileName))
{
    // Here you write on your StreamWriter however you want.
    // Note that recCount and recLength have a placeholder $1 and $2.
    using (var xw = XmlWriter.Create(sw))
    {
        xw.WriteWhitespace("\r\n");
        xw.WriteStartElement("Table");
        xw.WriteAttributeString("recCount", "$1");
        xw.WriteAttributeString("recLength", "$2");

        // You have to add some white space that will be 
        // partially replaced by the recCount and recLength value
        xw.WriteWhitespace("\r\n");
        xw.WriteComment("Reserved space:" + reservedSpace);

        // <--------- BEGIN YOUR CODE
        for (int i = 0; i < 100; i++)
        {
            xw.WriteWhitespace("\r\n");
            xw.WriteStartElement("Rec");

            string str = string.Format("Some number: {0}", i);
            if (str.Length > maxRecLength)
            {
                maxRecLength = str.Length;
            }
            xw.WriteValue(str);

            recCount++;

            xw.WriteEndElement();
        }
        // <--------- END YOUR CODE

        xw.WriteWhitespace("\r\n");
        xw.WriteEndElement();
    }
}

var lines = new List<string>();

using (var sr = new StreamReader(fileName))
{
    // Now we read the first lines to modify them (normally we will
    // read three lines, the xml header, the <Table element and the
    // <-- Reserved space:

    while (true)
    {
        string str = sr.ReadLine();
        lines.Add(str);

        if (str.StartsWith("<Table"))
        {
            // We read the next line, the comment line
            str = sr.ReadLine();
            lines.Add(str);
            break;
        }
    }
}

// We have to use the Stream overload of StreamWriter because
// we want to modify the text!
using (var fs = File.OpenWrite(fileName))
using (var sw = new StreamWriter(fs))
{
    string strCount = XmlConvert.ToString(recCount);
    string strMaxRecLength = XmlConvert.ToString(maxRecLength);

    // We do some replaces for the tokens
    int oldLen = lines[lines.Count - 2].Length;
    lines[lines.Count - 2] = lines[lines.Count - 2].Replace("=\"$1\"", string.Format("=\"{0}\"", strCount));
    lines[lines.Count - 2] = lines[lines.Count - 2].Replace("=\"$2\"", string.Format("=\"{0}\"", strMaxRecLength));
    int newLen = lines[lines.Count - 2].Length;

    // Remove spaces from reserved whitespace
    lines[lines.Count - 1] = lines[lines.Count - 1].Replace(":" + reservedSpace, ":" + new string('#', reservedSpace.Length - newLen + oldLen));

    // We move back to just after the UTF8/UTF16 preamble
    sw.BaseStream.Position = sw.Encoding.GetPreamble().Length;

    // And we rewrite the lines
    foreach (string str in lines)
    {
        sw.Write(str);
        sw.Write("\r\n");
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(C#) 如何修改现有 XML 文件中的属性值而不加载或重写整个文件? 的相关文章

随机推荐

  • 当你是 git 中的原始仓库时,你如何进行本地拉取?

    我有一台服务器 我在其中设置了 Git 存储库 从我的客户那里 我可以执行 git拉原点 and git 推送原点 我的更改已正确推送 拉取到远程 Git 服务器 我还需要能够在服务器本身上签出项目 我没有使用初始化 裸露当我设置它时 因为
  • 如何在 Spark 2.0+ 中编写单元测试?

    我一直在尝试寻找一种合理的测试方法SparkSession使用 JUnit 测试框架 虽然似乎有很好的例子SparkContext 我不知道如何获得相应的示例SparkSession 即使它在内部的多个地方使用火花测试基地 https gi
  • AXIS-JAXB Unmarshal 不适用于除 jdk 1.8.077 之外的任何 JDK

    我已遵循以下程序 使用 WSDL Apache Axis 1 在 eclipse 中生成客户端文件 使用 JAXB 解组请求 XML 然后调用 Web 服务 如果我使用 JDK 1 8 077 则 XML 会成功解析 如果我使用任何其他 J
  • 在基于phonegap的应用程序上使用jspdf生成客户端pdf

    我尝试从本地数据生成pdf 我在使用 ArrayBuffer 和 Uint8Array 对象时遇到问题 解决方案是添加我在互联网上找到的 js 实现 现在这一行有一个错误 E Web Console 21515 Uncaught TypeE
  • 导入错误:没有名为 argparse 的模块

    我正在尝试运行 Python 程序 但出现错误 ImportError No module named argparse 我找到了问题 argparse cli 中的 Python 模块 https stackoverflow com qu
  • 在任何页面上设计注册表单

    我试图允许用户在我的主页 登陆页面上注册该网站 我已将设备注册表复制到我的登陆页面视图中 div br div div br div div div
  • Ruby,exec、system 和 %x() 或反引号之间的区别

    以下 Ruby 方法有什么区别 exec system and x or 反引号 我知道它们用于通过 Ruby 以编程方式执行终端命令 但我想知道为什么有三种不同的方法来执行此操作 system The system http www ru
  • dropzone.js 在没有 dropzone 的页面上给出错误“无效的 dropzone 元素”

    我正在使用 dropzone js 它在我需要 dropzone 的页面上运行得很好 在任何其他页面上 虽然它给了我一个 Invalid dropzone element 错误消息并导致我的其他 javascript 出现问题 我有一个自定
  • 区分 False 和 0

    假设我有一个包含不同值的列表 如下所示 1 2 3 b None False True 7 0 我想迭代它并检查每个元素是否不在某些禁止值列表中 例如 这个列表是 0 0 0 当我检查是否为 False 时 0 0 0 I get True
  • 为什么必须使用函数指针?

    什么情况下需要函数指针 标准答案似乎是回调 但为什么我们不能只传递一个函数呢 我正在阅读的关于 C 的书演示了将函数作为参数传递 并承认实际上编译器会将其转换为函数指针并传递它 因为函数不是实际对象 它显示了使用函数指针的等效代码 这稍微复
  • Ruby on Rails:默认情况下阻止选择列

    I have entries表与一个content可能包含大量文本的字段 在大多数情况下 我不需要访问该字段 因此每次从数据库加载大量未使用的数据 从 id 1 的条目中选择 似乎是对资源的巨大浪费 我如何指定default scope 除
  • 从 bash 输出中排除一个字符串

    我现在正在做一个项目 在这个项目中 由于某些原因 我需要从与模式匹配的输出 或文件 中排除第一个字符串 困难在于我只需要排除一个字符串 即流中的第一个字符串 例如 如果我有 1 abc 2 qwerty 3 open 4 abc 5 tal
  • 我怎样才能得到下面图片的黑白图像?

    我想将图片准确地转换为黑白图像 其中种子将由白色表示 背景为黑色 我想把它放在 python opencv 代码中 请帮帮我 I got good result for the above picture using the given c
  • Swift:UIDocumentInteractionController 不起作用?

    UIDocumentInteractionController 不适用于具有多个页面的大型 pdf 文件 在我的代码中 var docController UIDocumentInteractionController DispatchQu
  • 如何让文本区域占据 div 中的剩余高度?

    我有一组代码如下 要点是在 div 中放置一组图像 然后用文本区域填充 div 的其余部分 如果我设置 height 100 它将使其高度相同 这不是 div height images height 并使文本区域更长 w3c 上说的是in
  • 如何在不写入主目录的情况下运行 podman 和 buildah?

    我的主目录中几乎没有剩余磁盘空间 但是 我的目录中有很多磁盘空间 scratch tmp实验 该目录现在是空的 我想尝试一下命令podman and buildah 只是为了实验和学习 实验结束后我想删除该目录 scratch tmp实验
  • 如何在电报机器人中获得身份验证?

    Telegram 机器人现已准备就绪 如果我们使用网络浏览器和网站进行类比 那么电报客户端应用程序就像浏览器客户端 Telegram 聊天室就像网站 假设我们有一些信息 我们只想限制某些用户 在网站上 我们将进行身份验证 我们如何在 Tel
  • Unity3D 构建后加载资源

    我有很多组图像 PNG 它们放置在Resources项目的 Assets 文件夹中 使用编辑器时 我可以毫无问题地从不同的子文件夹加载图像 只需简单地使用Resources Load 命令并提供我尝试加载的特定图像的路径 例如 firstL
  • java中数组的所有可能的组合和子集

    给定一个可变维度的数组 例如 数组 1 2 4 5 我需要一种方法来概括数组的所有可能组合和子集 给定一个包含 n 个元素的数组 我需要拥有所有子集 1 个元素的所有子集 2 个元素的所有子集 n 个元素的所有子集 以及每个子集的所有可能排
  • (C#) 如何修改现有 XML 文件中的属性值而不加载或重写整个文件?

    我正在 XmlWriter 和 Linq2Xml 的帮助下制作一些巨大的 XML 文件 几个 GB 该文件的类型为 table table