у меня есть файл sample.txt
Processing...
Cl.Cog = "/u/l/Cg.txt"
V.DM=Nv
AL.Ft = "He Se Fe",Bt,@L(Ey,0),&Ct:&Cu3
мне нужно разделить этот файл с 4 разными столбцами с новым столбцом, добавленным до вывод.csv
PHP> This is hard coded line;
column1 column2 column3 column4
+-------------------+-------------------+-------------------+-------------------
abcdefg Cl Cog "/u/l/Cg.txt"
abcdefg V DM Nv
abcdefg AL Ft "He Se Fe",Bt,@L(Ey,0),&Ct:&Cu3
так что я пробовал это, что, похоже, не работает
Get-Content test.txt |
$headerString = "PHP> This is hard coded line;"
$headerElements = $headerString -split "\s+" | Where-Object{$_}
$headerIndexes = $headerElements | ForEach-Object{$headerString.IndexOf($_)}
$results = $data
Foreach {"$(($_ -split '\s+',4)[0..2])"} |
Out-file -filepath "D:\output.txt"
это дополнительный столбец, добавленный ранее с той же строкой (abcdefg) в этом... и мой код, над которым я все еще работаю
Это неправильный способ обработки данных. Можете ли вы опубликовать больше текстового файла с более чем одним образцом ввода. Одна строка CSV должна содержать несколько строк текстового файла.
Попробуйте следующее:
$inputFilename = 'c:\temp\test.txt'
$outputFilename = 'c:\temp\test.csv'
$contents = Get-Content $filename
$table = [System.Collections.ArrayList]::new()
foreach($row in $contents)
{
if ($row.Contains('='))
{
$newRow = New-Object -TypeName psobject
$newRow | Add-Member -NotePropertyName column1 -NotePropertyValue 'abcdefg'
$splitRow = $row.Split('=')
$splitPeriod = $splitRow[0].Split('.')
$newRow | Add-Member -NotePropertyName column2 -NotePropertyValue $splitPeriod[0]
$newRow | Add-Member -NotePropertyName column3 -NotePropertyValue $splitPeriod[1]
$newRow | Add-Member -NotePropertyName column4 -NotePropertyValue $splitRow[1]
$table.Add($newRow) | Out-Null
}
}
$table
$table | export-csv -Path $outputFilename
Ваш ответ не только однотипен для PowerShell и, следовательно, многословен, но и намного медленнее, чем идиоматическое решение. Основываясь на моих тестах на машине с Windows 11 22H2, работающей под управлением Windows PowerShell 5.1 с 1000 строками ввода, примерно в 50 (!) раз медленнее. Я предлагаю вам самостоятельно запустить тесты (также чтобы убедиться, что я правильно измеряю): gist.github.com/mklement0/…
@mklement0 mklement0: разница во времени - это не стиль программирования. Медленно работает Add-Member. Использование $newRow = [pscustomobject] @{ column1 = 'abcdefg' column2 = $splitPeriod[0] column3 = $splitPeriod[1] column4 = $splitRow[1] } делает мое и ваше время примерно одинаковыми.
Да, использование повторных вызовов Add-Member
, когда один литерал [pscustomobject]
будет причиной большей части замедления. Другая неидиоматическая часть заключается в том, чтобы вручную создать список (который вы необъяснимым образом назвали $table
) вместо того, чтобы использовать оператор foreach
в качестве выражения и позволить PowerShell создать для вас массив. Обе практики (как бы вы их ни называли — я не использовал термин «стиль программирования») сводятся к коду, который не только более многословен, но и в данном случае значительно медленнее.
Прямое использование API-интерфейсов .NET при правильном использовании может обеспечить повышение производительности (использование [System.Collections.ArrayList]
приводит к противоположному результату), но сначала всегда следует пробовать идиоматический высокоуровневый код PowerShell — нет необходимости начинать с API-интерфейсов .NET, а не просто чтобы избежать многословия и более низкого уровня абстракции, а также чтобы не нуждаться в знаниях из отдельной области знаний.
Цитата из Соображений производительности сценариев PowerShell: «Многие из описанных здесь методов не являются идиоматическими PowerShell и могут снизить удобочитаемость сценария PowerShell. Авторам сценариев рекомендуется использовать идиоматический PowerShell, если производительность не требует иного».
@mklement0 mklement0: я полностью не согласен с комментарием о читабельности. Код должен легко комментироваться, а объединение всего в одну длинную инструкцию не позволяет комментировать и очень затрудняет отладку.
Нет необходимости размещать пайплайн на одной строке, и можно легко избежать дозирования: просто заканчивайте каждый сегмент |
и продолжайте на следующей строке, что также позволяет размещать комментарии как после, так и между сегментами пайплайна. Блоки сценариев в конвейерах позволяют выполнять отладку, как и внешние конвейеры. Конвейеры без блоков сценариев редко нуждаются в отладке, и при необходимости вы можете проверить промежуточные результаты с помощью -OutVariable
. Что еще более важно, ваш аргумент принципиально благовиден: советовать не использовать основные функции и команды языка, очевидно, обречены на провал.
Я призываю вас не пытаться "создать" csv самостоятельно. Сосредоточьтесь на создании нужных вам объектов, а затем просто экспортируйте их в csv. В вашей строке заголовка больше столбцов, чем в ваших данных, плюс вы не указываете конкретно, что хотите исключить строку processing...
, и не учитываете ее в своем примере кода, но желаемый результат не показывает ее. Вот как бы я справился с тем, что, как я полагаю, вам нужно.
# Create the headers for the csv from your hard coded line
$headerString = "PHP> This is hard coded line;"
$headers = -split $headerString
# Get the text content, you can skip 1 here if you need to exclude processing
$data = Get-Content test.txt
# Iterate over each line replacing literal period and equals with a pipe.
# Combine this with your static first column value and a pipe
# I chose pipe as a delimiter as your text file had commas but not a pipe
$results = foreach($line in $data){
# again, this will create a pipe delimited string
$csv = 'abcdefg|' + ($line -replace '\.|=','|')
# convert this string to csv with your custom headers
$csv | ConvertFrom-Csv -Delimiter '|' -Header $headers
}
Весь вывод был собран в $results
. Вы можете просмотреть и/или экспортировать прямо сейчас
$results | Format-Table
PHP> This is hard coded line;
---- ---- -- ---- ----- -----
abcdefg Cl Cog /u/l/Cg|txt
abcdefg V DM Nv
abcdefg AL Ft He Se Fe,Bt,@L(Ey,0),&Ct:&Cu3
# output to csv
$results | Export-Csv Some.csv -NoType
Если вы хотите вывести представление таблицы в текстовый файл (я не рекомендую это делать, csv гораздо проще использовать позже), вы можете сделать это
$results | Format-Table | Out-String | Set-Content c:\temp\textfile.txt
я хочу исключить обработку, а также .. на выходе я получаю нужную таблицу .. как экспортировать ту же таблицу в текстовый файл .. я пробовал out-file .. и export-csv ... он не работает
' $data = Get-content 'output.txt' $data | foreach {$_.replace('","',' ').TrimStart('"').TrimEnd('"')} $data | Export-Csv -Path 'E:\new_1.txt' -NoType '
это способ задать фиксированную ширину для каждого столбца в этом текстовом файле таблицы форматов. Application"; ширина = 21}, ` @{Expression = {$_.Name};Label = "Database";ширина=20}, ` @{Expression = {$_.Name};Label = "variable";ширина =20}, @{Expression = {$_.Name};Label = "value";width=20} $width = Get-Process | Format-Table -Property $a |Out-string | Set-Content c:\inal.txt' '
откуда
abcdefg
в вашем коде? также вам нужно объяснить, что такое разделитель столбцов, поскольку ваш ожидаемый результат и ваш код вообще не связаны