Весь день ломаю голову над этим, и я приближаюсь, но не совсем туда. У меня есть небольшое подмножество моего гораздо большего сценария только для части регулярного выражения. Вот сценарий на данный момент:
$CCI_ID = @(
"003417 AR-2.1"
"003425 AR-2.9"
"003392 AP-1.12"
"009012 APP-1(21).1"
)
[regex]::matches($CCI_ID, '(\d{1,})|([a-zA-Z]{2}[-][\d][\(?\){0,1}[.][\d]{1,})') |
ForEach-Object {
if ($_.Groups[1].Value.length -gt 0){
write-host $('CCI-' + $_.Groups[1].Value.trim())}
else{$_.Groups[2].Value.trim()}
}
CCI-003417
AR-2.1
CCI-003425
AR-2.9
CCI-003392
AP-1.12
CCI-009012
PP-1(21
CCI-1
The output is correct for all but the last one. It should be:
CCI-009012
APP-1(21).1
Thanks for any advice.
Вместо того, чтобы описывать и количественно определять (необязательные) открывающую и закрывающую скобки по отдельности, сгруппируйте их вместе, а затем сделайте всю группу необязательной:
(?:\(\d+\))?
Таким образом, весь шаблон выглядит так:
[regex]::Matches($CCI_ID, '(\d{1,})|([a-zA-Z]{2,3}[-][\d](?:\(\d+\))?[.][\d]{1,})')
Как вы видите здесь, выражения Regex могут стать очень сложными и нечитаемыми. Поэтому часто рекомендуется рассматривать проблему с двух разных точек зрения:
В вашем случае, вероятно, проще сопоставить ту часть, которую вы не хотите: разделитель, пробел и разделить строку на это, чего, по-видимому, вы хотите достичь:
$CCI_ID | Foreach-Object {
$Split = $_ -Split '\s+', 2
'CCI-' + $Split[0]
$Split[1]
}
$_ -Split '\s+', 2, разбивает соответствующую строку на основе 1 или более пробелов (где вы также можете рассмотреть буквальное пространство: -Split ' '). , 2 предотвратит разделение строки более чем на 2 части. Это означает, что вторая часть не будет разделена, даже если она содержит пробелы.
Это был отличный ответ, и я буду использовать эту логику в будущем. Я проголосовал за вас по этой причине.
В своем шаблоне вы используете чередование |, но, глядя на данные примера, вы можете вместо этого сопоставить 1 или более пробелов после него.
Если есть совпадение с шаблоном, значение группы 1 уже содержит 1 или более цифр, поэтому вам не нужно проверять Value.length
Шаблон с необязательными цифрами в скобках:
\b(\d+)\s+([a-zA-Z]{2,}-\d(?:\(\d+\))?\.\d+)\b
Посмотрите демоверсию regex101.
$CCI_ID = @(
"003417 AR-2.1"
"003425 AR-2.9"
"003392 AP-1.12"
"009012 APP-1(21).1"
)
[regex]::matches($CCI_ID, '\b(\d+)\s+([a-zA-Z]{2,}-\d(?:\(\d+\))?\.\d+)\b') |
ForEach-Object {
write-host $( 'CCI-' + $_.Groups[1].Value.trim() )
write-host $_.Groups[2].Value.trim()
}
Выход
CCI-003417
AR-2.1
CCI-003425
AR-2.9
CCI-003392
AP-1.12
CCI-009012
APP-1(21).1
В качестве отступления: если вы передадите массив в качестве первого аргумента (входной строки) в [regex]::Matches(), PowerShell неявно преобразует его в строку, т. е. преобразует его в одну строку, содержащую (строковые) элементы массива, разделенные пробелом. каждый.