2013-05-02 4 views
-1

У меня есть текстовый файл с несколькими пунктами (блоки текста), как следующее:Regex заменить текст без использования просмотра назад

SAMPLE 
ITEM_ID sample_id_0000028 
blah blah 
ABCD <--- do NOT remove 
blah blah blah 
blah blah 
blah 
SAMPLE_END 


SAMPLE 
ITEM_ID sample_id_0000033 
other text 
more text 
ABCD <--- Remove this 
more text 
SAMPLE_END 

SAMPLE 
ITEM_ID sample_id_00041 
ABCD <--- do NOT remove 
blah blah blah 
blah 
SAMPLE_END 

Я хочу, чтобы заменить/удалить экземпляр ABCD, что происходит в элементе с ID sample_id_0000033. Проблема в том, что в файле есть другие экземпляры ABCD, которые я хочу оставить в покое. Кроме того, количество строк между ITEM_ID и ABCD варьируется от элемента к элементу, и возможно, что ABCD не будет найдено в указанном элементе.

Я должен управлять файлом через vbscript в VBA. Я решил, что я буду использовать Regex для этого, но VBA не поддерживает регулярные выражения с lookbehind. Есть ли шаблон, который можно использовать для достижения этого с отрицательным взглядом или чем-то более простым?

Я бы выполнил регулярное выражение на строке, определенной как textfile.ReadAll, где textfile - это TextStream.

ответ

1

Вы можете использовать это:

pattern: (ITEM_ID sample_id_0000033\D(?:[^S]|S(?!=AMPLE_END))+?)ABCD 
replace: $1 

или лучше, это:

pattern: (ITEM_ID sample_id_0000033\D(?:[^\r]+\r\n)+?)ABCD 
replace: $1 

или короче acheong87 Например:

pattern: (sample_id_0000033\D(?:[^\r]+\r\n)+?)ABCD 
replace: $1 
+0

Первый работал для меня. Спасибо! Однако любой шаблон, включающий '\ n', не соответствует ожидаемому в VBA. – Excellll

+0

@Excellll: Да, \ n необходимо заменить на \ r \ n (CRLF - это естественная новая строка в окнах). Я отредактирую свой пост. –

0

способ разграничения каждого «блока», например, пустым листом между каждым блоком. Например, вы могли бы заменить

(sample_id_0000033(?:\r|\n|\r\n)(?:.*\S.*(?:\r|\n|\r\n))*)ABCD 

с

$1 

Вот что происходит.

  1. sample_id_0000033 не требует разъяснений.
  2. Я пишу (?:\r|\n|\r\n) как longhand для «любого рода linebreak», будь то CR (Mac), LF (UNIX) или CR/LF (DOS). Сокращение составляет (?:\r|\r?\n). Причина, я не пишу что-то вроде [\r\n]+ или \s+ что мы делаем не хочу найти более чем одиночный linebreak.
  3. Затем, мы хотим, чтобы пропустить строки, которые содержат, по меньшей мере, один непробельных характера, т.е. непустых строки: .*\S.*. Кроме того, разумеется, что-то вроде этого, сразу же после. Обратите внимание, что подстановочный знак . делает не соответствует линейным перерывам по умолчанию —, если вы находитесь в режиме точек-совпадений-новых строк, тогда вы должны использовать [^\r\n] вместо ..
  4. Неконвертирующие группы (?: ...) являются необязательными, но хорошей практикой, поскольку мы не планируем использовать эти группы.
  5. Если мы наконец встретим линию с ABCD, тогда все, прежде чем оно будет зафиксировано в $1, и восстановлено как есть с помощью замены —, оставив ABCD. Если мы не делаем, встречаем линию с ABCD, прежде чем мы встретим пустую строку, совпадение не будет выполнено и ничего не будет заменено.
+0

Это разбился Excel каждый раз, когда я пытался его. – Excellll

0

Рассмотрим следующий Powershell пример универсальное регулярное выражение и логика. Это не использует никаких регулярных выражений и будет соответствовать ABCD на любой из линий blah blah.

Вы должны переписать эту концепцию в свою логику VBA.

Пример

$Matches = @() 
$String = 'SAMPLE 
ITEM_ID sample_id_0000028 
blah blah 
ABCD <--- do NOT remove 
blah blah blah 
blah blah 
blah 
SAMPLE_END 


SAMPLE 
ITEM_ID sample_id_0000033 
other text 
more text 
ABCD <--- Remove this 
more text 
SAMPLE_END 

SAMPLE 
ITEM_ID sample_id_00041 
ABCD <--- do NOT remove 
blah blah blah 
blah 
SAMPLE_END 

