在字符串中搜索具有特殊字符的子字符串[重复]
Posted
技术标签:
【中文标题】在字符串中搜索具有特殊字符的子字符串[重复]【英文标题】:search a substring with special characters in a string [duplicate] 【发布时间】:2019-09-03 06:45:42 【问题描述】:我正在搜索字符串中包含特殊字符的子字符串。如何在字符串中搜索子字符串。
$path = 'c:\test'
$mountpoint = 'c:\test\temp\20190987-120\'
我想在$mountpoint
中搜索$path
我尝试过使用-match
、-contains
、-in
等。
PS C:\>$path = 'c:\test'
PS C:\>$mountpoint = 'c:\test\temp\20190987-120\'
PS C:\>$path -contains $mountpoint
False
【问题讨论】:
【参考方案1】:AdminOfThing's answer 很有帮助,但我发现自己希望以不同的方式构图。
您正在寻找一种方法来执行在开始时锚定的文字子字符串搜索,它是唯一的间接在 PowerShell 中得到支持 - 请参阅下一节了解解决方案。
运算符 -contains
和 -in
与 substring 匹配 无关(尽管 -contains
和 @ 之间的名称相似987654322@.NET 方法)。
他们通过逐个元素的方式在集合中测试单个值的成员身份(被作为一个整体包含)平等比较(隐含-eq
)。详情请参阅docs 和底部this answer。
如果您想组合这两个任务 - 在集合的所有元素中查找子字符串 - 您可以利用 PowerShell 的 -match
和 -like
运算符这一事实 - 下面将讨论- 也可以对 collection 值的 LHS 进行操作,在这种情况下,它们充当 过滤器;虽然这与成员资格测试并不完全相同,但它可以有效地用于此; this answer 展示了如何以这种方式使用 -match
。
解决方案:
使用 .NET 框架:
.NET String.IndexOf()
方法执行文字子字符串搜索,并返回输入字符串中子字符串开头的字符的基于0
的索引(如果子字符串不能,则返回-1
根本找不到):
PS> 0 -eq 'foo\bar'.IndexOf('foo\')
True
请注意,与 PowerShell 的运算符不同,上述操作符默认区分大小写,但您可以通过附加参数更改为不区分大小写的行为:
PS> 0 -eq 'foo\bar'.IndexOf('FOO\', [System.StringComparison]::InvariantCultureIgnoreCase)
True
请注意,PowerShell 在许多(但不是全部)上下文中使用 invariant 而不是当前区域性,例如使用运算符 -eq
、-contains
、-in
和 switch
声明。
如果不需要锚你的子字符串搜索,即如果你只想知道输入字符串中是否包含子字符串somewhere,你可以使用String.Contains()
:
# Substring is present, but not at the start
# Note: matching is case-SENSITIVE.
PS> 'foo\bar'.Contains('oo\')
True
警告:在 Windows PowerShell 中,.Contains()
总是区分大小写。在 PowerShell (Core) 7+ 中,提供了一个不区分大小写的额外重载(例如,
'FOO\BAR'.Contains('oo\', 'InvariantCultureIgnoreCase')
)
使用-match
运算符:
虽然-match
确实隐式执行substring 匹配,但它是基于regex (regular expression) 而不是文字字符串。
-match
默认执行大小写-不敏感匹配;使用-cmatch
变体区分大小写。
这意味着您可以方便地使用输入起始锚点^
,以确保搜索表达式仅在输入字符串的开始处匹配。
相反,为了让您的搜索字符串在您的正则表达式中被视为 literal 字符串,您必须 \
-escape 其中的任何正则表达式 元字符(字符具有特殊含义的)在正则表达式中。
由于\
本身就是一个元字符,它也必须被转义,即\\
。
在字符串文字中,您可以手动进行转义:
# Manual escaping: \ is doubled.
# Note the ^ to anchor matching at the start.
PS> 'foo\bar' -match '^foo\\'
True
以编程方式,当字符串作为变量时,必须使用[regex]::Escape()
方法:
# Programmatic escaping via [regex]::Escape()
# Note the ^ to anchor matching at the start.
PS> $s = 'foo\'; 'foo\bar' -match ('^' + [regex]::Escape($s))
True
使用-like
运算符:
与-match
不同,-like
执行 full-string 匹配,并且基于 wildcard expressions(在 Unix 世界中也称为 globs)进行匹配;虽然与正则表达式关系很远,但它们使用更简单、不兼容的语法(并且功能远没有那么强大)。
-like
默认执行大小写-不敏感匹配;使用-clike
变体区分大小写。
通配符只有 3 个基本结构,因此只有 3 个元字符:?
(匹配单个字符)、*
(匹配任意数量的字符,包括无字符)和 [
(开始匹配单个字符的字符集或范围。例如,[a-z]
或 [45]
)。
在最简单的情况下,您只需将*
附加到您的搜索字符串,以查看它是否与输入字符串的开头匹配:
# OK, because 'foo\' contains none of: ? * [
PS> 'foo\bar' -like 'foo\*'
True
# With a variable, using an expandable string:
PS> $s = 'foo\'; 'foo\bar' -like "$s*"
True
不过,与-match
一样,可能需要进行程序转义,这需要调用[WildcardPattern]::Escape()
:
PS> $s = 'foo['; 'foo[bar' -like ([WildcardPattern]::Escape($s) + '*')
True
【讨论】:
【参考方案2】:在这种特殊情况下,您可以使用-Match
。
$mountpoint -match [regex]::escape($path)
这里的问题在于\
字符。它是正则表达式模式中的特殊字符,需要转义。由于-Match
运算符进行正则表达式匹配,因此需要考虑特殊字符。对于这种情况,我选择使用Escape()
方法。您可以使用\
字符单独转义字符,例如c:\\test
。 LotPings cmets 重申这个想法。
使用正则表达式匹配,您可以控制要进行多少匹配。您可以包含锚点和其他特殊字符来定制您的匹配。 Regex101 是测试和了解正则表达式的众多在线选项之一。
如果您注意到下面的示例,则匹配返回 True。这是因为字符串c:\test
存在于c:\testing
中,这可能会给您带来不想要的结果。您需要仔细考虑这些情况。
"c:\testing" -match [regex]::Escape("c:\test")
True
-Contains
和 -in
是收容操作员。它们的目的是检查对象值集合中是否存在单个对象值。例如,当您想将'c:\test'
之类的单个字符串与'c:\test','c:\folder','c:\folder\test'
之类的集合进行比较时,最好使用这些方法。他们采用您正在测试的值,并基本上对集合中的每个项目执行-eq
比较(不是字面意思,而是更有效地)。但是,您可以比较集合,但整个测试集合必须作为元素存在于引用集合中。使用-Contains
,您希望您的参考集合位于操作员的 LHS 上。使用 -in
,您希望您的参考集合位于运营商的 RHS 上。
使用 -Contains 和 -In 的示例
$collection = 'c:\test','c:\folder','c:\folder\test'
$path = 'c:\test'
$collection -contains $path
True
$path -in $collection
True
"c:\test\" -in $collection
False
请注意最后一个示例中的 False 返回,因为尾随的 \
字符使其与集合中的任何元素都不同。
有关-Match
的信息,请参阅About_Comparison_Operators,有关Escape()
方法的更多详细信息,请参阅Regex.Escape Method。
【讨论】:
以上是关于在字符串中搜索具有特殊字符的子字符串[重复]的主要内容,如果未能解决你的问题,请参考以下文章
在 CMD 中使用 enabledelayedexpansion 时如何获取具有特殊字符的子字符串?