Извлечение многострочного текстового файла в однострочный CSV с помощью Powershell - PullRequest
0 голосов
/ 07 августа 2020

У меня есть задача, и у меня нет простого способа преобразовать некоторые данные в правильный формат. Текстовый файл, который у меня есть, имеет следующий формат:

#N Last Name: Joe
#D First Name: Doe
#P Middle Name: A
Some Data:
#C ID Number: (1) 12345
#S Status: (1) Active 

#N Last Name: Jane
#D First Name: Doee
#P Middle Name: 
Some Data:
#C ID Number: (1) 11111
#S Status: (1) Active 
ID Number: (2) 1231
Status: (2) Active

Вот код, который я пытался использовать.

$A = Select-String -Pattern "#N" MYFILE.txt;
$B = Select-String -Pattern "#D" MYFILE.txt;
$C = Select-String -Pattern "#P" MYFILE.txt;
$D = Select-String -Pattern "#C" MYFILE.txt;
$E = Select-String -Pattern "#S" MYFILE.txt;

$wrapper = New-Object PSObject -Property @{ FirstColumn = $A; SecondColumn = $B; ThirdColumn = $C; FourthColumn = $D; FifthColumn = $E }
Export-Csv -InputObject $wrapper -Path .\output.csv -NoTypeInformation

Это результат, который я получаю

"SecondColumn","ThirdColumn","FifthColumn","FourthColumn","FirstColumn"
"System.Object[]","System.Object[]","System.Object[]","System.Object[]","System.Object[]"

Результат, который я ищу: # N, # D, # P, # C, # S

 Joe, Doe, A, 12345, Active
 Jane, Doee, , 11111, Active

Любая помощь очень ценится.

Ответы [ 3 ]

2 голосов
/ 07 августа 2020

вот еще один способ разобрать этот блок данных. я изменил информацию о пользователе, чтобы было более очевидно, что происходит. [ ухмылка ]

что он делает ...

  • создает одну многострочную строку для работы когда будете готовы сделать это по-настоящему, замените весь блок #region/#endregion вызовом Get-Content -Raw.
  • определяет разделитель между блоками пользовательских данных в данном случае это 2 символа новой строки - один в конце последней строки данных и один для пустой строки.
  • разбивает одну многострочную строку на несколько таких строк
  • итерация по результирующим блокам of text
  • инициализирует $ Vars, используемые для построения PSCO
  • разбивает текстовые блоки на строки текста
  • отфильтровывает любую строку, которая НЕ начинается с # , затем буква и последний пробел
  • итерация по оставшимся строкам
  • запускает switch на втором символе в каждой строке
  • , когда он соответствует одному из кодовые буквы, проанализируйте строку и установите эквивалентное значение $ Var
  • завершает итерацию по текущему набору строк
  • создает [PSCustomObject] для хранения значений
  • отправляет это в коллекцию $Result
  • завершает итерацию по текстовым блокам [внешний foreach]
  • отображает коллекцию на экране
  • сохраняет коллекцию в CSV

i Если вы хотите удалить кавычки из CSV, не делайте этого. [ ухмылка ], если вы ДОЛЖНЫ рискнуть испортить CSV, вы можете использовать Get-Content для загрузки строк из файла и замените кавычки ничем.

код ...

#region >>> fake reading in a text file as a single multiline string
#    in real life, use "Get-Content -Raw"
$InStuff = @'
#N Last Name: ALast
#D First Name: AFirst
#P Middle Name: AMid
Some Data:
#C ID Number: (1) 11111
#S Status: (1) Active 

#N Last Name: BLast
#D First Name: BFirst
#P Middle Name: 
Some Data:
#C ID Number: (1) 22222
#S Status: (1) Active 
ID Number: (2) 1231
Status: (2) Active
'@
#endregion >>> fake reading in a text file as a single multiline string

$BlockDelim = ([System.Environment]::NewLine) * 2

$Result = foreach ($Block in ($InStuff -split $BlockDelim))
    {
    # initialize stuff to $Null
    #    this handles non-matches [such as a missing middle name] 
    $FirstName = $MidName = $LastName = $IdNumber = $Status = $Null

    # the "-match" filters for lines that start with a "#", a single letter, and a space
    foreach ($Line in ($Block -split [System.Environment]::NewLine -match '^#\w '))
        {
        switch ($Line[1])
            {
            'N' {
                $LastName = $Line.Split(':')[-1].Trim()
                break
                }
            'D' {
                $FirstName = $Line.Split(':')[-1].Trim()
                break
                }
            'P' {
                $MidName = $Line.Split(':')[-1].Trim()
                break
                }
            'C' {
                $IdNumber = $Line.Split(':')[-1].Trim().Split(' ')[-1].Trim()
                break
                }
            'S' {
                $Status = $Line.Split(':')[-1].Trim().Split(' ')[-1].Trim()
                break
                }
            } # end >>> switch ($Line[1])
        } # end >>> foreach ($Line in ($Block -split [System.Environment]::NewLine))

        # create a custom object and send it out to the collection
        [PSCustomObject]@{
            FirstName = $FirstName
            LastName = $LastName
            MidName = $MidName
            IdNumber = $IdNumber
            Status = $Status
            }
    } # end >>> foreach ($Block in ($InStuff -split $BlockDelim))

# display on screen
$Result

# send to a CSV file
$Result |
    Export-Csv -LiteralPath "$env:TEMP\Veebster_ParsedResult.csv" -NoTypeInformation

вывод на экран ...

FirstName : AFirst
LastName  : ALast
MidName   : AMid
IdNumber  : 11111
Status    : Active

