将分层文本列表转换为离散列表

Convert hierarchical text list to discrete lists

从大型层次结构中提取一层深度的聪明方法是什么?想象以下制表符分隔的分层文本列表:

plants
   fruits
      apples
      bananas
      cherries
   trees
      oaks
      maples
   vegetables
      onions
      peppers

目标是只输出一级深度的子​​列表:

plants
   fruits
   trees
   vegetables

fruits
   apples
   bananas
   cherries

trees
   oaks
   maples

vegetables
   onions
   peppers

应该可以处理 mac 和 larger/deeper 文件。例如,这是一个更大的文件:

life
    animals
        mammals
            dogs
            cats
            rabbits
        fish
            salmon
        birds
    plants
        fruits
            apples
            bananas
            cherries
            mangoes
        trees
            oaks
            maples
            birch
            pine
        vegetables
            onions
            peppers

这将是较大输入的输出:

life
    animals
    plants

animals
    mammals
    fish
    birds

mammals
    dogs
    cats
    rabbits

fish
    salmon

plants
    fruits
    trees
    vegetables

fruits
    apples
    bananas
    cherries
    mangoes

trees
    oaks
    maples
    birch
    pine

vegetables
    onions
    peppers

由于您已将问题标记为 XSLT,我 post 一个 XSLT 3.0 解决方案应该适用于 Saxon 9.6(例如可用于 oXygen)或 Saxon 9.7 或 Exselt 的商业版本:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="xs math mf"
    version="3.0">

<xsl:param name="text-uri" as="xs:string" select="'test2016030301.txt'"/>

<xsl:param name="indent" as="xs:string" select="'&#9;'"/>

<xsl:output method="text"/>

<xsl:function name="mf:group" as="element(item)*">
    <xsl:param name="lines" as="xs:string*"/>
    <xsl:param name="level" as="xs:integer"/>
    <xsl:for-each-group select="$lines" group-starting-with=".[. instance of xs:string and matches(., '^' || string-join((1 to $level)!$indent) || '\w')]">
        <item data="{normalize-space()}">
            <xsl:sequence select="mf:group(current-group()[position() gt 1], $level + 1)"/>
        </item>
    </xsl:for-each-group>
</xsl:function>

<xsl:template name="main" match="/">
    <xsl:apply-templates select="mf:group(unparsed-text-lines($text-uri), 0)"/>
</xsl:template>

<xsl:template match="item">
    <xsl:value-of select="@data, */($indent || @data)" separator="&#10;"/>
    <xsl:text>&#10;&#10;</xsl:text>
    <xsl:apply-templates select="item[item]"/>
</xsl:template>
</xsl:stylesheet>

使用 it:main 和文本文件的 URI 作为参数调用 XSLT text-uri