gpt4 book ai didi

regex - 在 Perl 替换中使用包含文字转义的字符串变量

转载 作者:行者123 更新时间:2023-12-04 16:55:10 24 4
gpt4 key购买 nike

我是 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 一样结束。

    但是例子千差万别。我需要一个通用的解决方案来在替换字符串中写下换行符,以便正确替换。

    最佳答案

    问题是替换字符串是从文件中逐字读取的,所以如果您的文件包含

    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 下。 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;
    }

    关于regex - 在 Perl 替换中使用包含文字转义的字符串变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25309798/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com