заменить символы новой строки в кавычках на \ n - PullRequest
4 голосов
/ 19 июня 2011

Мне нужно написать скрипт быстрого (к завтрашнему дню) фильтра, чтобы заменить разрывы строк (LF или CRLF), найденные в строках с двойными кавычками, на экранированную новую строку \n.Контент представляет собой (неработающую) программу javascript, поэтому мне нужно разрешить escape-последовательности, такие как "ab\"cd" и "ab\\"cd"ef" внутри строки.

Я понимаю, что sed не подходит для работы, так как он работает на строку, поэтому я перехожу к perl, о котором ничего не знаю:)

Я написал это регулярное выражение:"(((\\.)|[^"\\\n])*\n?)*" и протестировал его с http://regex.powertoy.org. Он действительно соответствует строкам в кавычках с переносами строк, однако, perl -p -e 's/"(((\\.)|[^"\\\n])*(\n)?)*"/TEST/g' - нет.

Поэтому мои вопросы:

  1. как заставить perl соответствовать разрывам строк?
  2. как написать часть «replace-by», чтобы она сохраняла исходную строку и заменяла только новые строки?

Есть это аналогичный вопрос с решением awk, но это не совсем то, что мне нужно.

ПРИМЕЧАНИЕ: я обычно не задаю вопросы "пожалуйста, сделайте это для меня", но я действительно нехочется изучать perl / awk к завтрашнему дню ...:)

РЕДАКТИРОВАТЬ : пример данных

"abc\"def" - matches as one string
"abc\\"def"xy" - match "abcd\\" and "xy"
"ab
cd
ef" - is replaced by "ab\ncd\nef"

Ответы [ 4 ]

2 голосов
/ 19 июня 2011

Вот простое решение Perl:

s§
    \G # match from the beginning of the string or the last match
    ([^"]*+) # till we get to a quote
    "((?:[^"\\]++|\\.)*+)" # match the whole quote
§
    $a = $1;
    $b = $2;
    $b =~ s/\r?\n/\\n/g; # replace what you want inside the quote
    "$a\"$b\"";
§gex;

Вот еще одно решение, если вы не хотите использовать /e и просто сделать это с одним регулярным выражением:

use strict;

$_=<<'_quote_';
hai xtest "aa xx aax" baix "xx"
x "axa\"x\\" xa "x\\\\\"x" ax
xbai!x
_quote_

print "Original:\n", $_, "\n";

s/
(
    (?:
        # at the beginning of the string match till inside the quotes
        ^(?&outside_quote) "
        # or continue from last match which always stops inside quotes
        | (?!^)\G
    )
    (?&inside_quote)  # eat things up till we find what we want
)
x   # the thing we want to replace
(
    (?&inside_quote)  # eat more possibly till end of quote
    # if going out of quote make sure the match stops inside them
    # or at the end of string
    (?: " (?&outside_quote) (?:"|\z) )?
)

(?(DEFINE)
    (?<outside_quote> [^"]*+ ) # just eat everything till quoting starts
    (?<inside_quote> (?:[^"\\x]++|\\.)*+ ) # handle escapes
)
/$1Y$2/xg;

print "Replaced:\n", $_, "\n";

Выход:

Original:
hai xtest "aa xx aax" baix "xx"
x "axa\"x\\" xa "x\\\\\"x" ax
xbai!x

Replaced:
hai xtest "aa YY aaY" baix "YY"
x "aYa\"Y\\" xa "Y\\\\\"Y" ax
xbai!x

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

s/
(
    (?:
        # at the beginning of the string match till inside the quotes
        ^(?&outside_quote) "
        # or continue from last match which always stops inside quotes
        | (?!^)\G
    )
    (?&inside_quote)  # eat things up till we find what we want
)
\r?\n # the thing we want to replace
(
    (?&inside_quote)  # eat more possibly till end of quote
    # if going out of quote make sure the match stops inside them
    # or at the end of string
    (?: " (?&outside_quote) (?:"|\z) )?
)

(?(DEFINE)
    (?<outside_quote> [^"]*+ ) # just eat everything till quoting starts
    (?<inside_quote> (?:[^"\\\r\n]++|\\.)*+ ) # handle escapes
)
/$1\\n$2/xg;
1 голос
/ 19 июня 2011

Используя Perl 5.14.0 (установить с perlbrew ), можно сделать следующее:

#!/usr/bin/env perl

use strict;
use warnings;

use 5.14.0;

use Regexp::Common qw/delimited/;

my $data = <<'END';
"abc\"def"
"abc\\"def"xy"
"ab
cd
ef"
END

my $output = $data =~ s/$RE{delimited}{-delim=>'"'}{-keep}/$1=~s!\n!\\n!rg/egr;

print $output;

Мне нужно 5.14.0 для флага /r внутренней замены.Если кто-то знает, как этого избежать, пожалуйста, дайте мне знать.

1 голос
/ 19 июня 2011
#!/usr/bin/perl
use warnings;
use strict;
use Regexp::Common;

$_ = '"abc\"def"' . '"abc\\\\"def"xy"' . qq("ab\ncd\nef");

print "befor: {{$_}}\n";
s{($RE{quoted})}
 {  (my $x=$1) =~ s/\n/\\n/g;
    $x
 }ge;
print "after: {{$_}}\n";
1 голос
/ 19 июня 2011

Пока OP не публикует пример содержимого для тестирования, попробуйте добавить флаг "m" (и, возможно, "s") в конец вашего регулярного выражения; от perldoc perlreref (ссылка) :

m  Multiline mode - ^ and $ match internal lines
s  match as a Single line - . matches \n

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

Обратите внимание, что если вы хотите захватить, но не сохранить что-то, вы можете использовать (?:PATTERN) вместо (PATTERN). Получив захваченный контент, используйте $1 - $9 для доступа к сохраненным совпадениям из соответствующего раздела.

Для получения дополнительной информации см. Ссылку, а также perldoc perlretut (учебное пособие) и perldoc perlre (полная документация)

...