使用属性字典将哈希表导出为CSV

时间:2016-10-07 17:22:13

标签: regex excel powershell

我似乎无法弄清楚如何简单地将格式化信息导出到CSV,除非我遍历对象中的每个项目并逐行写入CSV,这需要永远。我可以立即将值导出到CSV,只是在使用我遇到问题的属性字典时。

TestCSV文件的格式为包含IP地址的列。

以下是我所拥有的:

$CSV = "C:\TEMP\OutputFile.csv"
$RX = "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.|dot|\[dot\]|\[\.\])){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
$TestCSV = "C:\TEMP\FileWithIPs.csv"

$spreadsheetDataobject = import-csv $TestCSV
$Finding = $spreadsheetDataObject | Select-String $RX
$Props = @{ #create a properties dictionary
                LineNumber = $finding.LineNumber
                Matches = $finding.Matches.Value
       }
$OBJ = New-Object -TypeName psobject -Property $Props
$OBJ | Select-Object Matches,LineNumber | Export-Csv -Path $CSV -Append -NoTypeInformation

2 个答案:

答案 0 :(得分:2)

这不会按照书面形式运作。您正在使用Import-CSV创建具有属性的对象数组。 Select-String命令要求字符串作为输入,而不是对象。如果您想使用Select-String,您只需指定文件名,或在文件上使用Get-Content,并将其传递给Select-String。如果您想要的是行号和IP,我认为如果不是更好的话,这可能也会起作用:

$CSV = "C:\TEMP\OutputFile.csv"
$RX = "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.|dot|\[dot\]|\[\.\])){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
$TestCSV = "C:\TEMP\FileWithIPs.csv"

$spreadsheetDataobject = import-csv $TestCSV
$spreadsheetDataobject | 
    Where{$_.IP -match $RX} | 
    Select-Object @{l='Matches';e={$_.IP}},@{l='LineNumber';e={[array]::IndexOf($spreadsheetDataobject,$_)+1}} | 
    Export-Csv -Path $CSV -Append -NoTypeInformation

编辑: wOxxOm非常正确,这个答案比直接解析文本的开销要大得多。但是,对于那些对PowerShell不熟悉的人来说,它可能更容易理解。

关于$_.IP,因为您使用Import-CSV创建了一个对象数组。每个对象都具有基于CSV文件头的属性。 IP在标题中列为您的一个列,因此每个对象都具有IP的属性,该属性的值是该记录的IP列中的任何值。

让我为您解释Select行,然后您会发现将源路径添加为另一列很容易。

我正在做的是使用哈希表定义属性。对于我的例子,我将参考上面显示的第一个。由于它是哈希表,因此以@{开头,以}结尾。里面有两个键/值对:

l='Matches'
e={$_.IP}

基本上' l'是标签的简称,而且' e'是表达的缩写。标签确定要定义的属性的名称(这相当于导出时的列标题)。表达式定义分配给属性的值。在这种情况下,我实际上只是将IP列重命名为Matches,因为我为每一行分配的值是IP字段中的任何值。如果您在Excel中打开CSV,请复制整个IP列,将其粘贴到最后,然后将标题更改为匹配,这基本上就是我正在做的事情。因此,要将文件路径添加为列,我们可以使用以下内容向Select行添加一个哈希表:

@{
    l='FilePath'
    e={$CSV}
}

添加第三个属性,其名称为FilePath,值为$CSV中存储的值。更新后的Select行看起来像这样:

    Select-Object @{l='Matches';e={$_.IP}},@{l='LineNumber';e={[array]::IndexOf($spreadsheetDataobject,$_)+1}},@{l='FilePath'e={$CSV}} | 

答案 1 :(得分:1)

基于内置CSV cmdlet的任何代码都非常慢,因为为每行上的每个字段创建了对象,并且它在大文件上很明显(例如,来自另一个答案的代码需要900秒来处理9MB文件100k行)。

如果您的输入CSV文件很简单,您可以在不到一秒的时间内将其作为文本处理为100k行文件:

$CSV = .......
$RX = .......
$TestCSV = .......

$line = 0 # header line doesn't count
$lastMatchPos = 0
$text = [IO.File]::ReadAllText($TestCSV) -replace '"http.+?",', ','
$out = New-Object Text.StringBuilder
ForEach ($m in ([regex]"(?<=,""?)$RX(?=""?,)").Matches($text)) {
    $line += $m.index - $lastMatchPos - 
             $text.substring($lastMatchPos, $m.index-$lastMatchPos).Replace("`n",'').length
    $lastMatchPos = $m.Index + $m.length
    $out.AppendLine('' + $line + ',' + $m.value) >$null
}
if (!(Test-Path $CSV)) {
    'LineNumber,IP' | Out-File $CSV -Encoding ascii
}
$out.ToString() | Out-File $CSV -Encoding ascii -Append

代码在不太可能但可能包含匹配IP的情况下删除引用的URL字段。