【问题标题】:Find and conditionally edit text in an XML file在 XML 文件中查找和有条件地编辑文本
【发布时间】:2019-12-17 05:10:43
【问题描述】:

我有一个包含以下内容的 (XML-) 文件:

<class>OverAll</class>
        <char>
                <rank> 1</rank>
                <name> yyy</name>
                <level> 9</level>
                <experience>53842</experience>
                <class>xxx</class>
        </char>
        <char>
                <rank> 2</rank>
                <name>aaa</name>
                <level> 9</level>
                <experience>53074</experience>
                <class>zzz</class>
        </char>

..等等。我想提取&lt;experience&gt; &lt;/experience&gt; 行之间的数字,并将其替换为我在标签之间找到的数字的修改版本。例如,该文件在脚本之后应如下所示:

<class>OverAll</class>
        <char>
                <rank> 1</rank>
                <name> yyy</name>
                <level> 9</level>
                <experience>53.842</experience>
                <class>xxx</class>
        </char>
        <char>
                <rank> 2</rank>
                <name>aaa</name>
                <level> 9</level>
                <experience>53.074</experience>
                <class>zzz</class>
        </char>

(我想添加一个千位分隔符,并且需要 100 万以上的值。所以 2000 个分隔符 :) 我能够找到并替换号码,但我不知道如何使用输入的号码并对其进行修改并将其添加回该行。

也许有人可以在这里提供帮助? 非常感谢:)

【问题讨论】:

  • 你试过了吗?
  • 如果您有 XML 文件,请使用 XML 工具,例如 XMLStarlet

标签: bash shell replace xml-parsing find


【解决方案1】:

单行sed 可以做到,假设最后三位总是十进制:

sed -zE 's#([[:digit:]]{7,})([[:digit:]]{1})[[:space:]]*(</experience[[:space:]]*>)#\1.\2\3#g;s#([[:digit:]]{3})[[:space:]]*(</experience[[:space:]]*>)#.\1\2#g'

sed参数分解:

  • -zE
    • -z--null-data:用 NULL 字符分隔行以允许跨行进行模式匹配,因为 XML 语法允许在标记的 &gt; 括号之前使用空格、制表符和换行符。
    • -E--regexp-extended:在脚本中使用扩展的正则表达式(为了便于移植,请使用 POSIX -E)。
  • s#([[:digit:]]{7,})([[:digit:]]{1})[[:space:]]*(&lt;/experience[[:space:]]*&gt;)#\1.\2\3#g:
    在最后一位前插入小数点,以体验包含七位加一(八)位或更多位的数字(百万位或更多位带一个额外的小数位)。
  • s#([[:digit:]]{3})[[:space:]]*(&lt;/experience[[:space:]]*&gt;)#.\1\2#g:
    在最后三位数字前插入小数点,以体验以三位数字结尾的数字(自动排除之前sed 命令已处理的百万体验。

现在请记住,它也不会解析 XML,因为它会替换 XML 树中任何位置的 &lt;experience&gt; 标记中的数字。

正则表达式并不意味着解析标记语言。有更好、更高效和专用的工具来使用 XSLT/XPATH 操作 XML,例如 saxonxsltprocxmllint...

xsltproc 使用适当的 XML 处理:

decimal-experience.xsl

<?xml version="1.0"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!-- Cosmetic sugar to have the xml declaration header and indent -->
  <xsl:output omit-xml-declaration="no" indent="yes"/>

  <!-- Cosmetic sugar to remove unneeded spaces in elements -->
  <xsl:strip-space elements="*"/>

  <!-- Copy all the nodes as-is from the source xml -->
  <xsl:template match="*">

    <xsl:copy>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- Process the content of the experience tag within the char tag -->
  <xsl:template match="char/experience/">

    <!-- If the experience is not already in decimal form -->
    <xsl:if test="not(contains(., '.'))">

      <xsl:choose>

        <!-- When the experience is less than a Million -->
        <xsl:when test=". &lt; 9999999">
          <!-- The last three digits are decimals -->
          <xsl:value-of select="format-number(. div 1000, '0.000')"/>
        </xsl:when>

        <!-- Otherwise the experience is a Million or more -->
        <xsl:otherwise>
          <!-- The last digit is decimal -->
          <xsl:value-of select="format-number(. div 10, '0.0')"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

运行上面的 XSLT 转换:

xsltproc decimal-experience.xsl characters.xml

示例输出:

我创建了一个带有 span 根标记的有效虚构 characters.xml,因为您的提取是无效的 XML。

<?xml version="1.0"?>
<span>
  <class>OverAll</class>
  <char>
    <rank> 1</rank>
    <name> yyy</name>
    <level> 9</level>
    <experience>53.842</experience>
    <class>xxx</class>
  </char>
  <char>
    <rank> 2</rank>
    <name>aaa</name>
    <level> 9</level>
    <experience>53.074</experience>
    <class>zzz</class>
  </char>
  <char>
    <rank> 3</rank>
    <name>Million</name>
    <level>42</level>
    <experience>5585307.4</experience>
    <class>zzz</class>
  </char>
</span>

【讨论】:

  • 不会删除结束标签吗?此外,扩展正则表达式以处理 xp > 1M 的额外积分。 ;)
  • 修复了结束标签的不必要删除
  • 未指定问题,但如果它必须适用于超过一百万的值。只有最后一千人得到一个点。
  • 谢谢。但是,是的,可能有超过 100 万的值。有解决方案吗? :o)
  • 超过 100 万的值只有一位小数:sed -E 's#([[:digit:]]{7,})([[:digit:]]{1})(&lt;/experience&gt;)#\1.\2\3#g;s#([[:digit:]]{3})(&lt;/experience&gt;)#.\1\2#g'
猜你喜欢
  • 2014-01-05
  • 1970-01-01
  • 1970-01-01
  • 2019-10-16
  • 2016-06-10
  • 2021-12-10
  • 1970-01-01
  • 2016-08-10
  • 2020-12-15
相关资源
最近更新 更多