SAMPLE 
ITEM_ID sample_id_0000028 
blah blah 
ABCD <--- do NOT remove 
blah blah blah 
blah blah 
blah 
SAMPLE_END 
SAMPLE 
ITEM_ID sample_id_0000033 
other text 
more text 
ABCD <--- Remove this 
more text 
SAMPLE_END 
SAMPLE 
ITEM_ID sample_id_00041 
ABCD <--- do NOT remove 
blah blah blah 
blah 
SAMPLE_END' 


$NewString = $String 
([regex]'(sample_id_0000033((.|\n|\r)*?)SAMPLE_END)').matches($String) | foreach { 
    write-host -------------------------------------------- 
    Write-Host "found at $($_.Groups[1].Index) = '$($_.Groups[1].Value)'" 
    Write-Host "found at $($_.Groups[2].Index) = '$($_.Groups[2].Value)'" 

    $ThisRecord = $_.Groups[1].Value 

    $InnerText = $_.Groups[2].Value 
    $NewInnerText = $InnerText -replace "ABCD", "I like kittens" 

    $NewRecord = $ThisRecord -replace $InnerText, $NewInnerText 

    write-host 
    Write-Host NewRecord: 
    Write-Host $NewRecord 

    $NewString = $NewString -replace $ThisRecord, $NewRecord 


    } # next match 

Урожайность

Примечания В этом примере я оставил <--- Remove this значение на строке, так что было бы легче определить, где изменения были сделаны

-------------------------------------------- 
found at 136 = 'sample_id_0000033 
other text 
more text 
ABCD <--- Remove this 
more text 
SAMPLE_END' 
found at 153 = ' 
other text 
more text 
ABCD <--- Remove this 
more text 
' 

NewRecord: 
sample_id_0000033 
other text 
more text 
I like kittens <--- Remove this 
more text 
SAMPLE_END 
-------------------------------------------- 
found at 452 = 'sample_id_0000033 
other text 
more text 
ABCD <--- Remove this 
more text 
SAMPLE_END' 
found at 469 = ' 
other text 
more text 
ABCD <--- Remove this 
more text 
' 

NewRecord: 
sample_id_0000033 
other text 
more text 
I like kittens <--- Remove this 
more text 
SAMPLE_END 
-------------------------------------------- 
New String 
SAMPLE 
ITEM_ID sample_id_0000028 
blah blah 
ABCD <--- do NOT remove 
blah blah blah 
blah blah 
blah 
SAMPLE_END 


SAMPLE 
ITEM_ID sample_id_0000033 
other text 
more text 
I like kittens <--- Remove this 
more text 
SAMPLE_END 

SAMPLE 
ITEM_ID sample_id_00041 
ABCD <--- do NOT remove 
blah blah blah 
blah 
SAMPLE_END 

SAMPLE 
ITEM_ID sample_id_0000028 
blah blah 
ABCD <--- do NOT remove 
blah blah blah 
blah blah 
blah 
SAMPLE_END 
SAMPLE 
ITEM_ID sample_id_0000033 
other text 
more text 
I like kittens <--- Remove this 
more text 
SAMPLE_END 
SAMPLE 
ITEM_ID sample_id_00041 
ABCD <--- do NOT remove 
blah blah blah 
blah 
SAMPLE_END 

Резюме

  1. С помощью этого регулярного выражения (sample_id_0000033((.|\n|\r)*?)SAMPLE_END) найдите все блоки текста, начиная с sample_id_0000033 и заканчивая следующей SAMPLE_END. Конечно, если вы используете другой разделитель для конца записи, вам также необходимо включить его здесь.
  2. За кулисами Powershell скрывает, как он заполняет массив $Matches со всеми найденными подстроками. Затем они передаются в цикл foreach, где $_ эквивалентен $ Matches (в данном случае).
  3. Внутри foreach блока мы обрабатываем каждый найденный экземпляр вашего матча:
    • заменяющего известный текст ABCD с нужной строкой I like kittens и сохранить получившееся изменение в $NewInnerText. Я создаю новую переменную здесь, потому что $InnerText не будет включать строки open и close, которые в зависимости от фактического значения вашего ABCD могут случайно изменить текст в концевых тегах.
    • $NewRecord создается из результатов замены $InnerText с $NewInnerText внутри $ThisRecord
    • с $NewString мы затем выполнить замену $ThisRecord с $NewRecord
+1

СОВЕТ: '(. | \ N | \ r)' не является хорошим способом сопоставления * ничего, включая новые строки *. Во-первых, вам придется приспособить его к каждому вкусу: в PowerShell (.NET) это будет '(. \\ n)', потому что '\ n' - единственный символ, который' .' не соответствует; в VBA (VBScript) вам придется использовать '(. | [\ r \ n \ u2028 \ u2029])'. Но он более эффективен и менее подвержен ошибкам использовать '.'в режиме' DOTALL/Singleline' в тех ароматах, которые его поддерживают, и '[\ s \ S]' в ароматах ECMAScript (JavaScript, VBScript и т. д.). –

Смежные вопросы