Негативный просмотр вперед работает некорректно

Вопрос или проблема

Я хотел бы создать регулярное выражение, которое соответствует следующим критериям, и я использую функцию Select-String в PowerShell. У меня есть этот запрос, и я хочу получить все варианты параметров (то есть часть, начинающуюся с @PAR) между определенной частью запроса, где часть #KEINERW отсутствует. Проблема в том, что, когда я тестирую это в regex101 или запускаю свой скрипт (результат тот же), версия с #KEINERW также совпадает, чего не должно быть.
Вот соответствующая часть запроса:

INSERT INTO #BASIS_VARIABLEN
        select ''tagid'' as ParameterTyp,IdentCode as Variable 
        from R34_META_DELIVERY.zeit.Tag t
        where 1=1 '+dbo.SVF_GetAuswahlEinschraenkung('t.IdentCode',@PAR_MONAT_Stichtag)+'

        INSERT INTO #BASIS_VARIABLEN
        select ''bankid'' as ParameterTyp,BLZ as Variable 
        from R34_ZDW.orga.Bank t
        where 1=1 '+dbo.SVF_GetAuswahlEinschraenkung('t.BLZ',@PAR_BLZ)

        INSERT INTO #BASIS_VARIABLEN
        select ''segmentid'' as ParameterTyp,Segment as Variable 
        from R34_ZDW.orga.Bank t
        where 1=1 '+dbo.SVF_GetAuswahlEinschraenkung('t.Segment',@PAR#KEINERW_SEG_Segment)  

EXEC sp_executesql @sql

И я хотел бы получить только эту часть:

INSERT INTO #BASIS_VARIABLEN
        select ''tagid'' as ParameterTyp,IdentCode as Variable 
        from R34_META_DELIVERY.zeit.Tag t
        where 1=1 '+dbo.SVF_GetAuswahlEinschraenkung('t.IdentCode',@PAR_MONAT_Stichtag)+'

        INSERT INTO #BASIS_VARIABLEN
        select ''bankid'' as ParameterTyp,BLZ as Variable 
        from R34_ZDW.orga.Bank t
        where 1=1 '+dbo.SVF_GetAuswahlEinschraenkung('t.BLZ',@PAR_BLZ)

EXEC sp_executesql @sql

Он не исключит часть:

INSERT INTO #BASIS_VARIABLEN
        select ''segmentid'' as ParameterTyp,Segment as Variable 
        from R34_ZDW.orga.Bank t
        where 1=1 '+dbo.SVF_GetAuswahlEinschraenkung('t.Segment',@PAR#KEINERW_SEG_Segment)

Это мой скрипт, который я создал, есть идеи, как его исправить, чтобы получить правильный результат? Самое главное, я должен использовать регулярное выражение и меньше программной логики, отсюда моя трудность. Я мог бы решить это с помощью -raw и индексов, но регулярное выражение должно быть повторно используемым в других языках. Вот мой полный PSScript:

$OutputFile = "C:\Users\WRSKOI\Documents\Check_USP_VW\outfile.txt"
$ErrorFile = "C:\Users\WRSKOI\Documents\Check_USP_VW\errorfile.txt"

# Создать или перезаписать выходные и ошибочные файлы
New-Item -ItemType File -Path $OutputFile -Force | Out-Null
New-Item -ItemType File -Path $ErrorFile -Force | Out-Null

# Определить шаблон regex для поиска параметров (с поддержкой многострочности с использованием '(?s)')
$RegexPattern = "(?s)INSERT\sINTO\s#BASIS_VARIABLEN.*?@PAR_(?!#KEINERW)(?:TAG|MONAT|DATUM|DATUMVONBIS|BLZ|SEG|BER|GES|MAR|SPR|KSG|KST|REF|TEXT|KTO|TEXTAREA)[\w-]*.*?EXEC\s+sp_executesql\s+@sql"

# Получить все .txt файлы в указанной директории (рекурсивно)
Get-ChildItem -Path "C:\Users\WRSKOI\Documents\Check_USP_VW\TestUSP" -Filter "*.txt" -Recurse | ForEach-Object {

    try {
        $File = $_.FullName

        # Прочитать весь файл как одну строку для многострочного поиска regex
        $FileContent = Get-Content -Path $File -Raw

        # Использовать Select-String для поиска многострочного шаблона в содержимом
        $Matches = [regex]::Matches($FileContent, $RegexPattern)

        # Если найдены совпадения, записать их в выходной файл
        if ($Matches) {
            foreach ($Match in $Matches) {
                $LineNumber = ($FileContent.Substring(0, $Match.Index).Split("`n").Count) # Вычислить номер строки
                $Output = "File: $File, Line: $LineNumber, Match: $($Match.Value)"
                Add-Content -Path $OutputFile -Value $Output
            }
        }
    }
    catch {
        # Записать любые исключения в файл ошибок
        $_.Exception.Message | Add-Content -Path $ErrorFile
    }
}

