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

如何在Perl中将转义字符转换为实际的特殊字符?,perl,escaping,Perl,Escaping,可能重复: 我正在从特定文件中读取字符串。问题在于它包含转义字符,如: Hello!\nI\'d like to tell you a little \"secret\"... 我希望打印出来时没有转义序列,比如: Hello! I'd like to tell you a little "secret". 我曾考虑过删除单个反斜杠,并将double替换为single(因为\表示为\\),但这对解决\n、\t等问题没有帮助。在尝试摆弄丑陋、复杂的替换字符串之前,我想问一下——也许Perl有

可能重复:

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

Hello!\nI\'d like to tell you a little \"secret\"...
我希望打印出来时没有转义序列,比如:

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

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

我不想这么说,但是string
eval
可以解决问题,但是string
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"]}.
#/usr/bin/perl
严格使用;
使用警告;
while(my$input=){
#注意:仅当字符串中不允许#作为字符时,此选项才有效
my$string=eval“qq#$input#”或die$@;
打印$string;
}
__资料__
你好!\我想告诉你一个小秘密。
这太糟糕了@{[打印“我已经赢了你”]}。
另一种解决方案是创建一个散列,定义所有要实现的转义并进行替换。

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

考虑:

#!/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
s/\\([rnt'\\])/“qq\\\$1”/gee
起作用

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

  • gee
    部分对替换字符串进行双重求值

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

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

此方法无法正确处理以下问题:

  • 带引号的字符串。例如,由于单引号的缘故,Perl不会取消对字符串“line 1\nline 2”的scape

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

  • 锚定转义,例如\LMAKE LOWER CASE\E或\Umake upper CASE\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
#/usr/bin/perl
使用警告;
严格使用;
打印“\n\n\n\n”;
binmode标准输出“:utf8”;
while(my$data=){
$data=~s/\\(
(?:[arnt'\\])|#单字符转义
(?:[ul])|#uc或lc下一个字符
(?:x[0-9a-fA-F]{2})|#2位十六进制转义
(?:x\{[0-9a-fA-F]+\})大于2位十六进制
(?:\d{2,3})|#八进制
(?:N\{U\+[0-9a-fA-F]{2,4}})#十六进制的unicode
)/“qq\\\$1”/geex;
打印$数据;
}
__资料__
你好!\n我想告诉你一点“秘密”。
这是八进制:\120
这是UNICODE:\N{U+0041}和\N{U+41}和\N{U+263D}
这里有一个小十六进制:\x50\x5fa\x{5fa}\x{263B}
小写下一个字符\lU\lA
大写下一字符\ua\uu
反激光:\\
制表符'\t'停止
第1行\r第2行(在Unix上,“第1行”将被覆盖)
第3行\\n第4行(应导致“第3行\\n第4行”)
第5行\r\n第6行
它处理除以下内容之外的所有Perl:

  • 锚定类型(\Q\U\L以\E结尾)

  • 带引号的表单,例如
    “不要用单引号转义”
    [不在这里]

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

  • 控制字符,如
    \cD
    (易于添加…)


  • 但据我所知,这不是你问题的一部分…

    这是一个本地应用程序,一个命令行脚本,用于解析来自其他实用程序的日志文件。如果是这样的话,我认为eval不会有太大的安全漏洞,对吧?你在评估日志文件中的内容吗?如果是这样,数据是如何进入日志文件的?如果所有用户都没有你要做的就是精心设计正确的消息来破坏或破坏你的代码,然后他们就会这样做。更好的选择是修复正在编写日志文件的人,使用标准化的方法来转义特殊字符,如RFC 3986中的字符(即URI转义)。第一次替换非常有效,谢谢!
    #!/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