【问题标题】:Filter only uppercase words from file仅过滤文件中的大写单词
【发布时间】:2019-03-10 07:35:37
【问题描述】:

我有一个 output.txt 文件,其中包含大约 1000 个单词,如下所示:

每周会议 电影长篇 tblTrans_Ticket。 承认详情ALT2 MESSAGESTUB2ALT3 StartDayOfWeek 描述 MESSAGESTUB2ALT2 电影短片 应用 门票类型LONGALT

我需要过滤那个文件,只选择只有大写字符的单词,并去掉那些有小写字符的单词。

我在 PowerShell 中运行这个命令:

Get-Content .\out.txt | ForEach-Object if ($_.IsUpper) {Write-Host $_}

shell 会一一解析所有单词,并为每个单词打印我:

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

我不明白我哪里错了?

【问题讨论】:

    标签: regex powershell foreach


    【解决方案1】:

    你好弗朗切斯科·曼托瓦尼,

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

    您可以测试所有大写的数组项...

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

    希望对你有帮助,

    【讨论】:

      【解决方案2】:

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

      Get-Content .\out.txt | Where-Object { $_ -cmatch  '^\p{Lu}+$' }
      
      • -cmatch-match operator(别名为-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
      This GitHub issue 建议添加一个新的开关参数以支持直接输出匹配的字符串。


      至于你尝试了什么

      ForEach-Object cmdlet 执行的代码必须作为 脚本块 传递 - 即包含在 { ... } 中的一段代码。

      您忽略了这样做,导致您看到的语法错误。

      另外,[string] 类型(一个 .NET 字符串)没有.IsUpper() 方法(即使有,您也忘记了().IsUpper 之后)。

      只有[char] 类型具有.IsUpper() 方法,即static 方法,您可以按如下方式调用它:[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 的 implicit 输出行为:简单地使用 $_ 作为它自己的语句将输出它 - 你既不捕获也不重定向的任何表达式或命令都是自动输出(发送到成功输出流)。


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

      [2] 在 PSv4- 中永远不会,但在 PSv5+ 中您可以通过额外的努力 - 但关键是 Write-Host 不用于输出结果(数据)。支持>

      【讨论】:

        【解决方案3】:

        最简单的方法可能是使用正则表达式。

        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

        【讨论】:

        • 很好的答案,尽管排除任何带有小写字母的东西可能更简单,例如|?{$_ -cnotmatch '[a-z]'}
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-10-06
        • 2019-06-15
        • 2022-08-03
        • 2021-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多