如何将转义字符转换为Perl中的实际特殊字符?

时间:2010-10-02 11:46:41

标签: perl escaping

  

可能重复:
  How can I manually interpolate string escapes in a Perl string?

我正在读取特定文件中的字符串。它的问题在于它包含转义字符,例如:

Hello!\nI\'d like to tell you a little \"secret\"...

我希望它在没有转义序列的情况下打印出来,例如:

Hello!
I'd like to tell you a little "secret".

我考虑过删除单个反斜杠并用单个替换double(因为\表示为\\),但这对我没有帮助\ n,\ t问题等等。在试图摆弄丑陋,复杂的替换字符串之前,我想我会问 - 也许Perl有这种转换的内置机制?

2 个答案:

答案 0 :(得分:4)

对于Perl单个字符backslash escapes,您可以使用两个字符eval作为替换的一部分安全地执行此操作。您需要在\之后插入可在字符类中解释的字符,然后在eval之后插入单个字符并插入字符串中。

考虑:

#!/usr/bin/perl
use warnings;
use strict;

print "\n\n\n\n";

while (my $data = <DATA>) {
    $data=~s/\\([rnt'"\\])/"qq|\\$1|"/gee;
    print $data;
}

__DATA__
Hello!\nI\'d like to tell you a little \"secret\".
A backslask:\\
Tab'\t'stop
line 1\rline 2  (on Unix, "line 1" will get overwritten)
line 3\\nline 4 (should result in "line 3\\nline 4")
line 5\r\nline 6

输出:

Hello!
I'd like to tell you a little "secret".
A backslask:\
Tab'    'stop
line 2  (on Unix, "line 1" will get overwritten)
line 3\nline 4 (should result in "line 3\nline 4")
line 5
line 6

s/\\([rnt'"\\])/"qq|\\$1|"/gee完成工作。

  • \\([rnt'"\\])在大括号内有可接受的字符值。

  • gee部分对替换字符串执行双重评估。

  • "qq|\\$1|"部分被评估两次。第一个eval$1替换为字符串,第二个执行插值。

我想不出这里会出现安全漏洞的两个字符组合......

此方法正确处理以下内容:

  • 引用字符串。例如,由于单引号,Perl不会取消字符串'line 1 \ nline 2'。

  • 转义超过单个字符的序列,例如十六进制\x1b或Unicode(例如\N{U+...})或控制序列(例如\cD

  • 锚定转义,例如\ LMAKE LOWER CASE \ E或\ Umake大写\ E

如果您想要更完整的转义替换,可以使用此正则表达式:

#!/usr/bin/perl
use warnings;
use strict;

print "\n\n\n\n";

binmode STDOUT, ":utf8";

while (my $data = <DATA>) {
    $data=~s/\\(
        (?:[arnt'"\\]) |               # Single char escapes
        (?:[ul].) |                    # uc or lc next char
        (?:x[0-9a-fA-F]{2}) |          # 2 digit hex escape
        (?:x\{[0-9a-fA-F]+\}) |        # more than 2 digit hex
        (?:\d{2,3}) |                  # octal
        (?:N\{U\+[0-9a-fA-F]{2,4}\})   # unicode by hex
        )/"qq|\\$1|"/geex;  
    print $data;
}

__DATA__
Hello!\nI\'d like to tell you a little \"secret\".
Here is octal: \120 
Here is UNICODE: \N{U+0041} and \N{U+41} and \N{U+263D}
Here is a little hex:\x50 \x5fa \x{5fa} \x{263B}
lower case next char \lU \lA
upper case next char \ua \uu
A backslask:\\
Tab'\t'stop
line 1\rline 2  (on Unix, "line 1" will get overwritten)
line 3\\nline 4 (should result in "line 3\\nline 4")
line 5\r\nline 6

除了处理所有Perl escapes之外:

  1. 锚定类型(\ Q,\ U,\ L以\ E结尾)

  2. 引用的表单,例如'don't \n escape in single quotes'[not \n in here]

  3. 命名的unicode字符,例如\N{THAI CHARACTER SO SO}

  4. 控制\cD等字符(很容易添加......)

  5. 但这不是你问题的一部分,因为我明白了......

答案 1 :(得分:3)

我讨厌建议这一点,但字符串eval可以解决问题,但字符串eval会带来一系列安全和维护问题。这些数据来自哪里?数据生产者之间是否有任何关于字符串将保留的内容的合同?

#!/usr/bin/perl

use strict;
use warnings;

while (my $input = <DATA>) {
    #note: this only works if # is not allowed as a character in the string
    my $string = eval "qq#$input#" or die $@;
    print $string;
}

__DATA__
Hello!\nI\'d like to tell you a little \"secret\".
This is bad @{[print "I have pwned you\n"]}.

另一种解决方案是创建一个哈希,定义您要实现的所有转义并进行替换。