【问题标题】:Split String by XML elements按 XML 元素拆分字符串
【发布时间】:2015-10-16 11:57:40
【问题描述】:

我有以下来自 10MB 文本文件的输入字符串

string data = "0x52341\n0x52341<?xml version=\"1.0\" encoding=\"UTF-8\"?><element1 value=\"3\"><sub>1</sub></element1>0x52341\n0x52341 <element1><sub><element>2</element></sub></element1>0x52341<element2><sub>3</sub‌​‌​></element2> <element2><sub>4</sub></element2>0x4312";

现在我想要 element1element2 XML 节点的这个字符串

这种情况下的结果应该是

output[0] = "<element1 value="3"><sub>1</sub></element1>";
output[1] = "<element1><sub><element>2</element></sub></element1>";
output[2] = "<element2><sub>3</sub></element2>";
output[3] = "<element2><sub>4</sub></element2>";

我的努力:

我已经尝试过正则表达式,但是对于那个大文件来说速度很慢,我也尝试过

string[] output= input.Split(new string[] { "<element1>", "<element2>" }, StringSplitOptions.None);

string.Split() 是迂回的,因为它会抛出内存不足异常,并且分隔符在拆分时被删除。

问题: 有没有一种简单的方法可以从我的文本文件中解析出这些 xml 元素?

更新: 我简化了我的文件,因为我无法在 SO 中发布整个 10MB 文件 - 有时 xml 元素之间有 0x1234 值,有时没有

【问题讨论】:

  • 我了解您使用 c#,您有很多东西要处理 html 解析:selenium、.Net htmlagilitypack、mshtml 为什么不将它们用于此目的?
  • 如果您正在处理 XML,请使用像 Linq-to-XML 这样的 XML 解析器。
  • @juharr - 这不起作用,已经尝试过这种方法。我没有一个根,我有很多根(element1element2
  • @kenny - 不是整个文件,但 element1element2 是有效的 xml

标签: c# string parsing split


【解决方案1】:

如果您可以保证每个&lt;elementX&gt;&lt;/elementX&gt; 片段都是格式良好的XML 节点(可以这么说),请将整个字符串包装在&lt;elements&gt; ... &lt;/elements&gt; 中并使用标准.NET 方法处理它,无论是XmlDocument、Linq to XML 还是其他适合你的东西。

【讨论】:

  • @Byyo 究竟在哪里抛出什么?标签之间有文本不会使 XML 格式错误。
  • @Byyo 您可以逐块解析文本(4096 个字符),并删除这些字符。
  • XDocument xDoc = XDocument.Parse() throws unexpected xml declaration. the xml declaration must be the first node in the document
  • XmlDocument x = new XmlDocument(); x.LoadXml(file); 抛出完全相同的错误 - 刚刚测试过
  • @Byyo 将file 中的任何内容逐字粘贴到此处。
【解决方案2】:

编辑: 一个更快的替代方案(因为它不使用正则表达式)不替换元素内容中的 0x... 片段将是以下一个:

string data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>0x52341<element1 value=\"3\"><sub>1</sub></element1>0x234512 <element1><sub><element>2</element></sub></element1>0x52341<element2><sub>3</sub></element2> <element2><sub>4</sub></element2>0x4312";

XmlReaderSettings xrs = new XmlReaderSettings();
xrs.ConformanceLevel = ConformanceLevel.Fragment;
XDocument doc = new XDocument(new XElement("root"));
XElement root = doc.Descendants().First();

using(var ms = new StreamWriter(new MemoryStream()))
{
    ms.Write(data);
    ms.Flush();
    ms.BaseStream.Position = 0;
    using (StreamReader fs = new StreamReader(ms.BaseStream))
    //using (StreamReader fs = new StreamReader("file.xml"))
    {
        using (XmlReader rdr = XmlReader.Create(fs, xrs))
        {
            while (rdr.Read())
            {
                if (rdr.NodeType == XmlNodeType.Element)
                {
                    root.Add(XElement.Load(rdr.ReadSubtree()));
                }
            }
        }
    }
}

您也可以使用另一个 StreamReader 构造函数直接从文件中读取(删除 StreamWriter 部分)

【讨论】:

  • 检查我的编辑 --> 不使用正则表达式应该更快,并且不会替换 elementx 中的内容
  • 以下异常:意外的 XML 声明。 XML 声明必须是文档中的第一个节点,并且它之前不允许出现空白字符。
  • 您必须删除“
  • 我认为错误是因为它是 &lt;root&gt;&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt; 而不是 &lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;&lt;root&gt; - 删除第一个空格没有帮助
  • 单独使用第二个示例 --> 不要使用编辑前的代码。 (仅第一行示例数据创建)--> 我编辑了代码,现在它应该可以工作了
【解决方案3】:

这是一个可以做到这一点的控制台应用程序:

class Program
{
    static void Main(string[] args)
    {
        string source = "0x52341<element1 value=\"3\"><sub>1</sub></element1>0x234512 <element1><sub><element>2</element></sub></element1>0x52341<element2><sub>3</sub></element2> <element2><sub>4</sub></element2>0x4312";
        List<string> components = new List<string>();
        while (source.Length > 0)
        {
            int start = source.IndexOf('<');
            if (-1 == start)
                break;
            int next = source.IndexOf("0x", start, StringComparison.OrdinalIgnoreCase);
            if (-1 == next)
                break;
            components.Add(source.Substring(start, next - start));
            source = source.Substring(next);
        }
        foreach (string s in components)
            Console.WriteLine(s);
        Console.ReadLine();
    }
}

试试看。

【讨论】:

  • 这不起作用,因为我的&lt;elementx&gt; 中也有0x
  • 啊,好吧。我在示例数据中没有看到这种情况。我刚刚意识到我也没有处理你用空格而不是十六进制值引导组件之一的情况。我会再考虑一下。
  • 我简化了我的文件,因为我无法在 SO 中发布整个 10MB 文件 - 有时0x1234 值有时没有,所以我不能依赖它们
【解决方案4】:

这会将文件作为流处理 - 查找打开和关闭元素,仅解析正在处理的那些元素:

  using (var stream = File.OpenRead("..."))
  {
    StringBuilder builder = null;
    StringBuilder xml = null;
    using (var reader = new StreamReader(stream, Encoding.UTF8))
    {
      while (!reader.EndOfStream)
      {
        char c = (char)reader.Read();
        if (c == '<' && builder == null)
        {
          builder = new StringBuilder();
        }
        if (builder != null)
        {
          builder.Append(c);
        }
        if (xml != null)
        {
          xml.Append(c);
        }

        if (c == '>')
        {
          var token = builder.ToString();
          if (xml == null)
          {
            if (token.StartsWith("<element1", StringComparison.Ordinal) || token.StartsWith("<element2", StringComparison.Ordinal))
            {
              xml = new StringBuilder("<?xml version='1.0' encoding='utf-8' ?>");
              xml.Append(token);
            }
          }
          else
          {
            if (token.StartsWith("</element1", StringComparison.Ordinal) || token.StartsWith("</element2", StringComparison.Ordinal))
            {
              XElement element = XElement.Parse(xml.ToString());
              // do something with the element
              xml = null;
            }
          }
          builder = null;
        }
      }
    }
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-01
    • 2012-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    相关资源
    最近更新 更多