将XML标记表示为字符范围

将XML标记表示为字符范围,xml,xml-parsing,Xml,Xml Parsing,您知道有任何基于原始未标记文本将XML标记与字符范围或偏移量信息进行转换的XML库吗?(我不太关心库的基础平台:它可以是Java、Python、Perl等。) 例如,假设我有以下未标记的文本: the calico cat and the black dog 标记为 the <PET>calico</PET> cat and the <PET>black do</PET>g 在修复偏移量之后,我将重新生成XML 我只找到了几个返回字符偏移量信息

您知道有任何基于原始未标记文本将XML标记与字符范围或偏移量信息进行转换的XML库吗?(我不太关心库的基础平台:它可以是Java、Python、Perl等。)

例如,假设我有以下未标记的文本:

the calico cat and the black dog
标记为

the <PET>calico</PET> cat and the <PET>black do</PET>g
在修复偏移量之后,我将重新生成XML


我只找到了几个返回字符偏移量信息的XML解析库,偏移量基于XML文本,而不是未标记的文本。偏移量也可能是错误的(cf.)。

您反对.NET吗

您可能希望从HTML的角度解决这个问题。有一个名为的库可以解析HTML(不管怎样,它只是XML)。在这样做时,您的示例看起来像一个节点列表,在文本节点和HTML(XML)
PET
节点之间分解:

HtmlNode[n]
|
+--[0] "the " (text node)
|
+--[1] <PET>
|   |
|   +--[0] "calico" (text node)
|
+--[2] " cat and the " (text node)
|
+--[3] <PET>
|   |
|   +--[0] "black do" (text node)
|
+--[4] "g" (text node)
HtmlNode[n]
|
+--[0]“文本节点”(文本节点)
|
+--[1] 
|   |
|+--[0]“印花布”(文本节点)
|
+--[2] “猫和猫”(文本节点)
|
+--[3] 
|   |
|+--[0]“黑色do”(文本节点)
|
+--[4] “g”(文本节点)
每个
HtmlNode
对象都有一个
LinePosition
属性,可以为您提供起始偏移量。可以通过增加节点文本的长度(属性
InnerText
)或从下一个节点的
LinePosition
中减去1来计算端点偏移

我不知道你是否认为这种方法不那么痛苦,但我会从这里开始(以前从未解决过这样的问题)


这里列出了各种语言的HTML解析库。

下面介绍如何使用.NET中的XmlReader实现这一点:

class MarkupSpan
{
    internal string Name;
    internal int Start;
    internal int Stop;
    internal List<object> ChildItems;

    internal MarkupSpan(string name, int start)
    {
        Name = name;
        Start = start;
        ChildItems = new List<object>();
    }

    public override string ToString()
    {
        return string.Concat(ChildItems);
    }
}


private static string ProcessMarkup(string text)
{
    Stack<MarkupSpan> inputStack = new Stack<MarkupSpan>();

    StringReader sr = new StringReader("<n>" + text + "</n>");

    XmlReader xr = XmlReader.Create(sr);
    int pos = 0;
    StringBuilder output = new StringBuilder();

    while (xr.Read())
    {
        if (xr.Depth > 0)
        {
            switch (xr.NodeType)
            {
                case XmlNodeType.Text:
                    pos += xr.Value.Length;
                    if (inputStack.Count != 0)
                    {
                        inputStack.Peek().ChildItems.Add(xr.Value);
                    }
                    break;
                case XmlNodeType.Element:
                    MarkupSpan ms = new MarkupSpan(xr.LocalName, pos);
                    if (inputStack.Count != 0)
                    {
                        inputStack.Peek().ChildItems.Add(ms);
                    }
                    inputStack.Push(ms);
                    break;
                case XmlNodeType.EndElement:
                    ms = inputStack.Pop();
                    ms.Stop = pos;
                    if (inputStack.Count == 0)
                    {
                        output.Append(OutputSpan(ms));
                    }
                    break;
            }
        }
    }

    return output.ToString();
}

