Excel - 使用 FILTERXML 从字符串中提取子字符串

Excel - Extract substring(s) from string using FILTERXML

提问人:JvdV 提问时间:5/16/2020 最后编辑:Mark RotteveelJvdV 更新时间:6/1/2023 访问量:15515

问:

背景

最近,我一直在尝试更熟悉将分隔字符串更改为 XML 的概念,以便使用 Excel 的 FILTERXML 进行解析并检索感兴趣的子字符串。请注意,此功能来自 Excel 2013,在 Excel for Mac 和 Excel Online 上不可用。

对于带分隔符的字符串,我的意思是使用空格作为分隔符的普通句子或任何其他可用于定义字符串中子字符串的字符组合。例如,让我们想象一下:

ABC|123|DEF|456|XY-1A|ZY-2F|XY-3F|XY-4f|xyz|123

问题

因此,很多人知道如何获取第 n 个元素(例如:检索)。或者与 、 和所有这些结构的其他组合,我们如何使用更具体的标准来提取关注的子字符串并清理整个字符串?例如,如何检索:=TRIM(MID(SUBSTITUTE(A1,"|",REPT(" ",LEN(A1))),3*LEN(A1)+1,LEN(A1)))456LEN()MID()FIND()FILTERXML

  • 按位置划分的元素
  • 数值或非数值元素
  • 元素本身包含子字符串
  • 以子字符串开头或结尾的元素
  • 大写或小写的元素
  • 保存数字的元素
  • 唯一值
  • ...
数组 excel xml xpath excel-formula

评论


答:

50赞 JvdV 5/16/2020 #1

Excel的用途,不幸的是,这意味着它并不像我们希望的那样多样化。此外,Excel 似乎不允许返回返工的节点值,并且只允许您按外观顺序选择节点。但是,我们仍然可以使用相当多的功能。可以在此处找到有关此内容的更多信息。FILTERXMLXPATH 1.0

该函数采用两个参数:=FILTERXML(<A string in valid XML format>,<A string in valid XPATH format>)

假设单元格保存字符串:.为了创建有效的 XML 字符串,我们使用分隔符将分隔符更改为有效的结束标记和开始标记构造。因此,为了获得给定示例的有效 XML 构造,我们可以执行以下操作:A1ABC|123|DEF|456|XY-1A|ZY-2F|XY-3F|XY-4f|xyz|123SUBSTITUTE

"<t><s>"&SUBSTITUTE(A1,"|","</s><s>")&"</s></t>"

出于可读性的原因,我将使用单词 <XML> 作为占位符来引用上述构造。下面,您将在有效的构造中找到不同的 usefull 函数来过滤节点:XPATH


1) 所有元素:

=FILTERXML(<XML>,"//s")

返回:ABC123DEF456XY-1AZY-2F、XY-3F、XY-4fxyz123(所有节点)


2) 按位置划分的元素:

=FILTERXML(<XML>,"//s[position()=4]")

艺术

=FILTERXML(<XML>,"//s[4]")

返回值:456(索引 4 上的节点)

=FILTERXML(<XML>,"//s[position()<4]")

返回:ABC123DEF(索引< 4 上的节点)

=FILTERXML(<XML>,"//s[position()=2 or position()>5]")

返回:123ZY-2F、XY-3F、XY-4fxyz123(索引 2 或> 5 上的节点)

=FILTERXML(<XML>,"//s[last()]")

返回值:123(最后一个索引上的节点)

=FILTERXML(<XML>,"//s[position() mod 2 = 1]")

返回:ABCDEF、XY-1A、XY-3Fxyz(奇数节点)

=FILTERXML(<XML>,"//s[position() mod 2 = 0]")

返回:123456ZF-2FXY-4f123(偶数节点)


3) (非)数值元素:

=FILTERXML(<XML>,"//s[number()=.]")

艺术

=FILTERXML(<XML>,"//s[.*0=0]")

返回值:123456123(数值节点)

=FILTERXML(<XML>,"//s[not(number()=.)]")

艺术

=FILTERXML(<XML>,"//s[.*0!=0)]")

返回:ABCDEFXY-1AZY-2F、XY-3F、XY-4fxyz(非数字节点)


4) (不)包含以下元素:

