Я пишу сценарий PowerShell
для преобразования коллекции нескольких рабочих листов Excel
xlsx
в один файл csv
.Одна из вещей, которые я хочу взять, - это вычисленный текст гиперссылки, созданной формулой HYPERLINK.Например, ячейка содержит =HYPERLINK(CONCATENATE("http://foo/bar.aspx?pid=",A2),"Click Here")
Я могу захватить ячейку с помощью $currentCell = $sheet.Cells.Item($r, $c)
.Я могу получить текст ссылки Click Here
, используя $currentCell.Text
Я могу определить ячейку с формулой, протестировав $currentCell.HasFormula
.Я могу взять формулу, используя $currentCell.Formula
, и проанализировать ее с помощью регулярного выражения, чтобы обнаружить, что она содержит формулу HYPERLINK
.Но я хочу получить результаты выполнения формулы.Я могу выполнить формулу, используя $currentCell.Calculate()
, но не могу понять, как получить результаты (когда я присваиваю переменную $ currentCell.Calculate () переменной, переменная в итоге становится System.DBNull
).
Как программно получить результаты метода Calculate
ячейки?
Обновление
Подумав об ответе Бенуа Майера,Я понял, что не понял оснований моего собственного вопроса.Я пытался обобщить обработку ячеек, содержащих формулы, но это не сработает.Формула ячейки рассчитывается, т.е. когда я извлекаю текст ячейки (ячейка с формулами HYPERLINK и CONCATENATE), я получаю Click Here
, который является результатом выполнения формулы (например, =HYPERLINK(CONCATENATE("http://foo/bar.aspx?pid=",A2),"Click Here")
).Мне нужно обнаружить и проанализировать формулы HYPERLINK и CONCATENATE и использовать подход, который описывает Бенуа.
Вот мой код.Он преобразует несколько книг Excel, каждая с несколькими листами, и извлекает результаты конкретных формул на листах, которые мне нужно обработать.См. Код после строк 136 и 145.
** Код.Обновлено 5/7 с исправлениями ошибок и кодом для обнаружения и извлечения данных из определенных формул **
cls
#region Functions
Function Remove-WhiteSpaceFromNonQuoted($inString)
{
$quoted = $false
$newString = ""
for ($i = 0; $i -lt $inString.Length; $i++)
{
if ($inString[$i] -eq "`"")
{
$quoted = $quoted -xor $true
}
if (($inString[$i] -match "\S" -and !$quoted) -or ($quoted))
{
$newString = $newString + $inString[$i]
}
}
return $newString
}
#endregion
$sortedFieldNameList = New-Object -TypeName System.Collections.SortedList
$fqBookNames = New-Object -TypeName System.Collections.SortedList
$fqBookNames.Add("C:\foo\bar1.xlsx", "")
$fqBookNames.Add("C:\foo\bar2.xlsx", "")
$fqBookNames.Add("C:\foo\barN.xlsx", "")
$global:workBook = $null
$global:excel = $null
try
{
$global:excel = New-Object -Com Excel.Application
$global:excel.Visible = $false
write-host ("Scan for column names")
#Scan all sheets in all books and create an object with all the column names encountered
foreach ($fqBookName in $fqBookNames.Keys)
{
$global:workBook = $global:excel.Workbooks.Open($fqBookName)
foreach ($sheet in $global:workBook.Sheets)
{
$columnIndexMax = $sheet.UsedRange.Column + $sheet.UsedRange.Columns.Count - 1
write-host ("Workbook=" + $global:workBook.Name + ". Sheet=" + $sheet.Name)
$rowOne = $sheet.Rows(1)
for ($columnIndex = 1; $columnIndex -le $columnIndexMax; $columnIndex++)
{
$columnName = $rowOne.Cells($columnIndex).Text.Trim().ToUpper()
if ($columnName.Length -gt 0)
{
if (!$sortedFieldNameList.ContainsKey($columnName))
{
$sortedFieldNameList.Add($columnName, "")
}
}
else
{
break
}
}
}
$global:workBook.Close($false)
Clear-Variable workBook
}
#Create a class that represents the worst-case collection of columns that will be output to, e.g., a grid or CSV file
#https://stackoverflow.com/questions/49117127/create-a-class-with-dynamic-property-names-in-powershell
Invoke-Expression @"
Class ClsExportCsv {
$(($sortedFieldNameList.Keys).ForEach({"[string] `${$($_)}`n "}))
}
"@
#create array to hold list of rows that will be output to, e.g., a grid or CSV file
$itemList = New-Object System.Collections.ArrayList
$itemList.clear()
write-host ("Scan for data")
foreach ($fqBookName in $fqBookNames.Keys)
{
$global:workBook = $global:excel.Workbooks.Open($fqBookName)
foreach ($sheet in $global:workBook.Sheets)
{
write-host -NoNewline ("Workbook=" + $global:workBook.Name + ". Sheet=" + $sheet.Name + ". Rows=")
$columnNameLookup = @{}
$columnNameLookup.Clear()
$columnIndexMax = $sheet.UsedRange.Column + $sheet.UsedRange.Columns.Count - 1
$rowOne = $sheet.Rows(1)
#create column name index lookup table for this sheet
for ($columnIndex = 1; $columnIndex -le $columnIndexMax; $columnIndex++)
{
$columnNameLookup.Add($columnIndex, $rowOne.Cells($columnIndex).Text.Trim().ToUpper())
}
for ($rowIndex = 2; $rowIndex -le $sheet.Cells.EntireRow.Count; $rowIndex++)
{
$rowCurrent = $sheet.Rows($rowIndex)
if (($rowCurrent.Cells(1).Text).Length -gt 0)
{
$listRow = New-Object -TypeName ClsExportCsv
for ($columnIndex = 1; $columnIndex -le $columnIndexMax; $columnIndex++)
{
if (($columnNameLookup.$columnIndex).Length -gt 0)
{
$cellObject = $rowCurrent.Cells($columnIndex)
$textFromFormula = ""
if ($cellObject.HasFormula)
{
$formulaNoWhiteSpace = Remove-WhiteSpaceFromNonQuoted -inString $cellObject.Formula
#detect and parse cells with =HYPERLINK(CONCATENATE("http://xxxx.aspx?pid=",A2),"Click Here")
if ($formulaNoWhiteSpace -match '^(?:\=HYPERLINK\(CONCATENATE\(\")(?<URL>.*)(?:\"\,)(?<A1>.*)(?:\)\,.*)$')
{
if (($Matches["URL"] -ne $null) -and ($Matches["A1"] -ne $null))
{
$textFromFormula = ($Matches["URL"] + $sheet.Range($Matches["A1"]).Text)
}
}
#detect and parse cells with =HYPERLINK("http://xxxx","Click Here")
if ($formulaNoWhiteSpace -match '^(?:\=HYPERLINK\(\")(?<URL>.*)(?:\"\,\".*\"\))$')
{
if ($Matches["URL"] -ne $null)
{
$textFromFormula = $Matches["URL"]
}
}
}
if ($textFromFormula.Length -eq 0)
{
$listRow.($columnNameLookup.$columnIndex) = $rowCurrent.Cells($columnIndex).Text.Trim()
}
else
{
$listRow.($columnNameLookup.$columnIndex) = $textFromFormula
}
} # if (($columnNameLookup.$columnIndex).Length -gt 0)
} # for ($columnIndex = 1; ...
$itemList.Add($listRow) | out-null
}
else
{
write-host ($rowIndex - 2).ToString()
break
}
} # for ($rowIndex = 2; .....
} # foreach ($sheet in $global:workBook.Sheets)
$global:workBook.Close($false)
Clear-Variable workBook
}
$global:excel.Quit()
Clear-Variable excel
$itemList | Export-CSV -LiteralPath "C:\Users\foo\combined.csv" -NoTypeInformation -Encoding UTF8 -Delimiter ',' $itemList | Out-GridView -Title "Rows"
}
finally
{
if ($global:excel -ne $null)
{
if ($global:workBook -ne $null)
{
$global:workBook.Close($false)
}
$global:excel.Quit()
Clear-Variable excel
}
}