用成对分隔符封闭仍然没有封闭的字符串

nov*_*cik 5 regex perl

需要用封闭的分隔符封装仍然没有封闭的字符串.示例文字:

Some text or random characters here. {% Another random string
enclosed in a pair of delimiters as next {% what can be deeply
nested {% as {%here%}%} end of delimited %} text. %}Another
bla-bla

random text outside of the delimiters - called
as "free text".
Run Code Online (Sandbox Code Playgroud)

需要附上出现的所有自由文本

%{ORIG .... original free text ... %}
Run Code Online (Sandbox Code Playgroud)

并且不要修改已经包含的字符串.所以,在上面的例子中需要包含两段自由文本,并且应该得到下一个:

{%ORIG Some text or random characters here. %}{% Another random string
enclosed in a pair of delimiters as next {% what can be deeply
nested {% as {%here%}%} end of delimited %} text. %}{%ORIG Another
bla-bla

random text outside of the delimiters - called
as "free text".%}
Run Code Online (Sandbox Code Playgroud)

因此,开头定界符是{%关闭的%}.

问题:

  • 可以用"regexes"来做到这一点,或者我需要为此编写一些解析器吗?
  • 存在一些CPAN模块我可以用于此任务吗?

Qta*_*tax 6

您可以使用regex在递归子模式调用的(?R)帮助下完成它.

例如:

$_ = <<'_STR_';
Some text or random characters here. {% Another random string
enclosed in a pair of delimiters as next {% what can be deeply
nested {% as {%here%}%} end of delimited %} text. %}Another
bla-bla

random text outside of the delimiters - called
as "free text".
_STR_

s/
  ( {% (?R)* %} )            # match balanced {% %} groups
|
  ( (?: (?! {% | %} ) . )+ ) # match everything except {% %}
/
  $1 ? $1 : "{%ORIG $2 %}";  # if {% ... %} matched, leave it as is. else enclose it
/gsex;

print;
Run Code Online (Sandbox Code Playgroud)

输出:

{%ORIG Some text or random characters here.  %}{% Another random string
enclosed in a pair of delimiters as next {% what can be deeply
nested {% as {%here%}%} end of delimited %} text. %}{%ORIG Another
bla-bla

random text outside of the delimiters - called
as "free text".
 %}
Run Code Online (Sandbox Code Playgroud)


Bir*_*rei 5

Jonathan Leffler的建议是正确的.您可以使用Text::Balanced具有其extract_tagged功能的模块来解决此问题:

#!/usr/bin/env perl

use warnings;
use strict;
use Text::Balanced qw<extract_tagged>;

my ($open_delim, $close_delim) = qw( {% %} );

my $text = do { local $/ = undef; <> };
chomp $text;

while (1) {
    my @r = extract_tagged($text, $open_delim, $close_delim, '(?s).*?(?={%)', undef);
    if (length $r[2]) {
        printf qq|%sORIG %s%s|, $open_delim, $r[2], $close_delim;
    }   

    if (length $r[0]) {
        printf qq|%s|, $r[0];
    }   
    else {
        if (length $r[1]) {
            printf qq|%sORIG %s%s|, $open_delim, $r[1], $close_delim;
        }
        last;
    }   

    $text = $r[1];
}
Run Code Online (Sandbox Code Playgroud)

该程序执行无限循环,直到文本中没有更多分隔符.直到那一刻,在每次迭代中,它检查前缀(文本直到开始分隔符$r[2]),并用分隔符围绕它,对于已经用它们包围的文本($r[0]),按原样打印.

在开始时,我会啜饮整个文件的内容,因为此函数仅适用于标量.您应该查看文档以了解函数返回的内容,并且我希望您能够获得有助于解决问题的想法,以防它比此示例复杂得多.

只是为了测试,运行它像:

perl script.pl infile
Run Code Online (Sandbox Code Playgroud)

产量:

{%ORIG Some text or random characters here. %}{% Another random string
enclosed in a pair of delimiters as next {% what can be deeply
nested {% as {%here%}%} end of delimited %} text. %}{%ORIG Another
bla-bla

random text outside of the delimiters - called
as "free text".%}
Run Code Online (Sandbox Code Playgroud)