You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用正则捕获XML同标签内可变数量组且不越界?

问题描述

我有如下格式的XML文件:

<Table>
  <Persons>
    <Person>
      <ID>71</ID>
      <FullNameLikeX>"sentence expected"</FullNameLikeX>
      <Age>49</Age>
      <FavoriteFood>Banana</FavoriteFood>
      <NameParts>
        <word>Jhon</word>
        <word>Henry</word>
        <word>Abbot</word>
      </NameParts>
    </Person>
    <Person>
      <ID>72</ID>
      <FullNameLikeX>"sentence expected"</FullNameLikeX>
      <Age>26</Age>
      <FavoriteFood>Cake</FavoriteFood>
      <NameParts>
        <word>Cecilia</word>
        <word>Elisabeth</word>
        <word>Maria</word>
        <word>Smith</word>
      </NameParts>
    </Person>
    <Person>
      <ID>73</ID>
      <FullNameLikeX>"sentence expected"</FullNameLikeX>
      <Age>17</Age>
      <FavoriteFood>Lasagna</FavoriteFood>
      <NameParts>
        <word>Luc</word>
        <word>Hernandez</word>
      </NameParts>
    </Person>
  </Persons>
</Table>

需要在Notepad++中用正则表达式将<FullNameLikeX>标签内的"sentence expected"替换为对应<NameParts>下所有<word>标签内容拼接后加" like "和<FavoriteFood>标签内容(比如第一个Person应替换为"Jhon Henry Abbot like Banana")。

但处理<NameParts>内数量可变(1-5个)的<word>标签时,尝试多种正则均出现问题:
我使用的正则结构为:

(&lt;FullNameLikeX&gt;&quot;)[\s\S]*?(&quot;&lt;\/FullNameLikeX&gt;)([\s\S]*?&lt;FavoriteFood&gt;([\s\S]*?)&lt;\/FavoriteFood&gt;[\s\S]*?&lt;NameParts&gt;###[\s\S]*?&lt;\/NameParts&gt;)

其中###位置尝试了以下写法:

  • (?:[\s\S]*?&lt;word&gt;([\s\S]*?)&lt;\/word&gt;)?:当<word>数量少于分组数时,组会越界到下一个<Person>;
  • (?:[\s\S]*?&lt;word&gt;([\s\S]*?)&lt;\/word&gt;)??:不会越界,但无法捕获分组;
  • (?:[\s\S]*?&lt;word&gt;([\s\S]*?)&lt;\/word&gt;)+?:当<word>数量少于分组数时,组会越界到下一个<Person>;
  • (?:[\s\S]*?&lt;word&gt;([\s\S]*?)&lt;\/word&gt;(?![\s\S]*?&lt;\/Person&gt;[\s\S]*?))?:不会越界,但捕获组为空。

现询问:仅用正则表达式是否可实现捕获可变数量XML标签内容且不越界的需求?


回答

单步正则替换很难完美实现这个需求——因为正则的捕获组数量是固定的,没法动态匹配并拼接可变数量的<word>内容。不过在Notepad++里可以分两步操作,简单高效:

第一步:合并<NameParts>内的<word>内容

先把每个<NameParts>下的所有<word>标签去掉,将内容用空格拼接:

  1. 打开Notepad++的替换窗口(Ctrl+H);
  2. 选择“正则表达式”模式,勾选“匹配换行符”;
  3. 查找内容:
(<NameParts>[\s\S]*?)<word>([^<]+)<\/word>([\s\S]*?<\/NameParts>)
  1. 替换为:
$1$2 $3
  1. 重复点击“替换全部”,直到提示“0处替换完成”。这时候<NameParts>里就只剩下用空格分隔的名字内容了。

第二步:替换<FullNameLikeX>的内容

现在可以直接提取拼接好的名字和<FavoriteFood>内容来替换目标标签:

  1. 同样在替换窗口,保持正则模式和匹配换行符的勾选;
  2. 查找内容:
(<Person>[\s\S]*?<FullNameLikeX>)"sentence expected"(<\/FullNameLikeX>[\s\S]*?<FavoriteFood>)([^<]+)(<\/FavoriteFood>[\s\S]*?<NameParts>)([^<]+?)(<\/NameParts>[\s\S]*?<\/Person>)
  1. 替换为:
$1"$5 like $3"$2$3$4$5$6

点击“替换全部”,就能得到你想要的结果。

如果非要用单步正则,也可以写一个覆盖1-5个<word>的冗长表达式,但扩展性极差(超过5个<word>就失效),还会出现多余空格需要处理,完全没必要。分两步操作是最实用的方案。


内容的提问来源于stack exchange,提问作者Nidar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:52:31