=FILTERXML(<XML>,"//s[contains(., 'Y')]")

返回:XY-1AZY-2F、XY-3F 和 XY-4f(包含“Y”,注意 XPATH 区分大小写,不包括 xyz

=FILTERXML(<XML>,"//s[not(contains(., 'Y'))]")

返回值:ABC123DEF456xyz123(不包含“Y”,请注意 XPATH 区分大小写,包括 xyz


5) 以(不)开头或/和结尾的元素:

=FILTERXML(<XML>,"//s[starts-with(., 'XY')]")

返回:XY-1A、XY-3FXY-4f(以“XY”开头)

=FILTERXML(<XML>,"//s[not(starts-with(., 'XY'))]")

返回:ABC123DEF456ZY-2Fxyz123(不要以“XY”开头)

=FILTERXML(<XML>,"//s[substring(., string-length(.) - string-length('F') +1) = 'F']")

返回:DEFZY-2F 和 XY-3F(以“F”结尾,请注意 XPATH 1.0 不支持以结尾

=FILTERXML(<XML>,"//s[not(substring(., string-length(.) - string-length('F') +1) = 'F')]")

返回:ABC123、456、XY-1A、XY-4fxyz123(不要以“F”结尾)

=FILTERXML(<XML>,"//s[starts-with(., 'X') and substring(., string-length(.) - string-length('A') +1) = 'A']")

返回:XY-1A(以“X”开头,以“A”结尾)


6) 大写或小写的元素:

=FILTERXML(<XML>,"//s[translate(.,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')=.]")

返回值:ABC123DEF456XY-1AZY-2FXY-3F123(大写节点)

=FILTERXML(<XML>,"//s[translate(.,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')=.]")

返回值:123456xyz123(小写节点)

注意:不幸的是,XPATH 1.0 不支持 upper-case() 或 lower-case(),因此以上是一种解决方法。如果需要,请添加特殊字符。


7) (不)包含任何数字的元素:

=FILTERXML(<XML>,"//s[translate(.,'1234567890','')!=.]")

返回:123456XY-1AZY-2F、XY-3F、XY-4f123(包含任意数字)

=FILTERXML(<XML>,"//s[translate(.,'1234567890','')=.]")

返回值:ABCDEFxyz(不包含任何数字)

=FILTERXML(<XML>,"//s[translate(.,'1234567890','')!=. and .*0!=0]")

返回:XY-1AZY-2F、XY-3F 和 XY-4f(包含数字,但不是数字)


8) 独特元素或重复元素:

=FILTERXML(<XML>,"//s[preceding::*=.]")

返回值:123(重复节点)

=FILTERXML(<XML>,"//s[not(preceding::*=.)]")

返回:ABC123DEF456XY-1AZY-2FXY-3FXY-4fxyz(唯一节点)

=FILTERXML(<XML>,"//s[not(following::*=. or preceding::*=.)]")

返回:ABCDEF456XY-1AZY-2F、XY-3F 和 XY-4f(没有类似同级的节点)


9)一定长度的元素:

=FILTERXML(<XML>,"//s[string-length()=5]")

返回:XY-1AZY-2F、XY-3F 和 XY-4f(长度为 5 个字符)

=FILTERXML(<XML>,"//s[string-length()<4]")

返回值:ABC123DEF456xyz123(短于 4 个字符)


10) 基于前/后要素:

=FILTERXML(<XML>,"//s[preceding::*[1]='456']")

返回值:XY-1A(前一个节点等于“456”)

=FILTERXML(<XML>,"//s[starts-with(preceding::*[1],'XY')]")

返回:ZY-2FXY-4fxyz(上一个节点以“XY”开头)

=FILTERXML(<XML>,"//s[following::*[1]='123']")

返回:ABCxyz(以下节点等于“123”)

=FILTERXML(<XML>,"//s[contains(following::*[1],'1')]")

返回值:ABC456xyz(以下节点包含“1”)

=FILTERXML(<XML>,"//s[preceding::*='ABC' and following::*='XY-3F']")

艺术

=FILTERXML(<XML>,"//s[.='ABC']/following::s[following::s='XY-3F']")    

返回:123DEF456XY-1AZY-2F(介于“ABC”和“XY-3F”之间的所有内容)


