【问题标题】:Powershell work with multiline text in Excel cellPowershell 使用 Excel 单元格中的多行文本
【发布时间】:2016-12-11 02:48:42
【问题描述】:

我有一个相当简单的请求(对我来说这是一项相当艰巨的任务)。

我有两个 CSV 文件,我想将它们转换为 Excel,因此这两个 CSV 文件中的每一个都将占据一张纸。 到目前为止,我已经成功了,但是我有一个小问题要纠正。

CSV 中的一个单元格包含多个文本行,如下所示:

这是条目 1

这是条目 2

我希望将这两个条目导入 Excel 单元格,就像在 CSV 中一样,但是当我检查我的 Excel 文件时,第二个条目被导入下一行:

第 1 行单元格 1 - 这是条目 1

第 2 行单元格 1 - 这是条目 2

我不知道我是否应该使用 .NET 类 worksheet.UsedRange.EntireRow 或 worksheet.UsedRange.EntireColumn 或其他东西。 我正在查看 MSDN,但由于我仍然是菜鸟,所以我找不到任何东西。

这是我的代码示例:

Function Merge-CSVFiles
{
Param(
$CSVPath = ".\Reports", 
$XLOutput=".\final_final_report.xlsx"
)

$csvFiles = Get-ChildItem ("$CSVPath\*") -Include *.csv
$Excel = New-Object -ComObject excel.application
$Excel.visible = $false
$Excel.sheetsInNewWorkbook = $csvFiles.Count
$workbooks = $excel.Workbooks.Add()
$CSVSheet = 1

Foreach ($CSV in $Csvfiles)

{
$worksheets = $workbooks.worksheets
$CSVFullPath = $CSV.FullName
$SheetName = ($CSV.name -split "\.")[0]
$worksheet = $worksheets.Item($CSVSheet)
$worksheet.Name = $SheetName
$TxtConnector = ("TEXT;" + $CSVFullPath)
$CellRef = $worksheet.Range("A1")
$Connector = $worksheet.QueryTables.add($TxtConnector,$CellRef)
$worksheet.QueryTables.item($Connector.name).TextFileCommaDelimiter = $True
$worksheet.QueryTables.item($Connector.name).TextFileParseType  = 1
$worksheet.QueryTables.item($Connector.name).Refresh()
$worksheet.QueryTables.item($Connector.name).delete()
$worksheet.UsedRange.EntireColumn.AutoFit()
$CSVSheet++

}

$workbooks.SaveAs($XLOutput,51)
$workbooks.Saved = $true
$workbooks.Close()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbooks) | Out-Null
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

}

【问题讨论】:

  • 嗯,似乎直接在 Excel 中打开 csv 时,您可以将整个条目括在引号中,并且 excel 会将换行符识别为同一条目的一部分 (stackoverflow.com/questions/566052/…)。因此,您可以预处理 CSV 并将条目用引号括起来。但即便如此,您使用 QueryTables 的方法也无法正确识别这一点。 Excel 自己的数据导入工具也不会 - 我可以让它工作的唯一方法是在 Excel 中将 .csv 作为新工作簿打开。
  • 我能想到的最佳解决方案:对引用所有数据进行预处理(例如,用“,”替换、替换),然后使用 Workbooks.Open 单独打开所有 .csv 文件。全部打开后,通过从所有工作簿复制到主工作簿来整合数据。
  • 否则预处理数据以用特殊代码替换换行符的实例,以便 excel 将整个内容视为一个条目,然后在导入后您可以后处理将此代码替换回换行符。

标签: excel vba csv powershell


【解决方案1】:

我不确定您使用的方法为什么会这样,但我也能够在我的系统上重现。我在我的一个构建大型 Excel 工作簿的脚本中做了一些类似的操作,我成功地使用了以下方法:

  1. 使用 Import-CSV 导入 CSV
  2. 将其转换为多维数组
  3. 将数组写入范围对象的Value2

例如,替换如下所示的代码:

$TxtConnector = ("TEXT;" + $CSVFullPath)
$CellRef = $worksheet.Range("A1")
$Connector = $worksheet.QueryTables.add($TxtConnector,$CellRef)
$worksheet.QueryTables.item($Connector.name).TextFileCommaDelimiter = $True
$worksheet.QueryTables.item($Connector.name).TextFileParseType  = 1
$worksheet.QueryTables.item($Connector.name).Refresh()
$worksheet.QueryTables.item($Connector.name).delete()

用这个代替:

$CsvContents = Import-Csv $CSVFullPath
$MultiArray = (ConvertTo-MultiArray $CsvContents -Headers).Value
$StartRowNum = 1
$StartColumnNum = 1
$EndRowNum = $CsvContents.Count + 1
$EndColumnNum = ($CsvContents | Get-Member | Where-Object { $_.MemberType -eq 'NoteProperty' }).Count
$Range = $worksheet.Range($worksheet.Cells($StartRowNum, $StartColumnNum), $worksheet.Cells($EndRowNum, $EndColumnNum))
$Range.Value2 = $MultiArray

为此,您还需要我用于将对象转换为多维数组的函数(基于发布的 here 但稍作修改):

function ConvertTo-MultiArray
{
    param (
        $InputObject,
        [switch]$Headers = $false
    )
    begin
    {
        $Objects = @()
        [ref]$Array = [ref]$null
    }
    process
    {
        $Objects += $InputObject
    }
    end
    {
        $Properties = $Objects[0].PSObject.Properties | ForEach-Object{ $_.Name }
        $Array.Value = New-Object 'object[,]' ($Objects.Count + 1), $Properties.Count
        $ColumnNumber = 0
        if ($Headers)
        {
            $Properties | ForEach-Object{
                $Array.Value[0, $ColumnNumber] = $_.ToString()
                $ColumnNumber++
            }
            $RowNumber = 1
        }
        else
        {
            $RowNumber = 0
        }
        $Objects | ForEach-Object{
            $Item = $_
            $ColumnNumber = 0
            $Properties | ForEach-Object{
                if ($Item.($_) -eq $null)
                {
                    $Array.Value[$RowNumber, $ColumnNumber] = ""
                }
                else
                {
                    $Array.Value[$RowNumber, $ColumnNumber] = $Item.($_).ToString()
                }
                $ColumnNumber++
            }
            $RowNumber++
        }
        $Array
    }
}

【讨论】:

  • 你好。乔恩,我已经尝试过你的解决方案,它奏效了 :) 非常感谢,你很可能为我节省了几个小时用头撞墙的时间。我得到了我想要的结果,当我将 CSV 文件中的单元格中的多行文本移入 Excel 时,它仍保留在一个单元格中。
  • 太棒了!我很高兴我在尝试自动执行 Excel 任务时所遭受的数小时的痛苦使其他人受益。 :)
猜你喜欢
  • 1970-01-01
  • 2013-04-15
  • 2021-01-27
  • 2015-10-23
  • 2015-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-28
  • 2021-12-10
相关资源
最近更新 更多