我正在处理一个脚本,该脚本需要处理相同类型的文件,但是在不同时间具有不同的内容。我有一个CSV文件,看起来像下面的示例。并非每个字段都可以包含一个值。
record,title,creator,date,subject,location
0,Title1,Creator1,2018-08-17,Subject1,Location1
1,Title2,Creator2,2018-08-17,,Location1
2,Title3,Creator3,,Subject2,Location2
我需要将此CSV从数据表转换为每条记录的键值对列表,仅在存在值的情况下。标头将是通用的,其中field,value
对行中的每个键值对重复。例如:
record,field,value,field,value,field,value,field,value,field,value
0,title,Title1,creator,Creator1,date,2018-08-17,subject,Subject1,location,Location1
1,title,Title2,creator,Creator2,date,2018-08-17,location,Location1,,,
2,title,Title3,creator,Creator3,subject,Subject2,location,Location2,,,
我可以使用Import-CSV
来读取CSV,但是我很难更改结构。我试图走的每条路都无路可走,就像寻找解决方案一样。在这一点上,手动构建新的CSV似乎是最容易的,但这似乎并不正确,因此我想在这里提出。谁能指出我正确的方向?
我可以在StackOverflow上找到很多CSV,哈希表和键值对问题,但没有什么比这更好的了。
答案 0 :(得分:1)
我认为您误解了Import-Csv
的工作方式。它不创建哈希表,而是创建对象数组。每个对象都有一组由标题定义的属性。由于数据是从CSV导入的,因此它通过确保每个对象具有相同的属性(它们可能没有值,但这些属性存在并且相同)简化了事情。因此,我们可以获取第一个对象的属性列表作为基线集,然后遍历每条记录并基于该基线为每条记录构建一个字符串。如您所建议,我们将手动构建CSV。
$DataIn = Import-Csv C:\Path\To\File.csv
$Props = $DataIn[0].psobject.properties.name
$DataOut = ('record,'+$((2..$Props.Count|%{'field,value'}) -join ',')),$(For($i=0;$i -lt $DataIn.count;$i++){
[array]$tmpRecord = Switch($Props){
'Record' {$DataIn[$i].record;continue}
{[string]::IsNullOrEmpty($DataIn[$i].$_)} {continue}
default {'{0},{1}' -f $_, $DataIn[$i].$_}
}
If(($tmpDiff = $Props.count - $tmpRecord.count) -gt 0){$tmpRecord += ','*($tmpDiff*2-1)}
$tmpRecord -join ','
})
$DataOut | Set-Content C:\Path\To\Output.csv
所以这完全符合我的建议,同时保留了示例输出中未对record
列执行键/值操作。 switch
会检查每个潜在的属性,如果它是'record'属性,它只会输出记录值并继续到下一个属性。如果不是,它将检查该属性是否为空,如果是,它将移至下一个属性。如果不是空白,则输出field,value
,然后将所有这些输出(记录和任何字段/值组合)以逗号分隔成每条记录的一行。它还为空字段添加了额外的逗号。每个记录的行都与计算出的标题行一起收集在$DataOut
中。
请记住,PowerShell不想使用Import-Csv
读取该文件,因为列重复,因为标题行主要是“字段,值”,反复重复。我假设您正在以这种格式保存某些需要该格式进行输入的外部程序。