ato*_*c44 5 regex perl replace
我是 Perl 新手,我发现了我不理解且无法解决的行为。
\n\n我正在制作一个小型查找和替换程序,我需要做一些事情。我有一堆文件需要处理。然后我在外部文本文件中有一个查找/替换规则列表。在替换那里我需要三个特殊的东西:
\n\n替换 utf-8 字符(捷克语变音符号)
添加/删除行(因此在吸食模式下工作)
使用正则表达式
我想要一个单独运行的程序,所以我编写了它,以便它需要三个参数:
\n\n我正在从 bash 脚本循环发送参数,该脚本解析规则列表并加载其他文件。
\n\n我的问题是当我"\\n"
在规则列表中有一个字符串并将其发送到 Perl 脚本时。如果它位于替换的第一部分(在查找部分),它会正确查找换行符,但当它位于第二部分(替换部分)时,它只会打印而\\n
不是换行符。
我尝试将"\\n"
字符串硬编码到变量中,而不是从列表中传递它,然后它工作正常。
Perl 不解释该"\\n"
字符串的原因是什么?我怎样才能让它工作?
这是我的代码:
\n\nlist.txt - 外部替换列表中的一行
\n\n1\\. ?\\\\n?N\xc3\x81ZEV P\xc5\x98\xc3\x8dPRAVKU;\\\\n<<K1>> N\xc3\x81ZEV P\xc5\x98\xc3\x8dPRAVKU;\n
Run Code Online (Sandbox Code Playgroud)\n\nfarkapitoly.shlist.txt
- 用于解析和循环所有文件并调用 Perl 脚本的bash 脚本
...\nFILE="/home/tmp.txt"\nwhile read LINE\ndo\n FIND=`echo "$LINE" | awk -F $\';\' \'BEGIN {OFS = FS} {print $1}\'`\n REPLACE=`echo "$LINE" | awk -F $\';\' \'BEGIN {OFS = FS} {print $2}\'`\n perl -CA ./pathtiny.pl "$FILE" "$FIND" "$REPLACE" \ndone < list.txt\n...\n
Run Code Online (Sandbox Code Playgroud)\n\npathtiny.pl - 用于查找和替换的 Perl 脚本
\n\n#!/usr/bin/perl\nuse strict;\nuse warnings;\nuse Modern::Perl;\nuse utf8; # Enable typing Unicode in Perl strings\nuse open qw(:std :utf8); # Enable Unicode to STDIN/OUT/ERR and filehandles\n\nuse Path::Tiny;\n\nmy $file = path("$ARGV[0]");\nmy $searchStr = "$ARGV[1]";\nmy $replaceStr = "$ARGV[2]";\n\n# $replaceStr="\\n<<K1>> N\xc3\x81ZEV PR\xc3\x8dPRAVKU"; # if I hardcode it here \\n is replaced right away\nprint("Search String:", "$searchStr", "\\n");\nprint("Replace String:", "$replaceStr", "\\n\\n");\n\nmy $guts = $file->slurp_utf8;\n$guts =~ s/$searchStr/$replaceStr/gi;\n$file->spew_utf8($guts);\n
Run Code Online (Sandbox Code Playgroud)\n\n如果重要的话,我在 VirtualBox 上使用 Linux Mint 13 64 位(在 Win 8.1 下)并且有 Perl v5.14.2。每个文件都是 UTF-8,并带有 Linux 结尾。
\n\n\n\n但例子差异很大。我需要一个通用的解决方案来在替换字符串中写下换行符,以便它可以正确替换。
\n问题是替换字符串是从文件中逐字读取的,因此如果您的文件包含
xx\ny
Run Code Online (Sandbox Code Playgroud)
那么你就会准确地读出这六个字符。此外,替换的替换部分会被评估,就好像它在双引号中一样。因此,您的替换字符串将"$replaceStr"
插入变量并且不再继续,因此您将再次使用xx\nyy
新字符串。(顺便说一句,请避免在本地 Perl 标识符中使用大写字母,因为实际上它们是为全局变量保留的,例如Module::Names
。)
答案在于使用eval
或其等效项 -/e
替换修饰符。
如果我写
my $str = '<b>';
my $r = 'xx\ny';
$str =~ s/b/$r/;
Run Code Online (Sandbox Code Playgroud)
然后替换字符串将被插入到xx\ny
,正如您所经历的那样。
单个/e
修饰符将替换计算为表达式,而不仅仅是双引号字符串,但当然又$r
是表达式xx\ny
。
您需要的是第二个/e
修改器,它执行与单个修改器相同的评估/e
,然后eval
在顶部执行附加结果。为此,如果您qq{ .. }
需要两级报价,那么使用它是最干净的。
如果你写
$str =~ s/b/qq{"$r"}/ee
Run Code Online (Sandbox Code Playgroud)
然后 perl 将计算qq{"$r"}
为一个表达式,给出"xx\nyy"
,再次计算时将给出您需要的字符串 - 与表达式 相同'xx' . "\n" . 'yy'
。
这是一个完整的程序
use strict;
use warnings;
my $s = '<b>';
my $r = 'xx\nyy';
$s =~ s/b/qq{"$r"}/ee;
print $s;
Run Code Online (Sandbox Code Playgroud)
输出
<xx
yy>
Run Code Online (Sandbox Code Playgroud)
但不要忘记,如果您的替换字符串包含任何双引号,如下所示
my $r = 'xx\n"yy"'
Run Code Online (Sandbox Code Playgroud)
那么在进行替换之前必须对它们进行转义,因为表达式本身也使用双引号。
所有这些都很难掌握,因此您可能更喜欢该String::Escape
模块,该模块具有一个unbackslash
功能,可以将字符串中的文字\n
(和任何其他转义符)更改为其等效的字符"\n"
。它不是核心模块,因此您可能需要安装它。
优点是您不再需要双重计算,因为如果替换字符串unbackslash $r
作为表达式计算,则可以给出正确的结果。它还可以$r
毫无问题地处理双引号,因为表达式本身不使用双引号。
使用的代码String::Escape
是这样的
use strict;
use warnings;
use String::Escape 'unbackslash';
my $s = '<b>';
my $r = 'xx\nyy';
$s =~ s/b/unbackslash $r/e;
print $s;
Run Code Online (Sandbox Code Playgroud)
并且输出与之前的代码相同。
更新
这是使用 的原始程序的重构String::Escape
。我已经删除了,Path::Tiny
因为我认为最好使用 Perl 的内置就地编辑扩展,该扩展记录在perlvar
.
#!/usr/bin/perl
use utf8;
use strict;
use warnings;
use 5.010;
use open qw/ :std :utf8 /;
use String::Escape qw/ unbackslash /;
our @ARGV;
my ($file, $search, $replace) = @ARGV;
print "Search String: $search\n";
print "Replace String: $replace\n\n";
@ARGV = ($file);
$^I = '';
while (<>) {
s/$search/unbackslash $replace/eg;
print;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2219 次 |
最近记录: |