【问题标题】:Using pipelining to split a string into a multidimensional array使用流水线将字符串拆分为多维数组
【发布时间】:2018-06-08 14:51:26
【问题描述】:

考虑以下字符串:

$foo = @"
cfa7b63c88ed1eb7443daeb12128084f17e1ac80
85e59563f059ecf45c104bef1eac9d70c22150ed
e207411bdb392a2d99719c221c8dd59e0dbebe26
df61cb22643321198656bfa7061c4e415eefdfef
3611ed35610793e814c8aa25715aa582ec08a8b6
089dfe7ceb9a0845342a9637527de65245ba297f qwerty
17570cc9387755367db0fc1c5c5f4757db7fd9b3 asdfgh
82a1be2b77e949cb45581c4d25bf962f77041846 uiop
0b726925f60c17795d4655f8ee37d51a3de70b87 lkjjh
7ed66867332bf06486117189701278cdabd31da6 zxcv
"@

我想把它拆分成一个数组,这样输出看起来像这样:

[0] => [ "089dfe7ceb9a0845342a9637527de65245ba297f", "qwerty" ]
...
[4] => [ "7ed66867332bf06486117189701278cdabd31da6", "zxcv" ]

换句话说,一个由 5 个元素组成的数组,每个元素都是一个 2 元素数组。请注意,必须排除缺少第二部分的行(即哈希之后的任何内容)。

到目前为止,我得到了这个:

$bar = $foo -split "`r?`n" |?{ $_.length -gt 40 } |%{ $_.split(" ") }

但这会产生一个包含 10 个元素的一维数组。

多玩一点收益:

$bar = $foo -split "`r?`n" |?{ $_.length -gt 40 } |select { $_.split(" ") }

这给了我一个 5 的数组,但该数组中的项目是 PSCustomObjects 和一个名为 NoteProperty$_.split(" "),它本身就是一个包含 2 个字符串的数组。如此接近,但如此遥远 - 如果我能把 NoteProperty 弄平,我会做生意的!

是否有可能实现我的愿望,牢记这些限制:

  • 一切都必须通过流水线完成
  • 没有明确命名哈希表元素等 - 我想要

如果这是不可能的,或者我对 Powershell 及其管道的工作方式存在根本性的误解(我非常怀疑是这种情况),请教育我!

【问题讨论】:

    标签: arrays powershell


    【解决方案1】:

    如果要将数组放入管道并防止 PowerShell 将它们作为单独的管道项删除,则需要通过在数组对象前面放置逗号来强制将其放入 .Net 数组列表:

    PS C:\> $bar = $foo -split "`r?`n" | %{ ,$_.split(" ", 2) }
    PS C:\> $bar[7]
    82a1be2b77e949cb45581c4d25bf962f77041846
    uiop
    PS C:\> $bar[7][1]
    uiop
    

    【讨论】:

    • 这不会删除空的第二个元素$bar =$foo -split '\r?\n'| ? {$_ -match '([0-9a-f]{40}) ([^ ].*)'}|%{ ,$_.split(" ") }
    • 如果文件名中有空格,它也会创建一个锯齿状数组
    • @LotPings,第一条评论;如果第二个元素不存在(除非有拖尾空间),它们确实不会被删除,但这不应该是一个问题,例如$bar[1][1] 返回 $Null(没有错误),在大多数情况下会自动使用其他字符串进行类型转换。如果没有,您可以通过:"$($Bar[1][1])" 将其强制为字符串。对于第二条评论,您可以使用 .Net Split 方法的第二个重载参数将字符串数限制为 2(更新答案),这将在第二个元素中留下空格:$bar[8][1] 现在在您的示例中返回:lkjjh test mess
    • 干得好;要删除单字段行,您可以在 -split "`r?`n" 之后插入 -match ' '
    • 我喜欢 PS,但这些小失误太令人沮丧了!你们都很棒,尤其是@iRon。
    【解决方案2】:

    这有点笨拙,但我认为它符合您的要求:

    $foo = @"
    cfa7b63c88ed1eb7443daeb12128084f17e1ac80
    85e59563f059ecf45c104bef1eac9d70c22150ed
    e207411bdb392a2d99719c221c8dd59e0dbebe26
    df61cb22643321198656bfa7061c4e415eefdfef
    3611ed35610793e814c8aa25715aa582ec08a8b6
    089dfe7ceb9a0845342a9637527de65245ba297f qwerty
    17570cc9387755367db0fc1c5c5f4757db7fd9b3 asdfgh
    82a1be2b77e949cb45581c4d25bf962f77041846 uiop
    0b726925f60c17795d4655f8ee37d51a3de70b87 lkjjh
    7ed66867332bf06486117189701278cdabd31da6 zxcv
    "@
    
    $foo | ForEach-Object {$array = New-Object System.Collections.ArrayList}{
        $match = [Regex]::Match($foo, "(?<code1>\w+) (?<code2>\w+)")
    
        while($match.Success)
        {
            $array.Add(@($match.Groups["code1"].Value, $match.Groups["code2"].Value)) | Out-Null
    
            $match = $match.NextMatch()
        }
    }{$array}
    

    输出是一个数组数组(每个数组都有来自$foo的匹配行),您可以将管道标记到最后一个括号并继续处理。

    【讨论】:

      猜你喜欢
      • 2020-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-15
      • 1970-01-01
      • 1970-01-01
      • 2019-03-15
      • 2012-01-25
      相关资源
      最近更新 更多