使用Regex将原始文本字符串中的子字符串输出到换行符



我有一个名称分隔符,我想用它来提取整行

[string]$testString = $null
# broken text string of text & newlines which simulates $testString = Get-Content -Raw
$testString = "initial text
preliminary text
unfinished line bfore the line I want
001 BOURKE, Bridget Mary ....... ........... 13 Mahina Road, Mahina Bay.Producrs/As 002 BOURKE. David Gerard ...
line after the line I want
extra text
extra extra text"
# test1
# simulate text string before(?<content>.*)text string after - this returns "initial text" only (no newline or anything after)
# $testString -match "(?<BOURKE>.*)"
# test2
# this returns all text, including the newlines, so that $testString outputs exactly as it is defined 
$testString -match "(?s)(?<BOURKE>.*)"
#test3
# I want just the line with BOURKE
$result = $matches['BOURKE']
$result

#Test1找到匹配项,但只打印到换行符#Test2查找匹配项并包含所有换行符。我想知道强制输出开始001 BOURKE ...的正则表达式模式是什么

如有任何建议,我们将不胜感激。

注意:

  • 我假设您正在查找整行BOURKE作为子字符串出现在该行上。

  • 在您自己的尝试中,(?<BOURKE>...)只是给正则表达式捕获组一个自己选择的名称(BOURKE),这与捕获组的子表达式(...)实际匹配的内容无关。

  • 对于手头的用例,根本不需要严格使用(命名的)捕获组,因此下面的解决方案可以不使用捕获组,当使用-match运算符时,这意味着在自动$Matches变量的索引[0]中报告成功匹配的结果,如下所示。


如果多行输入字符串只包含Unix格式的LF换行符(n),请使用以下命令:

if ($multiLineStr -match '.*BOURKE.*') { $Matches[0] }

注:

  • 要敏感地匹配大小写-,请使用-cmatch而不是-match
  • 如果您知道子字符串前面/后面至少有一个字符。,使用.+而不是.*
  • 如果您想逐字逐句地搜索子字符串,并且它恰好或可能包含正则表达式元字符(例如.),请对其应用[regex]::Escape();例如[regex]::Escape('file.txt')产生file.txt(-转义元字符)
  • 如有必要,添加额外的消歧约束,例如要求子串仅在单词边界处开始或结束(b)

如果存在Windows格式CLRF换行符(rn),请使用:

if ($multiLineStr -match '.*BOURKE[^rn]*') { $Matches[0] }

有关正则表达式及其实验能力的解释,请参阅此regex101.com页面(适用于.*BOURKE.*)和此页面(用于.*BOURKE[^rn]*)

简而言之:

  • 默认情况下,.匹配除n之外的任何字符,这完全不需要特定于行的锚(^$),但对于CRLF换行符,需要排除r,以免将其作为匹配的一部分捕获[1]

两个旁白:

  • PowerShell的-match运算符只查找一个匹配项;如果需要查找所有匹配,则当前需要直接使用底层[regex]API;例如,[regex]::Matches($multiLineStr, '.*BOURKE[^rn]*').Value, 'IgnoreCase'
    GitHub问题#7867建议以-matchall运算符的形式将此功能直接引入PowerShell。

  • 如果您想锚定子串以查找,即如果您想规定它发生在一行的开头或结尾,则需要切换到多行模式((?m)),这使得^$每行上匹配;例如,仅当BOURKE出现在一行的最开始时匹配:

    • if ($multiLineStr -match '(?m)^BOURKE[^rn]*') { $Matches[0] }

如果逐行处理是一个选项:

  • 逐行处理的优点是不必担心换行格式的差异(假设处理拆分为行的实用程序可以处理这两种换行格式,PowerShell通常也是如此)。

  • 如果您正在从文件中读取输入文本Select-Stringcmdlet(其目的是查找给定正则表达式或文字子字符串(-SimpleMatch)与其匹配的整行)还提供流式处理,即逐行读取,而无需将整个文件

(Select-String -LiteralPath file.txt -Pattern BOURKE).Line

为区分大小写的匹配添加-CaseSensitive

以下示例模拟了上面的内容(-split 'r?n'将多行输入字符串拆分为单独的行,识别换行格式):

(
@'
initial text
preliminary text
unfinished line bfore the line I want
001 BOURKE, Bridget Mary ....... ........... 13 Mahina Road, Mahina Bay.Producrs/As 002 BOURKE. David Gerard ...
line after the line I want
extra text
extra extra text
'@ -split 'r?n' |
Select-String -Pattern BOURKE
).Line

输出:

001 BOURKE, Bridget Mary ....... ........... 13 Mahina Road, Mahina Bay.Producrs/As 002 BOURKE. David Gerard ...

[1]严格地说,[^rn]*也会在隔离的r字符处停止匹配(即,即使不直接跟在n后面)。如果排除这种情况很重要(这似乎不太可能),请使用Mathias R.Jessen在对问题的评论中建议的正则表达式(的简化版本):.*BOURKE.*?(?=r?n)

我发现最好让一场比赛消耗掉不需要的东西;即CCD_ 45。这可以用集合中具有^的集合命名法来完成,例如[^rn]+,其表示消耗高达rn。因此,所有而不是的内容都是rn

要做到这一点,请使用

$testString -match "(?<Bourke>ddds[^rn]+)"

当知道有可匹配的txt时,也应该尽量避免*。。。CCD_ 52是消耗一切的贪婪类型。一个或多个+的使用大大限制了匹配,因为解析器不必尝试模式(*的零为零或更多),回溯作为其调用,这显然是不可信的。

最新更新