从文件中过滤出仅为大写的单词。

3
我有一个名为output.txt的文件,其中包含约1000个单词,看起来像这样:
SESSIONDAYOFWEEK FILMTITLELONGALT tblTrans_Ticket. ADMITDETAILSALT2 MESSAGESTUB2ALT3 StartDayOfWeek Description MESSAGESTUB2ALT2 FILMTITLESHORTALT Applications TICKETTYPELONGALT
我需要过滤该文件,只选择仅包含大写字符的单词,并且摆脱那些具有小写字符的单词。
我在PowerShell中运行以下命令:
Get-Content .\out.txt | ForEach-Object if ($_.IsUpper) {Write-Host $_}

并且shell逐个解析所有单词,并为每个单词打印:

ForEach-Object:输入名称“if”无法解析为方法。
在第1行第25个字符处:
+ ... et-Content .\out.txt | ForEach-Object if ($_.IsUpper) {Write-Host $_}
+                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (TAIL:PSObject) [ForEach-Object],PSArgumentException
    + FullyQualifiedErrorId : MethodNotFound,Microsoft.PowerShell.Commands.ForEachObjectCommand

我不明白我哪里错了?

3个回答

5

使用 -cmatch 操作符来针对正则表达式 (regular expression) 进行区分大小写的匹配:

Get-Content .\out.txt | Where-Object { $_ -cmatch  '^\p{Lu}+$' }
  • -cmatch-match 操作符的区分大小写版本(别名为 -imatch)。由于 -match 是不区分大小写的,因此必须使用 -cmatch 来检测大小写区别。

  • \p{Lu} 匹配单个大写字母 - 包括重音符号的非 ASCII 字符,例如 Ü[1] - 并添加 + 匹配一个或多个连续的字符。将表达式括在 ^ (字符串开头)和 $ (字符串结尾)中意味着仅匹配完全由大写字符组成的行。

    • Ansgar Wiechers 建议改用 -cnotmatch '\p{Ll}',它的工作方式略有不同:它会消除包含 至少一个小写字母 的行,这意味着即使行中包含非字母字符(只要没有小写字母),也会保留这些行。

使用 Select-String 的另一种选择,可能更有效率:

Select-String -CaseSensitive '^\p{Lu}+$' .\out.txt | Select-Object -ExpandProperty Line

Select-String 默认不区分大小写(与 PowerShell 一般情况相同),因此需要使用
-CaseSensitive 开关。

请注意,尽管其名称是 Select-String,但截至 PowerShell Core 6.1.0 版本,它不支持直接输出匹配的行;相反,它会输出包含匹配信息的对象,其中的 .Line 属性包含了匹配的行,因此需要使用 Select-Object -ExpandProperty Line
这个 GitHub 问题建议增加一个新的开关参数以支持直接输出匹配的字符串。


至于你尝试的内容:

ForEach-Object 命令执行的代码必须作为一个脚本块传递 - 即,一段被 { ... } 包围的代码。

你没做到这一点,导致了你看到的语法错误。

另外,[string] 类型(.NET 字符串)没有 .IsUpper() 方法(即使有,你也忘记在 .IsUpper 后面加上 () 了)。

只有 [char] 类型有一个 .IsUpper() 方法,即一个静态方法,你可以按如下方式调用它:[char]::IsUpper('A') - 但你必须在循环中为输入字符串的每个字符调用此方法:

Get-Content .\out.txt | Where-Object { 
  foreach ($c in $_.ToCharArray()) { if (-not [char]::IsUpper($c)) { return $False } }
  $True
}

最后,不要使用Write-Host来返回结果 - Write-Host仅在控制台上打印 - 您将无法捕获或重定向此类输出[2]。相反,请使用Write-Output或更好地依赖于PowerShell的隐式输出行为:仅使用$_作为自己的语句将输出它 - 任何您既不捕获也不重定向的表达式或命令都会自动输出(发送到成功输出流)。


[1] 相比之下,使用字符范围表达式[A-Z]只能识别ASCII范围(英文)大写字母。

[2] 在PSv4-中从未如此,但在PSv5+中您可以通过额外的努力实现 - 但关键是Write-Host不适用于输出结果(数据)。


4
最简单的方法可能是使用正则表达式。
Get-Content .\out.txt | Where-Object { $_ -cmatch "\b[A-Z0-9_]+\b" }
Where-Object作为过滤器,允许匹配的内容通过并丢弃不匹配的内容。 -cmatch将进行区分大小写的正则表达式匹配。
正则表达式解释: +量词——匹配一次或多次,尽可能多地匹配(贪婪模式)。 A-Z范围内的单个字符为从A(索引65)到Z(索引90)之间的字符。 0-9范围内的单个字符为从0(索引48)到9(索引57)之间的字符。 _与字符_匹配。 \b断言在单词边界处。
如果您不想允许带有这些字符的单词通过过滤器,则可以删除0-9_
参见:https://regex101.com/r/CfgEmU/1

4
好的回答,不过更简单的方法可能是只排除任何带有小写字母的内容,例如 |?{$_ -cnotmatch '[a-z]'} - TheMadTechnician

1

你好,Francesco Mantovani,

正如其他人所提到的那样,[string] 类型没有 .IsUpper 属性。 [char] 类型有一个 .IsUpper() 方法,但它也缺少一个同名的 属性。[笑脸]

你可以通过以下方式测试所有大写字母的数组项...

$Collection.Where({$_ -ceq $_.ToUpper()})

希望这有所帮助,

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接