FirstName : BFirst
LastName  : BLast
MidName   : 
IdNumber  : 22222
Status    : Active

содержимое файл CSV ...

"FirstName","LastName","MidName","IdNumber","Status"
"AFirst","ALast","AMid","11111","Active"
"BFirst","BLast","","22222","Active"

обратите внимание, что здесь нет обнаружения ошибок ИЛИ обработки ошибок. [ ухмылка ]

1 голос
/ 07 августа 2020

Вот еще одна рекомендация. Я предлагаю использовать ConvertFrom-String.

Сначала мы создадим шаблон, я учел две ненужные строки в вашем примере данных. Я очень надеюсь, что это опечатка / copyo.

$template = @'
#N Last Name {[string]Last*:last1}
#D First Name: {[string]First:first1}
#P Middle Name: {[string]Middle:A}
#C ID Number: (1) {[int]ID:11111}
#S Status: (1) {[string]Status:status1}

#N Last Name: {[string]Last*:Jane}
#D First Name: {[string]First:Doee}
#P Middle Name: {[string]Middle: \s}
#C ID Number: (1) {[int]ID:11111}
#S Status: (1) {[string]Status:Active}
{!Last*:ID Number: (2) 1231
Status: (2) Active}
'@

Теперь применим этот шаблон к вашим данным. Сначала мы проанализируем здесь-строку.

@'
#N Last Name: Joe
#D First Name: Doe
#P Middle Name: A
Some Data:
#C ID Number: (1) 12345
#S Status: (1) Active 

#N Last Name: Jane
#D First Name: Doee
#P Middle Name: 
Some Data:
#C ID Number: (1) 11111
#S Status: (1) Active 
ID Number: (2) 1231
Status: (2) Active
'@ | ConvertFrom-String -TemplateContent $template -OutVariable results

Вывод

Last   : Joe
First  : Doe
Middle : A
ID     : 12345
Status : Active

Last   : Jane
First  : Doee
ID     : 11111
Status : Active

Теперь мы можем построить наш объект для подготовки к экспорту.

$results | foreach {
    [pscustomobject]@{
        FirstName = $_.first
        LastName  = $_.last
        MidName   = $_.middle
        IdNumber  = $_.id
        Status    = $_.status
    }
} -OutVariable export

А теперь мы можем экспортировать его

$export | Export-Csv -Path .\output.csv -NoTypeInformation

Вот что находится в output.csv

PS C:\> Get-Content .\output.csv
"FirstName","LastName","MidName","IdNumber","Status"
"Doe","Joe","A","12345","Active"
"Doee","Jane",,"11111","Active"

Вот то же самое, читая его из файла.

$template = @'
#N Last Name {[string]Last*:last1}
#D First Name: {[string]First:first1}
#P Middle Name: {[string]Middle:A}
#C ID Number: (1) {[int]ID:11111}
#S Status: (1) {[string]Status:status1}

#N Last Name: {[string]Last*:Jane}
#D First Name: {[string]First:Doee}
#P Middle Name: {[string]Middle: \s}
#C ID Number: (1) {[int]ID:11111}
#S Status: (1) {[string]Status:Active}
{!Last*:ID Number: (2) 1231
Status: (2) Active}
'@

get-content .\ndpcs.txt | 
    ConvertFrom-String -TemplateContent $template | foreach {
        [pscustomobject]@{
            FirstName = $_.first
            LastName  = $_.last
            MidName   = $_.middle
            IdNumber  = $_.id
            Status    = $_.status
        }
    } | Export-Csv -Path .\output.csv -NoTypeInformation

Давайте дважды проверьте содержимое нашего CSV, чтобы быть уверенным.

Get-Content .\output.csv
"FirstName","LastName","MidName","IdNumber","Status"
"Doe","Joe","A","12345","Active"
"Doee","Jane",,"11111","Active"

Следует отметить пару моментов: если наборы данных после этого имеют другие характеристики, вам необходимо добавить больше образцов в шаблон. Если двух дополнительных строк (ID и статуса) не должно быть, просто удалите эту часть шаблона.

Я рекомендую всем использовать параметр -outvariable при разработке сценариев логики / построения, поскольку вы можете видеть вывод и одновременное присвоение переменной.

0 голосов
/ 07 августа 2020

Это будет работать, но это уродливо, как грех.

# Get content from text file
$Txtfile = Get-Content "C:\temp\test.txt" -raw
# Add delimiter to split users
$Delimiter = "

"
$Users = $Txtfile -split $Delimiter

# Create an array list to add data to so it can be exported later.
$collectionVariable = New-Object System.Collections.ArrayList
ForEach($Grouping in $Users) {
    $temp = New-Object System.Object
    $temp | Add-Member -MemberType NoteProperty -Name "#N" -Value (([regex]::match($Grouping, '#N.*').value) -split " ")[-1]
    $temp | Add-Member -MemberType NoteProperty -Name "#D" -Value (([regex]::match($Grouping, '#D.*').value) -split " ")[-1]
    $temp | Add-Member -MemberType NoteProperty -Name "#P" -Value (([regex]::match($Grouping, '#P.*').value) -split " ")[-1]
    $temp | Add-Member -MemberType NoteProperty -Name "#C" -Value (([regex]::match($Grouping, '#C.*').value) -split " ")[-1]
    # This is [-2] due to new line at end of the groupings.
    $temp | Add-Member -MemberType NoteProperty -Name "#S" -Value (([regex]::match($Grouping, '#S.*').value) -split " ")[-2]
    $collectionVariable.Add($temp) | Out-Null   
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...