Perl Regex Match и удаление - PullRequest
       16

Perl Regex Match и удаление

4 голосов
/ 17 сентября 2008

У меня есть строка, которая начинается с //#... и идет до символа новой строки. Я выяснил, регулярное выражение для которого это ..#([^\n]*).

Мой вопрос: как удалить эту строку из файла, если следующее условие соответствует

Ответы [ 9 ]

27 голосов
/ 17 сентября 2008

Ваше регулярное выражение выбрано неправильно по нескольким пунктам:

  1. Вместо того, чтобы специально сопоставлять две косые черты, вы используете .., чтобы сопоставить два символа, которые могут быть чем угодно, возможно потому, что вы не знаете, как сопоставлять косые черты, когда вы также используете их в качестве разделителей. (На самом деле, точки соответствуют почти чему угодно, как мы увидим в # 3.)

    Внутри литерала регулярного выражения, разделенного косой чертой, //, вы можете сопоставлять косые черты, просто защищая их обратными косыми чертами, например. /\/\//. Однако, более приятный вариант - использовать более длинную форму литерала регулярного выражения, m//, где вы можете выбрать разделитель, например. m!!. Поскольку вы используете для разграничения что-то отличное от косой черты, вы можете написать их, не экранируя: m!//!. См. perldoc perlop .

  2. Он не привязан к началу строки, поэтому он будет совпадать где угодно. Используйте ^ утверждение начала строки перед

  3. Вы написали [^\n], чтобы соответствовать «любому символу, кроме новой строки», когда есть гораздо более простой способ написать это, который является просто подстановочным знаком .. Это именно так - соответствует любому символу, кроме новой строки.

  4. Вы используете круглые скобки для группировки части совпадения, но группа не определена количественно (вы не указываете, что она может совпадать с любым другим числом раз, чем один раз), и вы не заинтересованы в ее сохранении. Так что скобки лишние.

В целом, это делает m!^//#.*!. Но помещать незафиксированный .* (или что-либо с квантификатором *) в конце регулярного выражения бессмысленно, поскольку он никогда не меняет, будет ли строка соответствовать или нет: * с удовольствием ничего не найдет.

Так что у вас остается m!^//#!.

Что касается удаления строки из файла, как все остальные объяснили, прочитайте ее построчно и напечатайте все строки, которые вы хотите сохранить в другом файле. Если вы не делаете это в более крупной программе, используйте переключатели командной строки perl, чтобы сделать это легко:

perl -ni.bak -e'print unless m!^//#!' somefile.txt

Здесь переключатель -n заставляет perl помещать цикл вокруг предоставленного вами кода, который будет последовательно читать все файлы, которые вы передаете в командной строке. Переключатель -i (для «на месте») говорит, что нужно собрать выходные данные из вашего скрипта и перезаписать им оригинальное содержимое каждого файла. Параметр .bak для опции -i указывает perl сохранять резервную копию исходного файла в файле, названном в честь исходного имени файла с добавлением .bak. Для всех этих битов см. perldoc perlrun .

Если вы хотите сделать это в контексте более крупной программы, самый простой способ сделать это безопасно - открыть файл дважды, один раз для чтения и отдельно, с помощью IO :: AtomicFile , в другой раз для письма. IO :: AtomicFile заменит исходный файл, только если он успешно закрыт.

4 голосов
/ 17 сентября 2008

Чтобы отфильтровать все строки в файле, которые соответствуют определенному регулярному выражению:

perl -n -i.orig -e 'print unless /^#/' file1 file2 file3

.orig после ключа -i создает резервную копию файла с заданным расширением (.orig). Вы можете пропустить его, если вам не нужна резервная копия (просто используйте -i).

Ключ -n заставляет perl выполнять ваши инструкции (-e '...') для каждой строки в файле. Строка хранится в $ _ (который также является аргументом по умолчанию для многих инструкций, в данном случае: сопоставление с печатью и регулярным выражением).

Наконец, аргумент переключателя -e говорит: «печатать строку, если она не соответствует символу # в начале строки.

PS. Также есть ключ -p, который ведет себя как -n, за исключением того, что строки всегда печатаются (хорошо для поиска и замены)

2 голосов
/ 17 сентября 2008

Как уже отмечали другие, если конечной целью является только удаление строк, начинающихся с //#, по соображениям производительности вам, вероятно, лучше использовать grep или sed:

grep -v '^\/\/#' filename.txt > filename.stripped.txt

sed '/^\/\/#/d' filename.txt > filename.stripped.txt

или

sed -i '/^\/\/#/d' filename.txt

, если вы предпочитаете редактирование на месте.

Обратите внимание, что в Perl ваше регулярное выражение будет

m{^//#}

, который соответствует двум слешам, за которыми следует # в начале строки.

Обратите внимание, что вы избегаете "обратного слешита", используя оператор сопоставления m{pattern} вместо более привычного /pattern/. Обучайтесь этому синтаксису рано, поскольку это простой способ избежать чрезмерного побега. Вы можете написать m{^//#} так же эффективно, как m%^//#% или m#^//\##, в зависимости от того, что вы хотите сопоставить. Стремитесь к ясности - регулярные выражения достаточно сложны для расшифровки без колючего леса с обратными слешами, которые убивают читабельность. Серьезно, m/^\/\/#/ выглядит как аллигатор с колотым зубом и пломбой или крошечной ASCII-картиной Альп.

Одна проблема, которая может возникнуть в вашем скрипте, заключается в том, что весь файл превращается в строку, символы новой строки и все. Чтобы защититься от этого случая, используйте модификатор / m (multiline) в регулярном выражении:

m{^//#}m

Это позволяет ^ совпадать в начале строки и после новой строки. Вы могли бы подумать, что есть способ обрезать или сопоставить строки, соответствующие m{^//#.*$}, с использованием модификаторов регулярных выражений /g, /m и /s в случае, когда вы записали файл в строку, но не Я не хочу делать его копию (сначала задается вопрос, почему он был добавлен в строку.) Это должно быть возможным, но уже поздно, и я не вижу ответа. Однако, один «простой» способ сделать это:

my $cooked = join qq{\n}, (grep { ! m{^//} } (split m{\n}, $raw));

, хотя это создает копию вместо редактирования на месте исходной строки $raw.

1 голос
/ 17 сентября 2008

Вам действительно не нужен Perl для этого.

sed '/^\/\/#/d' inputfile > outputfile

Я <3 сед. </p>

0 голосов
/ 17 сентября 2008

Итерация по каждой строке в файле и пропуск строки, если она соответствует шаблону:

my $fh = new FileHandle 'filename'
    or die "Failed to open file - $!";

while (my $line = $fh->getline) {
    next if $line =~ m{^//#};
    print $line;
}
close $fh;

Это напечатает все строки из файла, кроме строки, которая начинается с '//#'.

0 голосов
/ 17 сентября 2008

Попробуйте следующее:

perl -ne 'print unless m{^//#}' input.txt > output.txt

Если вы используете окна, вам нужны двойные кавычки вместо одинарных.

Вы можете сделать то же самое с grep

grep -v -e '^//#' input.txt > output.txt
0 голосов
/ 17 сентября 2008

Я не думаю, что ваше регулярное выражение верно.

Сначала вам нужно начать с ^, иначе он будет соответствовать этому шаблону в любом месте строки.

Во-вторых, .. должно быть \/\/, иначе оно будет соответствовать любым двум символам.

^\/\/#[^\n]* это, вероятно, то, что вы хотите.

Затем сделайте то, что говорит EricSchaefer, и построчно читайте файл, записывая только те строки, которые не соответствуют.

-
BMB

0 голосов
/ 17 сентября 2008

Он начинается с начала строки или может появиться где-нибудь? Если бывший s / old / new - это то, что вы хотите. Если последнее, я должен это выяснить. Я подозреваю, что обратные ссылки могут быть использованы как угодно.

0 голосов
/ 17 сентября 2008

Читайте файл построчно и записывайте только те строки в новый файл, которые не соответствуют регулярному выражению. Вы не можете просто удалить строку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...