我是Perl的新手,我发现了一些我不理解也无法解决的行为。
我正在制作一个小的查找和替换程序,我需要做一些事情。我有一堆我需要处理的文件。然后我在外部文本文件中有一个查找/替换规则列表。在替换那里我需要三件特别的东西:
更换utf-8字符(捷克变音符号)
使用添加/删除行(所以在slurp模式下工作)
使用正则表达式
我想要一个单独工作的程序,所以我写了它以便它需要三个参数:
我从bash脚本循环发送参数,该脚本解析规则列表并加载其他文件。
我的问题是当我在规则列表中有一个"\n"
字符串并将其发送到Perl脚本时。如果它位于替换的第一部分(在查找部分中),它会正确地查找换行符,但是当它位于第二部分(替换部分)时,它只会打印\n
而不是换行符。
我尝试将"\n"
硬编码到变量中的字符串,而不是从列表中传递它,然后它工作正常。
Perl没有解释那里的"\n"
字符串是什么原因,我怎么能让它工作?
这是我的代码:
list.txt - 来自外部替换列表的一行
1\. ?\\n?NÁZEV PŘÍPRAVKU;\\n<<K1>> NÁZEV PŘÍPRAVKU;
farkapitoly.sh - 用于解析list.txt
并循环遍历所有文件并调用Perl脚本的bash脚本
...
FILE="/home/tmp.txt"
while read LINE
do
FIND=`echo "$LINE" | awk -F $';' 'BEGIN {OFS = FS} {print $1}'`
REPLACE=`echo "$LINE" | awk -F $';' 'BEGIN {OFS = FS} {print $2}'`
perl -CA ./pathtiny.pl "$FILE" "$FIND" "$REPLACE"
done < list.txt
...
pathtiny.pl - 用于查找和替换的Perl脚本
#!/usr/bin/perl
use strict;
use warnings;
use Modern::Perl;
use utf8; # Enable typing Unicode in Perl strings
use open qw(:std :utf8); # Enable Unicode to STDIN/OUT/ERR and filehandles
use Path::Tiny;
my $file = path("$ARGV[0]");
my $searchStr = "$ARGV[1]";
my $replaceStr = "$ARGV[2]";
# $replaceStr="\n<<K1>> NÁZEV PRÍPRAVKU"; # if I hardcode it here \n is replaced right away
print("Search String:", "$searchStr", "\n");
print("Replace String:", "$replaceStr", "\n\n");
my $guts = $file->slurp_utf8;
$guts =~ s/$searchStr/$replaceStr/gi;
$file->spew_utf8($guts);
如果它很重要,我在VirtualBox上使用Linux Mint 13 64位(在Win 8.1下),我有Perl v5.14.2。每个文件都是带有Linux结尾的UTF-8。
可以在pastebin
上找到示例文件。 this最终应该像this一样。
但是例子变化很大。我需要一个通用的解决方案来在替换字符串中写下换行符,以便正确替换它。
答案 0 :(得分:3)
问题是替换字符串是从文件中逐字读取的,因此如果您的文件包含
xx\ny
然后你会读到这六个字符。此外,替换的替换部分被评估为使用双引号。所以你的替换字符串是"$replaceStr"
,它插入变量而不再进一步,所以你将在新字符串中再次使用xx\nyy
。 (顺便说一句,请避免在本地Perl标识符中使用大写字母,因为实际上它们是为Module::Names
等全局变量保留的。)
答案在于使用eval
或其等价物 - 替换上的/e
修饰符。
如果我写
my $str = '<b>';
my $r = 'xx\ny';
$str =~ s/b/$r/;
然后将替换字符串内插到xx\ny
,如您所见。
单个/e
修饰符会将替换值评估为表达式而不仅仅是双引号字符串,但当然$r
表达式为xx\ny
试。
您需要的是第二个/e
修饰符,它与单个/e
执行相同的评估,然后在顶部执行额外的eval
结果。为此,如果您使用qq{ .. }
,则需要两个级别的报价,这是最干净的。
如果你写
$str =~ s/b/qq{"$r"}/ee
然后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;
<强>输出强>
<xx
yy>
但请不要忘记,如果您的替换字符串包含任何双引号,例如
my $r = 'xx\n"yy"'
然后他们必须在通过替换之前进行转义,因为表达式本身也使用双引号。
所有这一切都很难掌握,所以你可能更喜欢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;
并且输出与前一代码的输出相同。
<强>更新强>
以下是使用String::Escape
的原始程序的重构。我已删除Path::Tiny
,因为我认为最好使用Perl的内置 inplace-edit 扩展程序,该扩展程序在General Variables {{3}}部分中有说明1}}。
perlvar
答案 1 :(得分:2)
你有\n
作为字符串的内容。 (作为两个字符1:\
和第二个n
,而不是一个newline
。
Perl将\n
解释为换行符(例如它在您的代码中)。
快速修复将是:
my $replaceStr=eval qq("$ARGV[2]"); #evaling a string causes interpreting the \n as literal
或者,如果你不喜欢eval,你可以使用String-Escape cpan模块。 (unbackslash函数)
答案 2 :(得分:0)
您希望将文字字符串视为双引号字符串。要做到这一点,你必须翻译任何反斜杠后跟另一个角色。
其他专家已经向您展示了如何在整个字符串上执行此操作(由于它使用eval
未经验证的数据,因此存在风险)。或者,你可以使用一个模块String::Escape
,它需要安装(不是高栏,但对某些人来说太高)。
但是,以下内容以安全的方式转换返回值字符串本身,然后在其他搜索中将其用作普通值并替换:
use strict;
use warnings;
my $r = 'xx\nyy';
$r =~ s/(\\.)/qq{"$1"}/eeg; # Translate \. as a double quoted string would
print $r;
输出:
xx
yy