Powershell 如何将大型垂直文本文件转换并解析为带有标题的CSV文件?

Powershell 如何将大型垂直文本文件转换并解析为带有标题的CSV文件?,powershell,csv,scripting,Powershell,Csv,Scripting,我有一个大文本文件(*.txt),格式如下: ; KEY 123456 ; Any Company LLC ; 123 Main St, Anytown, USA SEC1 = xxxxxxxxxxxxxxxxxxxxx SEC2 = xxxxxxxxxxxxxxxxxxxxx SEC3 = xxxxxxxxxxxxxxxxxxxxx SEC4 = xxxxxxxxxxxxxxxxxxxxx SEC5 = xxxxxxxxxxxxxxxxxxxxx SEC6 = xxxxxxxxxxxxxxx

我有一个大文本文件(*.txt),格式如下:

; KEY 123456
; Any Company LLC
; 123 Main St, Anytown, USA

SEC1 = xxxxxxxxxxxxxxxxxxxxx
SEC2 = xxxxxxxxxxxxxxxxxxxxx
SEC3 = xxxxxxxxxxxxxxxxxxxxx
SEC4 = xxxxxxxxxxxxxxxxxxxxx
SEC5 = xxxxxxxxxxxxxxxxxxxxx
SEC6 = xxxxxxxxxxxxxxxxxxxxx
这是重复约350-400键。这些是搭扣钥匙和与之相关的SEC代码。我试图将此文件解析为一个CSV文件,其中包含KEY和SEC1-SEC6作为标题,并填充行。这是我正在尝试的格式:

KEY,SEC1,SEC2,SEC3,SEC4,SEC5,SEC6
123456,xxxxxxxxxx,xxxxxxxxxxx,xxxxxxxxxx,xxxxxxxxxx,xxxxxxxxxx,xxxxxxxxxx
456789,xxxxxxxxxx,xxxxxxxxxx,xxxxxxxxxx,xxxxxxxxxx,xxxxxxxxxx,xxxxxxxxxx
我已经能够使用文本文件(我的测试文件)中的一个键将脚本导出到CSV,但是当我尝试在完整列表中运行它时,它只导出最后一个键和秒代码

$keysheet = '.\AllKeys.txt'
$holdarr = @{}

Get-Content $keysheet | ForEach-Object {
if ($_ -match "KEY") {
    $key, $value = $_.TrimStart("; ") -split " "
    $holdarr[$key] = $value }
elseif ($_ -match "SEC") {
    $key, $value = $_ -split " = "
    $holdarr[$key] = $value }
}

$hash = New-Object PSObject -Property $holdarr
$hash | Export-Csv -Path '.\allsec.csv' -NoTypeInformation
当我在完整列表中运行它时,它还添加了两个额外的列,这些列看起来像属性而不是值

如果您有任何帮助,我们将不胜感激


谢谢。

这将是一种方法

& {
    $entry = $null
    switch -Regex -File '.\AllKeys.txt' {
        "KEY" {
            if ($entry ) {
                [PSCustomObject]$entry
            }
            $entry = @{}
            $key, $value = $_.TrimStart("; ") -split " "
            $entry[$key] = [int]$value
        }
         "SEC" {
            $key, $value = $_ -split " = "
            $entry[$key] = $value 
        }
    }
    [PSCustomObject]$entry
} | sort KEY | select KEY,SEC1,SEC2,SEC3,SEC4,SEC5,SEC6 |
Export-Csv -Path '.\allsec.csv' -NoTypeInformation

以下是我建议的方法:

$output = switch -Regex -File './AllKeys.txt' {
    '^; KEY (?<key>\d+)' {
        if ($o) {
            [pscustomobject]$o
        }
        $o = @{
            KEY = $Matches['key']
        }
    }

    '^(?<sec>SEC.*?)\s' {
        $o[$Matches['sec']] = ($_ | ConvertFrom-StringData)[$Matches['sec']]
    }

    default {
        Write-Warning -Message "No match found: $_"
    }
}

# catch the last object
$output += [pscustomobject]$o

$output | Export-Csv -Path './some.csv' -NoTypeInformation
$output=switch-Regex-File'./AllKeys.txt'{
“^;键(?\d+)”{
如有的话($o){
[pscustomobject]$o
}
$o=@{
KEY=$Matches['KEY']
}
}
“^(?秒。*?)\s”{
$o[$Matches['sec']=($|从StringData转换)[$Matches['sec']]
}
违约{
写入警告-消息“未找到匹配项:$\
}
}
#抓住最后一个物体
$output+=[pscustomobject]$o
$output | Export Csv-Path'/some.Csv'-notype信息

让我们充分利用

将包含一个或多个键和值对的字符串转换为哈希表

所以我们要做的是

  • 分割成文本块
  • 编辑“键”行
  • 删除空行或分号行
  • 传递到
    convertfromstringdata
    以创建哈希表
  • 将其转换为PowerShell对象


  • 现在使用
    将CSV导出到您的内容中

    检查
    ConvertFrom-StringData
    cmdlet。当你尝试解析
    Key=Value
    对时,它会让你的生活变得更轻松。总是有一组正好6秒的行吗?@Matt Yes。总是有整整6秒的行。你的
    $entry=$null
    在头部是不必要的(除非它是在scriptblock之外定义的,但我认为应该使用
    $local:
    $private:
    修饰符)@1不可纠正的是,语言可能不需要它,但我认为添加它仍然是一种很好的风格。@marsze这很接近。标题行正在导出,但所有其他行都是空的,只有分隔符。@Harlan我做了一些编辑。你用的是最新版本吗?此外,请在导出之前检查输出。查看返回空值的确切位置。我用你的数据测试了这个,它应该可以正常工作。@marsze它取决于我使用的PS版本。最初我使用的是2.0。当我在一台装有PS5的机器上试用它时,它就像一个魔咒。唯一的变化是,我必须去掉[int]这一铸造键值的元素。不知道为什么。错误是它无法从
    System.Object[]
    转换到
    System.Int32
    。谢谢除了删除与sec行匹配的绒毛行之外,word也可以这样做。我的方法将整个块转换为键值对,这样就可以在一个快照中处理它。看起来开销很大,只能使用
    convertfromstringdata
    。我看不出有什么好处。无论如何,它工作得很好,所以+1
    $path = "c:\temp\keys.txt"
    # Split the file into its key/sec collections. Drop any black entries created in the split
    (Get-Content -Raw $path) -split ";\s+KEY\s+" | Where-Object{-not [string]::IsNullOrWhiteSpace($_)} | ForEach-Object{
        # Split the block into lines again
        $lines = $_ -split "`r`n" | Where-Object{$_ -notmatch "^;" -and -not [string]::IsNullOrWhiteSpace($_)}
        # Edit the first line so we have a full block of key=value pairs.
        $lines[0] = "key=$($lines[0])"
        # Use ConvertFrom-StringData to do the leg work after we join the lines back as a single string.
        [pscustomobject](($lines -join "`r`n") | ConvertFrom-StringData)
    
    } | 
        # Cannot guarentee column order so we force it with this select statement.
        Select-Object KEY,SEC1,SEC2,SEC3,SEC4,SEC5,SEC6