Convert multiple Excel workbooks each with multiple worksheets to CSV: How do I extract a url that is constructed by a formula?
我正在编写一个
我可以用
如何以编程方式获取单元格的
更新
考虑了贝诺·梅耶(Beno?t Mayer)的回答后,我意识到我不理解我自己问题的基础。我试图对包含公式的单元格的处理进行一般化,但这是行不通的。正在计算单元格的公式,即,当我提取单元格的文本(具有HYPERLINK和CONCATENATE公式的单元格)时,我得到
这是我的代码。它可以转换多个Excel工作簿,每个工作簿都有多个工作表,并提取我需要处理的工作表中特定公式的结果。请参见第136和145行之后的代码。
**代码。在5/7上进行了更新,提供了错误修复和代码,以检测和提取特定公式中的数据**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | 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 } } |
似乎不可能直接获取由Concatenate函数生成的地址,例如,请参见从Excel超链接公式提取URL。
为什么使用正则表达式的解决方案(例如以下解决方案)不合适?
1 2 3 | $split = $currentCell.Formula -split 'CONCATENATE' | Select -Last 1 | %{$_ -replace ` '[" ()]','' -split ','} $calculatedResult = $split[0] + $sheet.Range("$($split[1])").Text |