regex - 如何正确设置正则表达式以用 perl 替换多行变量占位符

标签 regex perl template-meta-programming

我正在使用 Perl 5.20 为数据转换工具编写复杂的配置文件。

配置文件在加载时和运行时有几个方面的占位符,封装了一些path code喜欢

# Load time placeholder example
CONFIG: NAME ${/path/pos/*/test/*[123] == 'ABC' }

# Runtime placeholder example
COLUMN: CSV_NAME STRING DEFAULT :{./CSV_FIRST} 

出于某种原因,它也应该适用于多行表达式。

我已经使用 Text::ParseWord(标准分隔符 \s+)编写了扫描器,并且希望在将数据行拆分为单个数据行之前转义占位符表达式由 base64 编码的表达式不包含 \s+ 的单词。这些表达式也是后续数据替换的关键。

转义是由模式匹配驱动的(...参见下面的代码):

 my @pat = $line =~ /([^\\]\Q$pfx\E\{[^\Q$pfx\E\{\}]+\})/gs;

例如,当我使用 $pfx = '$' 时,IMO 定义了多行模式 ${...},但掩码(转义)\${...} 表达式。

问题

我在模式 ... 的内部部分挣扎了一段时间,并得到 [^\Q$pfx\E\{\}]+工作,但感觉不正确,因为

  1. 它仅包含设置不使用的符号,
  2. 但不是外部部分的顺序,
  3. 例如,防止嵌套表达式。

执行此操作的正确表达方式是什么?

测试例程

#!/usr/bin/env perl
use strict;
use warnings;
use MIME::Base64;

use feature qw(signatures);
no warnings 'once';
no warnings 'experimental';
no warnings 'experimental::signatures';

my $line =
'# test data
 This are:
 1. ${/multiline/used/*[3]
      = "12345"}
 2. ${/single/line/compile/time/pattern/*[3]}
 3. ${/single/line/runtime/pattern/x == 1234}
 4. ${/multi/line/runtime/pattern \
      defer/1 \
      defer/2 \
      defer/3
     }
 5. ${//PG.GRM/*[
        key eq "TEST.VAR"
       ]}
';

sub testSpacedPlaceHolder($pfx, $line) {
    my %match;
    
    my @pat = $line =~ /([^\\]\Q$pfx\E\{[^\Q$pfx\E\{\}]+\})/gs;
    my %seen = ();
    my @uniq = grep { ! $seen{$_} ++ } @pat;    
    for my $key (@uniq) {
        my $hkey=$pfx.encode_base64($key);
        $hkey =~ s/\n//g;
        my $var = substr($key, 3, -1);
        $match{$hkey}= [ $var, $key ];
        $line =~ s/\Q$key\E/$hkey/g;
    }
    # Test the output ------------------------------
    my $cnt = 0;
    print "\nRESULT:\n";
    for my $key (sort keys %match) {
        $cnt++;
        my ($var, $orig) = @ { $match{$key} };
        print "---- $cnt ----\n";
        print "ORG: $orig\n";
        print "VAR: $var\n";
        print "ESC: $key\n";

    }
    print "\nLINE:\n$line\n";
    return ($line, \%match);
}

testSpacedPlaceHolder('$', $line);

结果

/usr/bin/env perl "test-strings.pl"

RESULT:
---- 1 ----
ORG:  ${/multi/line/runtime/pattern \
      defer/1 \
      defer/2 \
      defer/3
     }
VAR: /multi/line/runtime/pattern \
      defer/1 \
      defer/2 \
      defer/3
     
ESC: $ICR7L211bHRpL2xpbmUvcnVudGltZS9wYXR0ZXJuIFwKICAgICAgZGVmZXIvMSBcCiAgICAgIGRlZmVyLzIgXAogICAgICBkZWZlci8zCiAgICAgfQ==
---- 2 ----
ORG:  ${/multiline/used/*[3]
      = "12345"}
VAR: /multiline/used/*[3]
      = "12345"
ESC: $ICR7L211bHRpbGluZS91c2VkLypbM10KICAgICAgPSAiMTIzNDUifQ==
---- 3 ----
ORG:  ${/single/line/compile/time/pattern/*[3]}
VAR: /single/line/compile/time/pattern/*[3]
ESC: $ICR7L3NpbmdsZS9saW5lL2NvbXBpbGUvdGltZS9wYXR0ZXJuLypbM119
---- 4 ----
ORG:  ${/single/line/runtime/pattern/x == 1234}
VAR: /single/line/runtime/pattern/x == 1234
ESC: $ICR7L3NpbmdsZS9saW5lL3J1bnRpbWUvcGF0dGVybi94ID09IDEyMzR9
---- 5 ----
ORG:  ${//PG.GRM/*[
        key eq "TEST.VAR"
       ]}
VAR: //PG.GRM/*[
        key eq "TEST.VAR"
       ]
ESC: $ICR7Ly9QRy5HUk0vKlsKICAgICAgICBrZXkgZXEgIlRFU1QuVkFSIgogICAgICAgXX0=

LINE:
# test data
 This are:
 1.$ICR7L211bHRpbGluZS91c2VkLypbM10KICAgICAgPSAiMTIzNDUifQ==
 2.$ICR7L3NpbmdsZS9saW5lL2NvbXBpbGUvdGltZS9wYXR0ZXJuLypbM119
 3.$ICR7L3NpbmdsZS9saW5lL3J1bnRpbWUvcGF0dGVybi94ID09IDEyMzR9
 4.$ICR7L211bHRpL2xpbmUvcnVudGltZS9wYXR0ZXJuIFwKICAgICAgZGVmZXIvMSBcCiAgICAgIGRlZmVyLzIgXAogICAgICBkZWZlci8zCiAgICAgfQ==
 5.$ICR7Ly9QRy5HUk0vKlsKICAgICAgICBrZXkgZXEgIlRFU1QuVkFSIgogICAgICAgXX0=

编辑

假设我有一个定义某种配置的脚本:

MAGIC: MAGIC.TYPE

CONTAINER: NAME BEGIN

DEFINE: VAR1 'USER.NAME'
DEFINE: VAR2 '65789'

INTERNAL.CONTAINER: INTERNAL.NAME BEGIN

    TAG1: 'ABCDEF'
    TAG2: ${/NAME/VAR1}

    # Unwanted nested variant 
    TAG3: ${/NAME/VAR1 ${/NAME/VAR2} }

    # Valid runtime interpolation variant
    TAG4: "${/NAME/VAR1}/:{NAME.KEY}"
    
    # Valid runtime path variant but ignored
    TAG5: ${/NAME/VAR1/*/:{TEST{KEY}}

END.INTERNAL.NAME
 
END.NAME 

我想避免嵌套行

    # Nested variant 
    TAG3: ${/NAME/VAR1 ${/NAME/VAR2}}

出于变量解析的原因,但保留

    # Valid runtime path variant but ignored
    TAG5: ${/NAME/VAR2/*/:{TEST{KEY}}

因为它们是运行时驱动的。

由于简单的序列[\$\{\}]+,我的变体会阻止 TAG5。

最佳答案

以下示例说明了如何使用递归正则表达式排除 ${...} 的嵌套版本:

use feature qw(say);
use strict;
use warnings;
use Data::Dumper qw(Dumper);
my $str = <<'END_STR';
MAGIC: MAGIC.TYPE

CONTAINER: NAME BEGIN

DEFINE: VAR1 'USER.NAME'
DEFINE: VAR2 '65789'

INTERNAL.CONTAINER: INTERNAL.NAME BEGIN

    TAG1: 'ABCDEF'
    TAG2: ${/NAME/VAR1}

    # Unwanted nested variant
    TAG3: ${/NAME/VAR1 ${/NAME/VAR2} }

    # Valid runtime interpolation variant
    TAG4: "${/NAME/VAR1}/:{NAME.KEY}"

    # Valid runtime path variant but ignored
    TAG5: ${/NAME/VAR1/*/:{TEST{KEY}}}

END.INTERNAL.NAME

END.NAME
END_STR

my @matches;
while ($str =~ /(?:^|(?<!\\))
                   (?<G3>(?<G1> \$ \{ (?:
                       (?>(?:[^{}\$\\] | (?:\\.) |
                         (?<G2> \{ (?: (?>[^{}]+) | (?&G2))* \} ))
                           | (?<G4>(?&G1))))* \}))/msxg) {
    next if defined $+{G4}; # skip nested matches
    push @matches, $+{G3};
}
print Dumper(\@matches);

输出:

$VAR1 = [
          '${/NAME/VAR1}',
          '${/NAME/VAR1}',
          '${/NAME/VAR1/*/:{TEST{KEY}}}'
        ];

请注意,结果包括 TAG2、TAG4 和 TAG5,但不包括 TAG3。

关于regex - 如何正确设置正则表达式以用 perl 替换多行变量占位符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70903298/

相关文章:

c++ - 检查类型实例是否可以流式传输

java - 带有 cucumber 的正则表达式与给出错误的字符串不匹配,但如果多次写入测试,则会匹配相同的字符串

php - 是否使用 MySQL 中的当前/默认字符集转换 BLOB?

perl - 从Perl脚本创建/引发SIGINT(Ctrl-C)-并导致从模具处理程序进入调试器进入步骤模式?

c++ - C++ 模板中的模板参数

c++ - 在没有编译错误的情况下,基于静态条件延迟对非嵌套类型的引用?

javascript - 使用 javascript 检查表单

php - 对于非常基本的 Markdown ,这些正则表达式可以工作吗?

python - 通过python正则表达式忽略ip地址第一个八位字节中的这两个数字(以127或0开头)

perl - 如何在 Perl 模块中包含数据文件?