搜索字符串中包含特殊字符的子字符串

问题描述 投票:0回答:2

我正在搜索字符串中包含特殊字符的子字符串。如何在字符串中搜索子字符串。

$path = 'c:\test'
$mountpoint = 'c:\test\temp\20190987-120\'

我想在$path中搜索$mountpoint

我尝试过使用-match-contains-in等。

PS C:\>$path = 'c:\test'
PS C:\>$mountpoint = 'c:\test\temp\20190987-120\'
PS C:\>$path -contains $mountpoint
False
powershell substring string-matching
2个回答
0
投票

AdminOfThing's answer很有帮助,但我发现自己希望以不同的方式构建框架。

  • 您正在寻找一种方法来执行在开始时锚定的文字子字符串搜索,这只是在PowerShell中间接支持 - 请参阅下一节的解决方案。
  • 运算符-contains-in与子串匹配无关(尽管-containsString.Contains() .NET方法之间的名称相似)。 它们通过逐个元素的相等比较(隐含的-eq)测试集合中单个值的成员资格(作为一个整体包含)。有关详细信息,请参阅docsthis answer的底部。 如果你想要结合两个任务 - 在集合的所有元素中寻找子字符串 - 你可以利用PowerShell的-match-like操作符(下面讨论)也可以对集合值LHS进行操作,在这种情况下他们充当过滤器;虽然这与成员资格测试不完全相同,但它可以有效地用于此; this answer展示了如何使用-match

Solutions:

使用.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在许多(但不是全部)上下文中使用不变量而不是当前文化,例如运算符-eq-contains-inswitch语句。

如果不需要锚定子字符串搜索,即,如果您只想知道子字符串是否包含在输入字符串中的某个位置,则可以使用String.Contains()

 PS> 'foo\bar'.Contains('oo\')   #'# substring is present, but not at the start
 True

关于区分大小写的上述观点也适用于此。


使用-match运算符:

虽然-match隐式执行子串匹配,但它基于正则表达式(regular expression)而不是文字字符串。

-match默认执行不区分大小写的匹配;使用-cmatch变体区分大小写。

这意味着您可以方便地使用^(输入开始锚点)来确保搜索表达式仅匹配输入字符串的开头。

相反,为了将您的搜索字符串视为正则表达式中的文字字符串,您必须在正则表达式中\-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执行全字符串匹配,并基于wildcard expressions(在Unix世界中的a.k.a globs)执行此操作;虽然与正则表达式有很大关系,但它们使用更简单,不兼容的语法(并且功能强大得多)。

-like默认执行不区分大小写的匹配;使用-clike变体区分大小写。

通配符只有3个基本结构,因此只有3个元字符:?(匹配单个字符。),*(匹配任意数量的字符,包括无字符)和[(字符集的开头或匹配单个字符的范围) char。,例如,[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

1
投票

在这种特殊情况下,您可以使用-Match

$mountpoint -match [regex]::escape($path)

这里的问题是\字符。它是正则表达式模式中的特殊字符,需要进行转义。由于-Match运算符进行正则表达式匹配,因此需要考虑特殊字符。我选择在这种情况下使用Escape()方法。你可以使用像\这样的c:\\test字符单独转义字符。 LotPings评论重申了这一想法。

使用正则表达式匹配,您可以控制您想要进行多少匹配。您可以包含锚点和其他特殊字符来定制您的匹配。 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上。

示例使用-Co​​ntains和-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返回,因为尾随的\字符使其与集合中的任何元素不同。

有关About_Comparison_Operators的信息,请参阅-Match,有关Regex.Escape Method方法的更多详细信息,请参阅Escape()

© www.soinside.com 2019 - 2024. All rights reserved.