11) 基于子字符串的元素:

=FILTERXML(<XML>,"//s[substring-after(., '-') = '3F']")

返回值:XY-3F(连字符后以“3F”结尾的节点)

=FILTERXML(<XML>,"//s[contains(substring-after(., '-') , 'F')]")

返回值:ZY-2FXY-3F(连字符后包含“F”的节点)

=FILTERXML(<XML>,"//s[substring-before(., '-') = 'ZY']")

返回值:ZY-2F(连字符前以“ZY”开头的节点)

=FILTERXML(<XML>,"//s[contains(substring-before(., '-'), 'Y')]")

返回:XY-1AZY-2F、XY-3F 和 XY-4f(连字符前包含“Y”的节点)


12) 基于串联的元素:

=FILTERXML(<XML>,"//s[concat(., '|', following::*[1])='ZY-2F|XY-3F']")

返回值:ZY-2F(与“|”连接时的节点,后面的同级节点等于“ZY-2F|XY-3F')

=FILTERXML(<XML>,"//s[contains(concat(., preceding::*[2]), 'FA')]")

返回值:DEF(当节点与左侧的同级两个索引连接时,包含“FA”)


13) 空与非空:

=FILTERXML(<XML>,"//s[count(node())>0]")

艺术

=FILTERXML(<XML>,"//s[node()]")

返回:ABC123DEF456XY-1AZY-2F、XY-3F、XY-4fxyz123(所有不为空的节点)

=FILTERXML(<XML>,"//s[count(node())=0]")

艺术

=FILTERXML(<XML>,"//s[not(node())]")

返回值:None(所有为空的节点)


14) 之前或之后:

=FILTERXML(<XML>,"//s[substring(., string-length(.) - string-length('F') +1) = 'F'][last()]/following::*")

返回:XY-4fxyz123(最后一个节点右侧的所有节点,以大写字母“F”结尾)

=FILTERXML(<XML>,"//s[substring(., string-length(.) - string-length('F') +1) = 'F'][1]/preceding::*")

返回值:ABC123(第一个节点左侧以大写字母“F”结尾的所有节点)


15)(前面或后面)和自己:

=FILTERXML(<XML>,"(//s[.*0!=0][last()]|//s[.*0!=0][last()]/preceding::*)")

返回:ABC123DEF456XY-1AZY-2FXY-3FXY-4fxyz(从右侧修剪所有数字节点††

=FILTERXML(<XML>,"(//s[.*0=0][1]|//s[.*0=0][1]/following::*)")

返回值:123DEF456XY-1AZY-2F、XY-3F、XY-4fxyz123(从左侧修剪所有非数字节点)


16) 最大值或最小值:

=FILTERXML(<XML>,"(//s[.*0=0][not(.<//s[.*0=0])])[1]")

返回值:456(查看数值节点的最大值)

=FILTERXML(<XML>,"(//s[.*0=0][not(.>//s[.*0=0])])[1]")

返回值:123(查看数值节点的最小值)

注意:这相当于按照 #3 返回所有数字节点,并使用 Excel 的 MIN() 和 MAX() 函数对数组进行后处理。


现在显然,以上是功能可能性的演示,您可以获得上述各种组合以及更多!我试图涵盖最常用的字符串函数。如果您遗漏任何内容,请随时发表评论。XPATH 1.0

虽然这个问题本身就很宽泛,但我希望就如何使用手头的查询给出一些总体指导。该公式返回要以任何其他方式使用的节点数组。很多时候我会在 or 中使用它。但我想其他选择将是新的 DA 函数来溢出结果。FILTERXMLTEXTJOIN()INDEX()

请注意,在解析字符串时,与号字符 (&) 和左尖括号 (<) 不得以文字形式出现。它们将分别需要替换为 或 。另一种选择是分别使用其数字 ISO/IEC 10646 字符代码 as 或。解析后,该函数会将这些字符以文字形式返回给您。毋庸置疑,用分号拆分字符串就变得很棘手了。FILTERXML()&amp;&lt;&#38;&#60;


每个谓词,左方括号和右方括号之间的结构,都是给定节点列表的过滤器。写出多个这样的结构实际上是这样的谓词。