private static string OutputSpan(MarkupSpan ms)
{
    string nameAndRange = string.Format("{0}: {1}-{2}",
                                        ms.Name, ms.Start, ms.Stop);
    return string.Format("{0,-14}# \"{1}\"", nameAndRange, ms) +
           Environment.NewLine +
           string.Concat(ms.ChildItems.OfType<MarkupSpan>().Select(OutputSpan));
}
在更有趣的示例上运行时(使用嵌套标记):


我已经提供了一个.NET答案,但以下是如何使用XSLT实现这一点:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" />
  <xsl:variable name="space" select="'                  '" />
  <xsl:variable name="spaceLen" select="string-length($space)" />

  <xsl:template match="text()" />

  <xsl:template match="*/*">
    <xsl:param name="parentLeading" select="0" />
    <xsl:variable name="leadingText">
      <xsl:apply-templates select="preceding-sibling::node()" mode="value" />
    </xsl:variable>

    <xsl:variable name="leading" select="$parentLeading + 
                                             string-length($leadingText)" />

    <xsl:variable name="nameAndRange" 
                  select="concat(local-name(), ' ', $leading, 
                                 '-', $leading + string-length())" />
    <xsl:variable name="spacing"
                  select="substring($space, 1, 14 - string-length($nameAndRange))" />
    <xsl:value-of select="concat($nameAndRange, $spacing, 
                                 '# &quot;', ., '&quot;&#xA;')"/>
    <xsl:apply-templates>
      <xsl:with-param name="parentLeading" select="$leading" />
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="node()" mode="value">
    <xsl:value-of select="." />
  </xsl:template>
</xsl:stylesheet>
在该输入上运行时:

<n>the <PET>calico</PET> cat and the <PET>black do</PET>g</n>
<n>the <PET><COLOR>calico</COLOR></PET> cat and the <PET><COLOR>bla</COLOR>ck do</PET>g</n>

从长远来看,.Net可能不是我们的正确选择,但感谢您的回答。我已经用Java库编写了类似.NETXMLReader的解决方案,但是由于库错误,位置信息不准确。
PET: 4-10     # "calico"
COLOR: 4-10   # "calico"
PET: 23-31    # "black do"
COLOR: 23-26  # "bla"
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" />
  <xsl:variable name="space" select="'                  '" />
  <xsl:variable name="spaceLen" select="string-length($space)" />

  <xsl:template match="text()" />

  <xsl:template match="*/*">
    <xsl:param name="parentLeading" select="0" />
    <xsl:variable name="leadingText">
      <xsl:apply-templates select="preceding-sibling::node()" mode="value" />
    </xsl:variable>

    <xsl:variable name="leading" select="$parentLeading + 
                                             string-length($leadingText)" />

    <xsl:variable name="nameAndRange" 
                  select="concat(local-name(), ' ', $leading, 
                                 '-', $leading + string-length())" />
    <xsl:variable name="spacing"
                  select="substring($space, 1, 14 - string-length($nameAndRange))" />
    <xsl:value-of select="concat($nameAndRange, $spacing, 
                                 '# &quot;', ., '&quot;&#xA;')"/>
    <xsl:apply-templates>
      <xsl:with-param name="parentLeading" select="$leading" />
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="node()" mode="value">
    <xsl:value-of select="." />
  </xsl:template>
</xsl:stylesheet>
<n>the <PET>calico</PET> cat and the <PET>black do</PET>g</n>
PET 4-10      # "calico"
PET 23-31     # "black do"
<n>the <PET><COLOR>calico</COLOR></PET> cat and the <PET><COLOR>bla</COLOR>ck do</PET>g</n>
PET 4-10      # "calico"
COLOR 4-10    # "calico"
PET 23-31     # "black do"
COLOR 23-26   # "bla"