Использовать Powershell для замены подсекции результата регулярного выражения
Используя Powershell, я знаю, как искать файл для сложной строки с использованием регулярного выражения и заменять его некоторым фиксированным значением, как в следующем фрагменте:
Get-ChildItem "*.txt" |
Foreach-Object {
$c = ($_ | Get-Content)
$c = $c -replace $regexA,'NewText'
[IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
}
Теперь я пытаюсь выяснить, как заменить подраздел каждого соответствия регулярного выражения. Можно ли это сделать одним плавным шагом, как описано выше? Или вам нужно извлечь каждое совпадение большего регулярного выражения, выполнить поиск и заменить его, а затем каким-то образом вернуть этот результат в исходный текст?
Чтобы пояснить пример, предположим, что в следующем тестовом тексте я хочу найти только 14xx-нумерованные экземпляры типа "TEST = * 1404" в следующем тексте и заменить 14xx на 16xx?
A 2180 1830 12 0 3 3 TEST=C1404
A 900 1830 12 0 3 3 TEST=R1413
A 400 1830 12 0 3 3 TEST=R1411
A 1090 1970 12 0 3 3 TEST=U1400
A 1090 1970 12 0 3 3 TEST=CSA1400
A 1090 1970 12 0 3 3 TEST=CSA1414
A 1090 1970 12 0 3 3 TEST=CSA140
A 1090 1970 12 0 3 3 TEST=CSA14001
A 1090 1970 12 0 3 3 TEST=CSA17001
т.е. Я хочу, чтобы получившийся текст был следующим, где вы заметите, что должны измениться только первые 6 строк:
A 2180 1830 12 0 3 3 TEST=C1604
A 900 1830 12 0 3 3 TEST=R1613
A 400 1830 12 0 3 3 TEST=R1611
A 1090 1970 12 0 3 3 TEST=U1600
A 1090 1970 12 0 3 3 TEST=CSA1600
A 1090 1970 12 0 3 3 TEST=CSA1614 <- Second instance of '14' shouldn't change
A 1090 1970 12 0 3 3 TEST=CSA140 <- Shorter numbers shouldn't change
A 1090 1970 12 0 3 3 TEST=CSA14001 <- Longer numbers shouldn't change
A 1090 1970 12 0 3 3 TEST=CSA17001
Следующее регулярное выражение, похоже, выполняет поиск более крупных строк, где мне нужно делать замены, но я не знаю, какую функциональность в Powershell (replace
?) использовать, чтобы просто заменить подстроку результатов. Кроме того, не стесняйтесь предлагать лучшее регулярное выражение, если это поможет.
$regexA = "\bTEST=\b[A-Za-z]+14\d\d\r"
Мне бы не пришлось жестко закодировать исчерпывающий список вещей, которые могут находиться между "=" и цифрами, такими как "R", "C", "CSA" и т.д.
Я работал над чем-то в течение часа или около того, где я получаю все совпадения для регулярного выражения, поиск внутри них, чтобы заменить 14 на 16, а затем запустите замену на исходный текст старыми и новыми значениями, например. replace($myText,"TEST=CSA1400","TEST=CSA1600")
, но это не очень хорошо закрывает особые случаи, и мне кажется, что я направляюсь вниз по кроличьей дыре.
Ответы
Ответ 1
Вам нужно сгруппировать подвыражения, которые вы хотите сохранить (т.е. поместить их между круглыми скобками), а затем ссылаться на группы через переменные $1
и $2
в заменяющей строке. Попробуйте что-то вроде этого:
$regexA = '( TEST=[A-Za-z]+)14(\d\d)$'
Get-ChildItem "*.txt" | % {
$c = (Get-Content $_.FullName) -replace $regexA,'${1}16$2' -join "`r`n"
[IO.File]::WriteAllText($_.FullName, $c)
}
Ответ 2
Попробуйте следующее:
Get-ChildItem "*.txt" |
Foreach-Object {
$c = $_ | Get-Content | Foreach {$_ -replace '(?<=TEST=\D+)14(?=\d{2}(\D+|$))','16'}
$c | Out-File $_.FullName -Enc Ascii
}
Ответ 3
Вот пример использования делегата scriptblock (иногда называемого оценщиком):
$regex = [regex]'( TEST=\D+)14(\d{2})\s*$'
$evaluator = { '{0}16{1}' -f $args[0].Groups[1..2] }
filter set-number { $regex.Replace($_, $evaluator) }
foreach ($file in Get-ChildItem "*.txt")
{
($file | get-content) | set-number | Set-Content $file.FullName
}
Он, возможно, более сложный, чем оператор -replace, но позволяет использовать операторы powershell для создания заменяющего текста, поэтому вы можете делать все, что можно добавить в блок script.