如何避免Perl正则表达式替换中的警告?

How to avoid warnings in Perl regex substitution with alternatives?

提问人:okolnost 提问时间:10/28/2018 最后编辑:okolnost 更新时间:10/28/2018 访问量:148

问:

我有这个正则表达式。

$string =~ s/(?<!["\w])(\w+)(?=:)|(?<=:)([\w\d\\.+=\/]+)/"$1$2"/g;

正则表达式本身工作正常。

但是,由于我正在替换替代方案(并且在全球范围内),我总是收到警告,即 1 美元或 2 美元未初始化。这些警告使我的日志文件混乱不堪。

我能做些什么来避免这种警告? 还是我最好的选择是关闭警告?我对此表示怀疑。

附带问题:是否有更好的方法可以做到这一点,例如根本不使用正则表达式? 我正在做的是修复 JSON,其中某些键值对没有双引号,并且 JSON 模块在尝试解码时不喜欢它。

正则表达式 Perl 警告 替换

评论

0赞 Wiktor Stribiżew 10/28/2018
如果用 ?顺便说一句,您当前的模式无效。$1$2$&
0赞 okolnost 10/28/2018
@WiktorStribiżew是的,我刚刚修复了正则表达式中的错别字,感谢您的注意。 看起来很有前途(我的意思是,绝对比我现在拥有的要好)。如果两个选项都不匹配,使用它会产生警告吗?如果是这样,知道如何避免这种情况吗?$&
1赞 Wiktor Stribiżew 10/28/2018
$&是整个匹配占位符,如果使用它,请删除所有捕获组,因为它们变得多余 ()。如果需要使用 ,请使用s/(?<!["\w])\w+(?=:)|(?<=:)[\w\\.+=\/]+/"$&"/g$1s/(?|(?<!["\w])(\w+)(?=:)|(?<=:)([\w\\.+=\/]+))/"$1"/g
1赞 Grinnz 10/28/2018
由于 $1 或 $2 未定义(不幸的是,警告措辞错误)并导致空字符串实际上不是问题,因此您还可以在运行该正则表达式的词法范围内禁用该警告。{ no warnings 'uninitialized'; $string =~ ...; }

答:

3赞 user557597 10/28/2018 #1

有几种方法可以解决这个问题。

如果您打算使用捕获组:

  • 当捕获交替的每个子句的全部内容时。
    将捕获组合并为 1 个,然后将该组移出。

     (                             # (1 start)
          (?<! ["\w] )
          \w+ 
          (?= : )
       |  
          (?<= : )
          [\w\d\\.+=/]+ 
     )                             # (1 end)
    

    s/((?<!["\w])\w+(?=:)|(?<=:)[\w\d\\.+=\/]+)/"$1"/g

  • 使用 Branch Reset 构造。
    这将导致每个交替中的捕获组从同一点开始对其组
    进行编号。
    (?| aaa )

     (?|
          (?<! ["\w] )
          ( \w+ )                       # (1)
          (?= : )
       |  
          (?<= : )
          ( [\w\d\\.+=/]+ )             # (1)
     )
    

    s/(?|(?<!["\w])(\w+)(?=:)|(?<=:)([\w\d\\.+=\/]+))/"$1"/g

  • 使用可重复使用的命名捕获组(类似于分支重置)。
    在每次交替中,重复使用相同的名称。将不相关的组设为空组。
    这通过在替换中使用名称而不是数字来工作。

        (?<! ["\w] )
        (?<V1> \w+ )                  # (1)
        (?<V2> )                      # (2)
        (?= : )
     |  
        (?<= : )
        (?<V1> )                      # (3)
        (?<V2> [\w\d\\.+=/]+ )        # (4)
    

    s/(?<!["\w])(?<V1>\w+)(?<V2>)(?=:)|(?<=:)(?<V1>)(?<V2>[\w\d\\.+=\/]+)/"$+{V1}$+{V2}"/g


如果备用包含 1 个以上的捕获组,则可以将命名替换和分支重置这两个概念组合在一起

以下示例使用捕获组编号。

理论上,您将虚拟捕获组放在每个交替中,以“填充”分支以
等于单个交替中的最大组数。

事实上,必须这样做以避免 Perl 正则表达式中可能导致崩溃的错误。

 (?|                    # Branch Reset
                             # ------ Br 1 --------
      ( )                    # (1)
      ( \d{4} )              # (2)
      ABC294
      ( [a-f]+ )             # (3)
   |  
                             # ------ Br 2 --------          
      ( :: )                 # (1)
      ( \d+ )                # (2)
      ABC555
      ( )                    # (3)
   |  
                             # ------ Br 3 --------
      ( == )                 # (1)
      ( )                    # (2)
      ABC18888
      ( )                    # (3)
 )

s/(?|()(\d{4})ABC294([a-f]+)|(::)(\d+)ABC555()|(==)()ABC18888())/"$1$2$3"/g

1赞 Grinnz 10/28/2018 #2

您可以尝试使用 Cpanel::JSON::XS 的宽松模式或 JSONY)来解析几乎 JSON,然后使用 Cpanel::JSON::XS 写出常规 JSON。根据输入数据到底出了什么问题,一个人或另一个人可能会更好地理解它。

use strict;
use warnings;
use Cpanel::JSON::XS 'encode_json';

# JSON is normally UTF-8 encoded; if you're reading it from a file, you will likely need to decode it from UTF-8
my $string = q<{foo: 1,bar:'baz',}>;

my $data = Cpanel::JSON::XS->new->relaxed->decode($string);
my $json = encode_json $data;
print "$json\n";

use JSONY;
my $data = JSONY->new->load($string);
my $json = encode_json $data;
print "$json\n";