Ответ или решение

Для решения вашей проблемы с использованием регулярных выражений и исключением вхождений с "#KEINERW", вам нужно внести некоторые изменения в регулярное выражение.

Во-первых, давайте разберем ваш исходный шаблон регулярного выражения:

(?s)INSERT\sINTO\s#BASIS_VARIABLEN.*?@PAR_(?!#KEINERW)(?:TAG|MONAT|DATUM|DATUMVONBIS|BLZ|SEG|BER|GES|MAR|SPR|KSG|KST|REF|TEXT|KTO|TEXTAREA)[\w-]*.*?EXEC\s+sp_executesql\s+@sql

Ключевое здесь – использование негативного заранее заданного выражения (?!#KEINERW), которое говорит регулярному выражению не соответствовать шестизначному коду, если он начинается с @PAR#KEINERW. Однако проблема в том, что это негативное предшествующее выражение срабатывает только на следующем символе, поэтому регулярное выражение все равно может захватывать строки, где используется @PAR#KEINERW.

Чтобы решить данную проблему, нужно изменить подход, чтобы отфильтровать строки, содержащие #KEINERW, на более высоком уровне. Вот измененный шаблон:

(?s)INSERT\sINTO\s#BASIS_VARIABLEN(?:(?!#KEINERW).)*?@PAR_(?:TAG|MONAT|DATUM|DATUMVONBIS|BLZ|SEG|BER|GES|MAR|SPR|KSG|KST|REF|TEXT|KTO|TEXTAREA)[\w-]*.*?EXEC\s+sp_executesql\s+@sql

Обратите внимание на изменения:

  1. Мы добавили (?:(?!#KEINERW).)*?, что позволяет нам проверить, что строка не содержит последовательности #KEINERW до тех пор, пока не достигнем нужного паттерна.

Теперь ваш скрипт будет выглядеть следующим образом:

$OutputFile = "C:\Users\WRSKOI\Documents\Check_USP_VW\outfile.txt"
$ErrorFile = "C:\Users\WRSKOI\Documents\Check_USP_VW\errorfile.txt"

# Создание или перезапись файлов вывода и ошибок
New-Item -ItemType File -Path $OutputFile -Force | Out-Null
New-Item -ItemType File -Path $ErrorFile -Force | Out-Null

# Определение шаблона regex для соответствия параметрам с поддержкой многострочного режима '(?s)'
$RegexPattern = "(?s)INSERT\sINTO\s#BASIS_VARIABLEN(?:(?!#KEINERW).)*?@PAR_(?:TAG|MONAT|DATUM|DATUMVONBIS|BLZ|SEG|BER|GES|MAR|SPR|KSG|KST|REF|TEXT|KTO|TEXTAREA)[\w-]*.*?EXEC\s+sp_executesql\s+@sql"

# Получение всех .txt файлов в указанной директории (рекурсивно)
Get-ChildItem -Path "C:\Users\WRSKOI\Documents\Check_USP_VW\TestUSP" -Filter "*.txt" -Recurse | ForEach-Object {

    try {
        $File = $_.FullName

        # Чтение всего файла как одной строки для многострочного соответствия regex
        $FileContent = Get-Content -Path $File -Raw

        # Использование Select-String для поиска многострочного шаблона в содержимом
        $Matches = [regex]::Matches($FileContent, $RegexPattern)

        # Если найдены совпадения, записать их в файл вывода
        if ($Matches) {
            foreach ($Match in $Matches) {
                $LineNumber = ($FileContent.Substring(0, $Match.Index).Split("`n").Count) # Расчет номера строки
                $Output = "File: $File, Line: $LineNumber, Match: $($Match.Value)"
                Add-Content -Path $OutputFile -Value $Output
            }
        }
    }
    catch {
        # Запись любых исключений в файл ошибок
        $_.Exception.Message | Add-Content -Path $ErrorFile
    }
}

Попробуйте запустить этот скрипт. Он должен исключить части, содержащие #KEINERW, и возвращать только нужные вам параметры.

Оцените материал
Добавить комментарий

Капча загружается...