【问题标题】:replace newlines within quoted string with \n用 \n 替换引用字符串中的换行符
【发布时间】:2011-09-18 04:07:06
【问题描述】:

我需要编写一个快速(到明天)过滤器脚本,用转义换行符\n 替换双引号字符串中的换行符(LF 或 CRLF)。内容是一个(损坏的)javascript 程序,所以我需要在字符串中允许像"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”部分,使其保留原始字符串并仅替换换行符?

similar question 有 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"

【问题讨论】:

  • 双引号字符串在什么上下文中?
  • 好吧,javascript,但我认为这无关紧要。我不需要完全解析,只需要识别字符串文字
  • "Handling" \"\\" 可能意味着您期望字符串被扩展两次。或者您想保留恰好位于结束 " 之前的反斜杠。由于除了“正确处理”之外您没有提供任何所需的输出,我只能猜测“正确”对您意味着什么。
  • @davka,你能发布示例内容来试试吗?谢谢
  • @TLP,@Joel,我明白了,会编辑

标签: regex linux perl scripting awk


【解决方案1】:

这是一个简单的 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;

【讨论】:

  • 如果您要在代码中使用§ 作为正则表达式分隔符,请记住use utf8;
  • 谢谢。我的第一个解决方案出现以下错误:Nested quantifiers in regex; marked by &lt;-- HERE in m/, ` ([^"]*+
  • @bdonlan, § 是 latin1,所以不需要使用 utf8(除非我猜你是这样编码文件的)。
  • @davka,更新您的 perl,您使用的必须是古老的。或者您可以删除所有跟在量词后面的+(例如+*)。
  • 是什么让您认为我的(或 OP 的)默认编辑器字符集设置会使用 latin1? :) 你可以找到一个包含几乎任何字符的非 unicode 字符集,但假设人们正在使用它并不是一个好主意,尤其是当大多数发行版默认使用 utf8 时。
【解决方案2】:

在 OP 发布一些示例内容以供测试之前,请尝试将“m”(可能还有“s”)标志添加到正则表达式的末尾;来自perldoc perlreref (reference)

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

为了进行测试,您可能还会发现添加命令行参数“-i.bak”以便保留原始文件的备份(现在扩展名为“.bak”)。

另请注意,如果您想捕获但不存储某些内容,您可以使用(?:PATTERN) 而不是(PATTERN)。获取捕获的内容后,使用 $1$9 从匹配部分访问存储的匹配项。

有关更多信息,请参阅链接以及 perldoc perlretut (tutorial)perldoc perlre (full-ish documentation)

【讨论】:

    【解决方案3】:
    #!/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";
    

    【讨论】:

    • Can't locate Regexp/Common.pm - 我猜它是一个附加组件?
    【解决方案4】:

    使用 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 标志。如果有人知道如何避免这种情况,请告诉我。

    【讨论】:

    • %$^#,而我正在研究这个 tadmc 和 Qtax 在我之前到达那里!
    猜你喜欢
    • 1970-01-01
    • 2021-09-26
    • 2020-07-23
    • 1970-01-01
    • 2014-10-16
    • 1970-01-01
    • 1970-01-01
    • 2022-12-22
    • 1970-01-01
    相关资源
    最近更新 更多