将固定宽度的txt文件转换为CSV / set-content或out-file -append



输入文件为定宽文本文件。我的客户端通常在Excel中打开它并手动指定列分隔符。我希望用逗号替换某些空格,这样我就可以解析为CSV并保存为XLS或其他格式。

$columBreaks = 20, 35, 50, 80, 100, 111, 131, 158, 161, 167, 183
[array]::Reverse($columBreaks) #too lazy to re-write array after finding out I need to iterate in reverse
$files = get-childitem ./ |where-object {$_.Name -like "FileFormat*.txt"}
foreach($file in $files)
{
    $name = $file.Name.split(".")
    $csvFile = $name[0]+".csv"
    if (!(get-childitem ./ |where-object {$_.Name -like $csvFile})) #check whether file has been processed
    { 
        $text = (gc $file) 
        foreach ($line in $text)
        {
           foreach ($pos in $columBreaks)
            {
                #$line.Substring($char-1,3).replace(" ", ",")
                $line = $line.Insert($pos,",")
                #out-file -append?
            }
        } 
    }
    #set-content?
}

那么把这些内容写出来的最有效的方法是什么呢?我曾希望使用set-content,但我认为这是不可能的,因为我们是逐行处理的,所以我认为我要么必须为set-content构建一个行数组,要么为每次迭代使用write-out -append。有没有更有效的方法?

Set-Content只需要稍作调整就可以了。下面是它应该如何工作的一个示例(这是外部foreach循环中的所有内容):

$csvFile = $file.BaseName
    if (!(get-childitem ./ |where-object {$_.Name -like $csvFile})) #check whether file has been processed
    { 
        (gc $file | foreach {
                $_.Insert($columBreaks[0],",").Insert($columBreaks[1],",").Insert($columBreaks[2],",").`
                Insert($columBreaks[3],",").Insert($columBreaks[4],",").Insert($columBreaks[5],",").`
                Insert($columBreaks[6],",").Insert($columBreaks[7],",").Insert($columBreaks[8],",").`
                Insert($columBreaks[9],",").Insert($columBreaks[10],",")
            }) | set-content $csvFile #note parenthesis around everything that gets piped to set-content
    }

顺便说一下,不是在'。',您可以使用$file.BaseName:

获取不带扩展名的名称。
$csvFile = $file.BaseName + ".csv"

我认为这经常出现。这里有一个例子,它实际上走得太远了,把固定宽度文件变成了对象。然后将其导出为csv就很简单了。这应该也适用于转换旧命令,如netstat。

$cols = 0,19,38,59,81,97,120,123 # fake extra column at the end, assumes all rows are that wide, padded with spaces
$colsfile = 'columns.txt'
$csvfile = 'cust.csv'
$firstline = get-content $colsfile | select -first 1
$headers = for ($i = 0; $i -lt $cols.count - 1; $i++) {
  $firstline.substring($cols[$i], $cols[$i+1]-$cols[$i]).trim()
}
# string Substring(int startIndex, int length)                                                                                         
Get-Content $colsfile | select -skip 1 | ForEach {
  $hash = [ordered]@{}
  for ($i = 0; $i -lt $headers.length; $i++) {
    $value = $_.substring($cols[$i], $cols[$i+1]-$cols[$i]).trim()
    $hash += @{$headers[$i] = $value}
  }
  [pscustomobject]$hash
} | export-csv $csvfile

下面是工作代码。修正了一些错误。

CD 'C:\FOLDERPATH'
$filter = "FILE_NAME_*.txt" 
$columns = 11,22,32,42,54 
# DO NOT NEED TO REVERSE [array]::Reverse($columns) #too lazy to re-write array after finding out I need to iterate in reverse
$files = get-childitem ./ |where-object {$_.Name -like $filter}
$newDelimiter = '|'
foreach($file in $files)
{
    $file
    $csvFile = 'C:\FOLDERPATHNEW_' + $file.BaseName + '.txt'
    if (!(get-childitem ./ |where-object {$_.Name -like $csvFile})) #check whether file has been processed
    { 
        $content | ForEach {
            $line = $_
            $counter = 0
            $columns | ForEach {
                $line = $line.Insert($_+$counter, $newDelimiter)  
                $counter = $counter + 1
                }
            $line = $line.Trim($newDelimiter)
            $line
        } | set-content $csvFile
    }
} 

最新更新