没有一个简单的、跟随/先于兄弟姐妹自我的构造。因此,我使用了联合运算符。然而,这需要多个表达式位于 paranthesis 内。就像捕获组中的交替一样,如果人们会考虑正则表达式。

评论

3赞 T.M. 8/25/2020
仅供参考,您可能对一种棘手的方法感兴趣,该方法可以通过一些解释(以及指向上述帖子的链接:-) 将数字字符串更改为唯一数字的排序数组at 将数字划分为唯一的排序数字 - @JvdVFilterXML
2赞 JvdV 1/27/2021
@Harun24HR,是的,您可以使用逻辑运算符,例如“less then”。您可以直接应用例如:在上面的示例中,它将返回两个“123”节点。=FILTERXML(<XML>,"//s[.<200]")
2赞 Harun24hr 1/27/2021
@JvdV 那行得通!老实说,我从你的这篇文章中学到了。谢谢你。FILTERXML()
1赞 mark fitzpatrick 8/31/2021
这是一个很好的参考 - 好吧,完成,@JvdV。
2赞 David Leal 11/15/2022
很好的解释@JvdV,不是我更好地理解它。值得注意的是,根据 FILTERXML 文档,截至 2022 年 11 月 15 日,Excel 网页版和 Excel Mac 版中不可用
16赞 JvdV 12/19/2020 #2

编辑 22-3-2022

根据新功能,现在可以将字符串直接拆分为水平或垂直范围(或两者兼而有之)。它甚至可以拆分值数组。但是,它不能像“xpath”那样直接过滤。因此,我将保持以下答案不变,因为它仍然相关。TEXTSPLIT()


臭名昭著的缺失函数XSPLIT()

这篇文章旨在进一步介绍我们如何在不使用 VBA 的情况下使用自己的可重用函数。虽然目前处于测试阶段,但即将到来,使用此功能,我们可以创建自己的自定义函数。让我举个例子来解释一下:XSPLIT()FILTERXML()LAMBDA()

在此处输入图像描述

公式很简单,它按外观顺序溢出带分隔符的文本值。但是,我们在“名称管理器”中创建的函数的名称是:C1=XSPLIT(A1,B1:B3)SPLIT()LAMBDA()

=LAMBDA(txt,del,[xpath],FILTERXML("<t><s>"&TEXTJOIN("</s><s>",,TEXTSPLIT(txt,del))&"</s></t>","//s"&xpath))

如您所见,该函数有 4 个参数:

  • txt- 对源值的引用。
  • del- 我们想要的任何数量的分隔符。书面或参考。
  • [xpath]- 如果需要,请放置 xpath 表达式以应用一些过滤器。例如:仅返回数字子字符串。这是一个可选参数"[.*0=0]"
  • FILTERXML("<t><s>"&TEXTJOIN("</s><s>",,TEXTSPLIT(txt,del))&"</s></t>","//s"&xpath)

第 4 个参数是调用所有前 3 个参数以创建与主文章中介绍的相同的构造的地方。现在,由于 MS 不想给我们自己,我们用三个参数创建了自己的函数。XSPLIT()

主要帖子集中在特定分隔符上;在给定的示例中,pipe-symbol.但是,如果您有多个分隔符怎么办?你需要多个嵌套函数,对吧?如果我们也能在我们的函数中实现它,那不是很好吗?这是我个人感到兴奋的地方,因为我们可以为这个函数提供多个分隔符来将字符串拆分为元素。SUBSTITUTE()SUBSTITUTE()XSPLIT()TEXTSPLIT()

现在,我们已经使用三个参数创建了自己的函数:XSPLIT()

=XSPLIT(<StringToBeSplited>,<YourDelimiters>,<OptionalXpath>)

现在,我们可以将其用作整个工作簿中的函数。享受!

评论

1赞 JB-007 8/19/2022
用极好的答案让南非同胞感到自豪 - 不错的一个boet(假设我没有:)盗用性别。我只在 Lambda 中打过点,仍然需要了解 reduce 函数 - 似乎没有 Excel 知识的高峰期。无论如何 - 从我这边点赞,继续努力。
1赞 JvdV 8/19/2022
豪西特 @JB-007。谢谢你的赞誉。继续学习,不要回避问我是否可以提供帮助