PowerShell反向CSV数据

时间:2014-08-19 06:51:22

标签: excel powershell csv

在网络上,我发现此功能可以从PowerShell中的Excel工作表导入数据。我试图加强它,但效果很好。但是,我需要采取的最后一个障碍是扭转数据。由于并非Excel工作表中的所有数据都是垂直对齐的,有时也是水平对齐的。我想通过一个例子可能会更清楚。

' Test.xlsx'

的示例
Below you will find all the parameters:
Header 1 | Text 1 | Text 11
Header 2 | Text 2 | Text 22
Header 3 | Text 3 | Text 33

目前错误的结果:

Header 1  Text 1  Text 11
--------  ------  -------
Header 2  Text 2  Text 22
Header 3  Text 3  Text 33

期望的结果:

Header 1  Header 2  Header 3
--------  --------  --------
Text 1    Text 2    Text 3
Text 11   Text 22   Text 33

正如您所看到的,在某种程度上,我需要能够反转数据列和标题以使其全部正确。如果能够以Switch的方式将其添加到函数中,那将是很好的。因此,当它不需要时,它仍然能够以旧方式导入行和列。

功能:

Function Import-Excel {

    Param (
        [parameter(Mandatory=$true,Position=0)]
        [ValidateScript({Test-Path $_ -PathType Leaf })]
        [String]$FileName, 
        [parameter(Mandatory=$true,Position=1)]
        [String]$WorksheetName,
        [parameter(Mandatory=$false,Position=2)]
        [Int]$SkipLines
    )

    $csvFile = Join-Path $env:temp ("{0}.csv" -f (Get-Item -path $FileName).BaseName)
    if (Test-Path -path $csvFile) { Remove-Item -path $csvFile }

        Function FindSheet([Object]$workbook, [string]$name)
        {
            $sheetNumber = 0
            for ($i=1; $i -le $workbook.Sheets.Count; $i++) {
                if ($name -eq $workbook.Sheets.Item($i).Name) { $sheetNumber = $i; break }
            }
            return $sheetNumber
        }

        Function SetActiveSheet([Object]$workbook, [string]$name)
        {
            if (!$name) { return }
            $sheetNumber = FindSheet $workbook $name
            if ($sheetNumber -gt 0) { $workbook.Worksheets.Item($sheetNumber).Activate() }
            return ($sheetNumber -gt 0)
        }

        # convert Excel file to CSV file
        $xlCSVType = 6 # SEE: http://msdn.microsoft.com/en-us/library/bb241279.aspx
        $excelObject = New-Object -ComObject Excel.Application  
        $excelObject.Visible = $false 
        $workbookObject = $excelObject.Workbooks.Open($FileName)

        # check if worksheet exists
        foreach ($sheet in $workbookObject.Worksheets) {
            if ($Sheet.Name -eq $WorksheetName) {
                $SheetAvailable = $true
            }
        }
        if (-not $SheetAvailable) {
            $workbookObject.Close()
            $excelObject.Quit()
            throw "Import-Excel: Worksheet '$WorksheetName' not found in workbook '$FileName'"
        }

        SetActiveSheet $workbookObject $WorksheetName | Out-Null
        $workbookObject.SaveAs($csvFile,$xlCSVType) 
        $workbookObject.Saved = $true
        $workbookObject.Close()

         # cleanup 
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbookObject) | Out-Null
        $excelObject.Quit()
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excelObject) | Out-Null
        [System.GC]::Collect()
        [System.GC]::WaitForPendingFinalizers()

        # import and return the data 
        $Result = Get-Content -Path $csvFile | Select-Object -Skip $SkipLines | Where {$_ -notmatch "^[,]+$"} | ConvertFrom-Csv
        Write-Output $Result
}

Import-Excel C:\Test.xlsx -WorksheetName 'Parameters' -SkipLines 1

一如既往,感谢您的帮助。

解决方案感谢TheMadTechnician:

