Чтобы избавиться от строки, содержащей пробелы или ничего, вы можете использовать это регулярное выражение:
(?m)^[ \t]*[\r\n]+
Ваше регулярное выражение ^[\s|\t]*$\n
будет работать, если вы указали многострочный режим ((?m)
), но оно по-прежнему неверно. С одной стороны, |
соответствует буквальному |
; нет необходимости указывать «или» в классе символов. С другой стороны, \s
соответствует любому символу пробела, включая TAB (\t
), возврат каретки (\r
) и перевод строки (\n
), что делает его ненужным избыточным и неэффективным. Например, в первой пустой строке (после конца первой Sub
) ^[\s|\t]*
сначала попытается сопоставить все перед словом Public
, затем вернется к концу предыдущей строки, где $\n
может совпадать.
Но пустая строка, помимо того, что она пустая или содержит только горизонтальные пробелы (пробелы или табуляции), также может содержать комментарий. Я предпочитаю рассматривать эти строки «только для комментариев» как пустые строки, потому что это относительно легко сделать, и это упрощает задачу сопоставления комментариев в непустых строках, что намного сложнее. Вот мое регулярное выражение:
^[ \t]*(?:(?:REM|')[^\r\n]*)?[\r\n]+
После использования любого начального горизонтального пробела, если я вижу REM
или '
, обозначающий комментарий, я использую это и все после него до разделителя следующей строки. Обратите внимание, что единственное, что требуется для присутствия - это сам разделитель строк. Также обратите внимание на отсутствие концевого якоря, $
. Нет необходимости использовать это, когда вы явно сопоставляете разделители строк, и в этом случае это нарушит регулярное выражение. В многострочном режиме $
соответствует только до перевода строки (\n
), а не до возврата каретки (\r
). (Такое поведение разновидности .NET является неправильным и довольно удивительным, учитывая давнее предпочтение Microsoft для \r\n
в качестве разделителя строк.)
Соответствие оставшимся комментариям - принципиально иная задача. Как вы обнаружили, простой поиск REM
или '
не годится, потому что вы можете найти его в строковом литерале, где он не означает начало комментария. Вам нужно начать с начала строки, потребляя и захватывая все, что не является началом комментария или строкового литерала. Если вы найдете двойные кавычки, используйте строковый литерал. Если вы обнаружите REM
или '
, прекратите захват и продолжайте поглощать оставшуюся часть линии. Затем вы заменяете всю строку только захваченной частью, то есть всем перед комментарием. Вот регулярное выражение:
(?mn)^(?<line>[^\r\n"R']*(("[^"]*"|(?!REM)R)[^\r\n"R']*)*)(REM|')[^\r\n]*
Или, более наглядно:
(?mn) # Multiline and ExplicitCapture modes
^ # beginning of line
(?<line> # capture in group "line"
[^\r\n"R']* # any number of "safe" characters
(
(
"[^"]*" # a string literal
|
(?!REM)R # 'R' if it's not the beginning of 'REM'
)
[^\r\n"R']* # more "safe" characters
)*
) # stop capturing
(?:REM|') # a comment sigil
[^\r\n]* # consume the rest of the line
Строка замены будет "${line}"
. Некоторые другие заметки:
- Обратите внимание, что это регулярное выражение не оканчивается на
[\r\n]+
, чтобы использовать разделитель строк, как регулярное выражение "пустые строки".
- Также не заканчивается
$
по той же причине, что и раньше. [^\r\n]*
будет жадно потреблять все до разделителя строк, поэтому привязка не нужна.
- Единственное, что требуется для присутствия - это
REM
или '
; мы не ищем соответствия ни одной строке, в которой нет комментария.
- Режим ExplicitCapture означает, что я могу использовать
(...)
вместо (?:...)
для всех групп, которые я не хочу захватывать, но именованная группа, (?<line>...)
, все еще работает.
- Как ни крути, это регулярное выражение было бы намного хуже, если бы VB поддерживал многострочные комментарии или если его строковые литералы поддерживали escape-символы обратной косой черты.
Я не делаю VB, но вот демонстрация в C # .