在PowerShell中将行转置为列

时间:2017-09-14 06:45:36

标签: powershell

我有一个包含以下内容的源文件:

0
ABC
1
181.12
2
05/07/16
4
Im4thData
5
hello
-1
0
XYZ
1
1333.21
2
02/02/16
3
Im3rdData
5
world
-1
...

' -1'在上面的列表中是记录分隔符,它指示下一条记录的开始。 0,1,2,3,4,5等与列标识符(或列名称)类似。

这是我的代码。

$txt = Get-Content 'C:myfile.txt' | Out-String
$txt -split '(?m)^-1\r?\n' | ForEach-Object {
    $arr = $_ -split '\r?\n'
    $indexes = 1..$($arr.Count - 1) | Where-Object { ($_ % 2) -ne 0 }
    $arr[$indexes] -join '|'
}

上面的代码创建如下输出:

ABC|181.12|05/07/16|Im4thData|hello
XYZ|1333.21|02/02/16|Im3rdData|World
...

但我需要输出如下。如果源文件中没有列,则其行数据应在输出文件中具有如下所示的空白管道线(||)。请告知代码中所需的更改。

ABC|181.12|05/07/16||Im4thData|hello    ← There is no 3rd column in the source file. so blank pipe line (||).
XYZ|1333.21|02/02/16|Im3rdData||World   ← There is no 4th column column in the source file. so blank pipe line (||).
...

2 个答案:

答案 0 :(得分:1)

需要相当多的处理。可能有一种更有效的方法可以做到这一点,但下面的确有效。

$c = Get-Content ".\file.txt"
$rdata = @{}
$data = @()
$i = 0

# Parse the file into an array of key-value pairs
while ($i -lt $c.count) {
   if($c[$i].trim() -eq '-1') {
     $data += ,$rdata
     $rdata = @{}
     $i++
     continue
   }
   $field = $c[$i].trim()
   $value = $c[++$i].trim()
   $rdata[$field] = $value
   $i++
}

# Check if there are any missing values between 0 and the highest value and set to empty string if so
foreach ($row in $data) {
  $top = [int]$($row.GetEnumerator() | Sort-Object Name -descending | select -First 1 -ExpandProperty Name)
  for($i = 0; $i -lt $top; $i++) {
    if ($row["$i"] -eq $null) {
      $row["$i"] = ""
    }
  }
}

# Sort each hash by field order and join with pipe
$data | ForEach-Object { ($_.GetEnumerator() | Sort-Object -property Name | Select-Object -ExpandProperty Value) -join '|' }

while循环中,我们只是迭代文件的每一行。字段编号值由值1分隔,因此每次迭代我们都将两个值都加到哈希值中。

如果我们遇到-1,那么我们知道我们有一个记录分隔符,所以将哈希值添加到数组中,重置它,将计数器碰到下一个记录,然后continue到下一次迭代。< / p>

一旦我们收集了所有我们需要检查是否有任何缺失字段值的内容,我们就会从每个散列中获取最高数字,从0循环遍历它并填充任何缺少的值为空串。

完成后,您可以迭代数组,按字段编号对每个哈希值进行排序并加入值。

答案 1 :(得分:1)

如果您事先知道最大列数,可以执行以下操作:

$cols = 6
$txt = Get-Content 'C:myfile.txt' | Out-String
$txt -split '(?m)^-1\r?\n' | ForEach-Object {
    # initialize array of required size
    $row = ,$null * $cols

    $arr = $_ -split '\r?\n'
    for ($n = 0; $n -lt $arr.Count; $n += 2) {
        $i = [int]$arr[$n]
        $row[$i] = $arr[$n+1]
    }

    $row -join '|'
}

否则你可以这样做:

$txt = Get-Content 'C:myfile.txt' | Out-String
$txt -split '(?m)^-1\r?\n' | ForEach-Object {
    # create empty array
    $row = @()

    $arr = $_ -split '\r?\n'
    $k = 0
    for ($n = 0; $n -lt $arr.Count; $n += 2) {
        $i = [int]$arr[$n]
        # if index from record ($i) is greater than current index ($k) append
        # required number of empty fields
        for ($j = $k; $j -lt $i-1; $j++) { $row += $null }
        $row += $arr[$n+1]
        $k = $i
    }

    $row -join '|'
}