Function Import-Excel {

    [CmdletBinding(SupportsShouldProcess=$True)]
    Param (
        [parameter(Mandatory=$true,Position=0)]
        [ValidateScript({Test-Path $_ -PathType Leaf })]
        [String]$FileName, 
        [parameter(Mandatory=$true,Position=1)]
        [String]$WorksheetName,
        [parameter(Mandatory=$false,Position=2)]
        [Int]$SkipLines=0,
        [Switch]$Reverse
    )
    BEGIN {
        $csvFile = Join-Path $env:temp ("{0}.csv" -f (Get-Item -path $FileName).BaseName)
        if (Test-Path -path $csvFile) { Remove-Item -path $csvFile }
    }
    PROCESS {
        # Convert Excel file to CSV file
        $xlCSVType = 6 # SEE: http://msdn.microsoft.com/en-us/library/bb241279.aspx
        $excelObject = New-Object -ComObject Excel.Application  
        $excelObject.Visible = $false 
        $workbookObject = $excelObject.Workbooks.Open($FileName)

        # Check if worksheet exists
        $ws1 = $workbookObject.worksheets | where {$_.name -eq $WorksheetName}
        if ($ws1 -ne $Null) {
            $ws1.Activate()
        }
        else {
            $workbookObject.Close($false)
            $excelObject.Quit()
            throw "Import-Excel: Worksheet '$WorksheetName' not found in workbook '$FileName'"
        }

        If($Reverse){
            $usedRange = $workbookObject.ActiveSheet.UsedRange
            $ws = $workbookObject.ActiveSheet

            # Remove first lines
            for ($i = 1; $i -le $SkipLines; $i++) {
                [void]$ws.Cells.Item(1, 1).EntireRow.Delete()
            }

            # Remove empty lines
            $lastCell = $usedRange.SpecialCells(11) 
            $row = $lastCell.row 

            for ($i = 1; $i -le $row; $i++) {
                If ($ws.Cells.Item($i, 1).Value() -eq $Null) {
                    $Range = $ws.Cells.Item($i, 1).EntireRow
                    $Range.Delete() | Out-Null
                }
            }

            $usedRange.Copy() | Out-Null
            $newWorkbookObject = $excelObject.Workbooks.Add()
            $newWorkbookObject.ActiveSheet.Range("A1").PasteSpecial(12,-4142,$false,$true)| Out-Null

            $newWorkbookObject.SaveAs($csvFile,$xlCSVType) 
            $newWorkbookObject.Saved = $true
            $newWorkbookObject.Close()
            $workbookObject.Close($false)

            # Pause to let the CSV be written
            While(!(Test-Path $csvFile)){Start-Sleep -Milliseconds 50}

            # Import and return the data 
            $Result = Get-Content -Path $csvFile | ConvertFrom-Csv
            Write-Output $Result
        }
        Else{
            $WorkbookObject.SaveAs($csvFile,$xlCSVType) 
            $WorkbookObject.Saved = $true
            $workbookObject.Close()

            # Pause to let the CSV be written
            While(!(Test-Path $csvFile)){Start-Sleep -Milliseconds 50}

            # Import and return the data 
            $Result = Get-Content -Path $csvFile | Select-Object -Skip $SkipLines | Where {$_ -notmatch "^[,]+$"} | ConvertFrom-Csv
            Write-Output $Result
        }
        # Cleanup 
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbookObject) | Out-Null
        If($Reverse){[System.Runtime.Interopservices.Marshal]::ReleaseComObject($newWorkbookObject) | Out-Null}
        $excelObject.Quit()
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excelObject) | Out-Null
        [System.GC]::Collect()
        [System.GC]::WaitForPendingFinalizers()
    }
}

Import-Excel C:\Test.xlsx -WorksheetName 'Parameters' -Reverse -SkipLines 1

2 个答案:

答案 0 :(得分:1)

我找到了解决问题的方法。将其添加到参数部分[Switch]$Reverse并用以下代码替换代码的结尾:

# import and return the data 
$Result = Get-Content -Path $csvFile | Select-Object -Skip $SkipLines | Where {$_ -notmatch "^[,]+$"} 

if ($Reverse) {
    $Result | ForEach-Object {$Header += "$($_.Split(',')[0]),"; $Content += "$($_.Split(',')[1]),"}
    $Result = "$Header`n$Content"
}

$Obj = $Result | ConvertFrom-Csv
Write-Output $Obj

答案 1 :(得分:1)

我认为更清洁的解决方案是使用UsedRange,复制它,使用Transpose特殊粘贴,然后像往常一样处理它。以下是您修改的整个功能:

