#.net #regex #powershell #csv
#.net #регулярное выражение #powershell #csv
Вопрос:
У меня есть текстовый файл, который содержит несколько строк, каждая из которых является строкой, разделенной запятыми. Формат каждой строки:
<Name, Value, Bitness, OSType>
Bitness
и OSType
являются необязательными.
Например, файл может быть таким:
Name1, Value1, X64, Windows7
Name2, Value2, X86, XP
Name3, Value3, X64, XP
Name4, Value3, , Windows7
Name4, Value3, X64 /*Note that no comma follows X64 */
....
....
Я хочу разобрать каждую строку на 4 переменные и выполнить над ней некоторую операцию. Это скрипт PowerShell, который я использую..
Get-Content $inputFile | ForEach-Object {
$Line = $_;
$_var = "";
$_val = "";
$_bitness = "";
$_ostype = "";
$envVarArr = $Line.Split(",");
For($i=0; $i -lt $envVarArr.Length; $i ) {
Switch ($i) {
0 {$_var = $envVarArr[$i].Trim();}
1 {$_val = $envVarArr[$i].Trim();}
2 {$_bitness = $envVarArr[$i].Trim();}
3 {$_ostype = $envVarArr[$i].Trim();}
}
}
//perform some operation using the 4 temporary variables
}
Однако я хотел знать, возможно ли это сделать с помощью regex в PowerShell. Не могли бы вы, пожалуйста, предоставить пример кода для этого? Обратите внимание, что 3-е и 4-е значения в каждой строке могут быть необязательно пустыми.
Комментарии:
1. Пожалуйста, не используйте регулярные выражения для разбора CSV. 🙂 Это преступление против человечности.
Ответ №1:
Вы можете указать альтернативную строку заголовка столбца для импортируемого файла file с помощью -Header
параметра Import-Csv
командлета:
Import-Csv .test.txt -Header Col1,Col2,Bitness,OSType
Ответ №2:
Не было бы лучше использовать Import-Csv
который делает все это (и более надежно) за вас?
Комментарии:
1. И если у вас уже есть строка CSV, вы можете передать ее в
ConvertFrom-Csv
, чтобы превратить в объект. Обе команды также позволяют указать пользовательский разделитель.
Ответ №3:
Как предлагает Тим, вы можете использовать Use Import-Csv. Разница в том, что Import-Csv считывает данные из файла.
@"
Name1, Value1, X64, Windows7
Name2, Value2, X86, XP
Name3, Value3, X64, XP
Name4, Value3, , Windows7
Name4, Value3, X64 /*Note that no comma follows X64 */
"@ | ConvertFrom-Csv -header var, val, bitness, ostype
# Result
var val bitness ostype
--- --- ------- ------
Name1 Value1 X64 Windows7
Name2 Value2 X86 XP
Name3 Value3 X64 XP
Name4 Value3 Windows7
Name4 Value3 X64 /*Note that no comma follows X64 */
Ответ №4:
Медленнее, чем меласса, но, потратив 20 лет на создание дюжины или более частичных решений, я решил заняться этим окончательно. Конечно, со временем теперь доступны все виды библиотек синтаксического анализа.
function SplitDelim($Line, $Delim=",", $Default=$Null, $Size=$Null) {
# 4956968,"""Visible,"" 4D ""Human"" Torso Anatomy Kit (4-5/8)",FDV-26051,"" ,"",,,,,,a
# "4956968,"""Visible,"" 4D ""Human"" Torso Anatomy Kit (4-5/8)",FDV-26051,"" ,"",,,,,,a
# ,4956968,"""Visible,"" 4D ""Human"" Torso Anatomy Kit (4-5/8)",FDV-26051,"" ,"",,,,,,a
$Field = ""
$Fields = @()
$Quotes = 0
$State = 'INF' # INFIELD, INQFIELD, NOFIELD
$NextState = $Null
for ($i=0; $i -lt $Line.length; $i ) {
$Char = $Line.substring($i,1)
if($State -eq 'NOF') {
# NOF and Char is Quote
# NextState becomes INQ
if ($Char -eq '"') {
$NextState = 'INQ'
}
# NOF and Char is Delim
# NextState becomes NOF
elseif ($Char -eq $Delim) {
$NextState = 'NOF'
$Char = $Null
}
# NOF and Char is not Delim, Quote or space
# NextState becomes INF
elseif ($Char -ne " ") {
$NextState = 'INF'
}
} elseif ($State -eq 'INF') {
# INF and Char is Quote
# Error
if ($Char -eq '"') {
return $Null}
# INF and Char is Delim
# NextState Becomes NOF
elseif ($Char -eq $Delim) {
$NextState = 'NOF'
$Char = $Null
}
} elseif ($State -eq 'INQ') {
# INQ and Char is Delim and consecutive Quotes mod 2 is 0
# NextState is NOF
if ($Char -eq $Delim -and $Quotes % 2 -eq 0) {
$NextState = 'NOF'
$Char = $Null
}
}
# Track consecutive quote for purposes of mod 2 logic
if ($Char -eq '"') {
$Quotes
} elseif ($NextState -eq 'INQ') {
$Quotes = 0
}
# Normal duty
if ($State -ne 'NOF' -or $NextState -ne 'NOF') {
$Field = $Char
}
# Push to $Fields and clear
if ($NextState -eq 'NOF') {
$Fields = (IfBlank $Field $Default)
$Field = ''
}
if ($NextState) {
$State = $NextState
$NextState = $Null
}
}
$Fields = (IfNull $Field $Default)
while ($Size -and $Fields.count -lt $Size) {
$Fields = $Default
}
return $Fields
}