Function Import-Excel {

    Param (
        [parameter(Mandatory=$true,Position=0)]
        [ValidateScript({Test-Path $_ -PathType Leaf })]
        [String]$FileName, 
        [parameter(Mandatory=$true,Position=1)]
        [String]$WorksheetName,
        [parameter(Mandatory=$false,Position=2)]
        [Int]$SkipLines=0,
        [Switch]$Reverse
    )

    $csvFile = Join-Path $env:temp ("{0}.csv" -f (Get-Item -path $FileName).BaseName)
    if (Test-Path -path $csvFile) { Remove-Item -path $csvFile }

        Function FindSheet([Object]$workbook, [string]$name)
        {
            $sheetNumber = 0
            for ($i=1; $i -le $workbook.Sheets.Count; $i++) {
                if ($name -eq $workbook.Sheets.Item($i).Name) { $sheetNumber = $i; break }
            }
            return $sheetNumber
        }

        Function SetActiveSheet([Object]$workbook, [string]$name)
        {
            if (!$name) { return }
            $sheetNumber = FindSheet $workbook $name
            if ($sheetNumber -gt 0) { $workbook.Worksheets.Item($sheetNumber).Activate() }
            return ($sheetNumber -gt 0)
        }

        # convert Excel file to CSV file
        $xlCSVType = 6 # SEE: http://msdn.microsoft.com/en-us/library/bb241279.aspx
        $excelObject = New-Object -ComObject Excel.Application  
        $excelObject.Visible = $false 
        $workbookObject = $excelObject.Workbooks.Open($FileName)

        # check if worksheet exists
        foreach ($sheet in $workbookObject.Worksheets) {
            if ($Sheet.Name -eq $WorksheetName) {
                $SheetAvailable = $true
            }
        }
        if (-not $SheetAvailable) {
            $workbookObject.Close()
            $excelObject.Quit()
            throw "Import-Excel: Worksheet '$WorksheetName' not found in workbook '$FileName'"
        }

        SetActiveSheet $workbookObject $WorksheetName | Out-Null
        If($Reverse){
            $usedRange = $workbookObject.ActiveSheet.UsedRange
            $usedRange.Copy() | Out-Null
            $newWorkbookObject = $excelObject.Workbooks.Add()
            $newWorkbookObject.ActiveSheet.Range("A1").PasteSpecial(12,-4142,$false,$true)|out-null
            $newWorkbookObject.SaveAs($csvFile,$xlCSVType) 
            $newWorkbookObject.Saved = $true
            $newWorkbookObject.Close()
        }Else{
            $WorkbookObject.SaveAs($csvFile,$xlCSVType) 
            $WorkbookObject.Saved = $true
            $workbookObject.Close()
        }

         # cleanup 
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbookObject) | Out-Null
        If($Reverse){[System.Runtime.Interopservices.Marshal]::ReleaseComObject($newWorkbookObject) | Out-Null}
        $excelObject.Quit()
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excelObject) | Out-Null
        [System.GC]::Collect()
        [System.GC]::WaitForPendingFinalizers()

        # Pause to let the CSV be written...
        While(!Test-Path $csvFile){Start-Sleep -Milliseconds 50}

        # import and return the data 
        $Result = Get-Content -Path $csvFile | Select-Object -Skip $SkipLines | Where {$_ -notmatch "^[,]+$"} | ConvertFrom-Csv
        Write-Output $Result
}

Import-Excel C:\Test.xlsx -WorksheetName 'Parameters' -SkipLines 1

好的,原来我错过了.ActiveSheet中的$UsedRange = $workbookObject.ActiveSheet.UsedRange,但现在已经修好了。我还设置了一个While循环,以确保在我们尝试为$Result =行解析CSV之前写入CSV。我还在PasteSpecial行之后添加了一个out-null,以禁止屏幕上的True响应。这是有效的,我刚刚遇到了初始问题,因为我正在测试的XLSX上有空白行,当转置时会导致空标题,这并不酷。另外,我在G1,H1和I1中具有相同的值,因此它尝试对三列使用相同的标题,并且默认失败,因此它只会响应任何内容。没有错误,没有。令人沮丧地排除故障。无论如何,我测试了这个,它对我有用。