如何使用Perl正则表达式将制表符转换为blockquotes

CJ7*_*CJ7 3 regex perl

如果我有类似以下内容的HTML行:(\t表示制表符)

<P>\tSome text</P>
<P>\t\tSome text</P>
<P>\tSome text</P>
Run Code Online (Sandbox Code Playgroud)

使用正则表达式,如何将以上内容转换为:

<P><BLOCKQUOTE>Some text</BLOCKQUOTE></P>
<P><BLOCKQUOTE><BLOCKQUOTE>Some text</BLOCKQUOTE></BLOCKQUOTE></P>
<p><BLOCKQUOTE>Some text></BLOCKQUOTE></P>
Run Code Online (Sandbox Code Playgroud)

目前,我有:

for $line (@lines)
{
   $line =~ s{^(<P>(?:<BLOCKQUOTE>)*)\t(.+?)((?:</BLOCKQUOTE>)*</P>)$}{$1<BLOCKQUOTE>$2</BLOCKQUOTE>$3}g;
}
Run Code Online (Sandbox Code Playgroud)

zdi*_*dim 5

此处的棘手问题是要以某种方式输入尽可能多的替换标签。

我将进行多次传递,首先计数标签,然后再次遍历字符串以将其替换为正确数量的开-关替换标签(BLOCKQUOTE)。在这种情况下,单个正则表达式将变得更加复杂,因此很难进行调整和维护。

use warnings;
use strict;
use feature 'say';

my @test_strings = ( 
    qq(<p>\t\ttwo tabs</p>),
    qq(<p>\tone tab</p>),
    qq(<p>no tab</p>),
    qq(<div>\tnot paragraph</div>),
);

say for @test_strings;  say '';

for (@test_strings) 
{
    if (my ($tabs) = /<p>(\t+)/)          # capture consecutive tabs
    { 
        my $nt = () = $tabs =~ /\t/g;     # count them

        my $ot = "<BLOCKQUOTE>"  x $nt;   # open-tag
        my $ct = "</BLOCKQUOTE>" x $nt;   # close-tag

        s{<p> \t+ ([^\t].+?) </p>}{<p>$ot$1$ct</p>}x; 

        say;
    }       
}
Run Code Online (Sandbox Code Playgroud)

版画

use warnings;
use strict;
use feature 'say';

my @test_strings = ( 
    qq(<p>\t\ttwo tabs</p>),
    qq(<p>\tone tab</p>),
    qq(<p>no tab</p>),
    qq(<div>\tnot paragraph</div>),
);

say for @test_strings;  say '';

for (@test_strings) 
{
    if (my ($tabs) = /<p>(\t+)/)          # capture consecutive tabs
    { 
        my $nt = () = $tabs =~ /\t/g;     # count them

        my $ot = "<BLOCKQUOTE>"  x $nt;   # open-tag
        my $ct = "</BLOCKQUOTE>" x $nt;   # close-tag

        s{<p> \t+ ([^\t].+?) </p>}{<p>$ot$1$ct</p>}x; 

        say;
    }       
}
Run Code Online (Sandbox Code Playgroud)

笔记

  • 就目前而言,它最多可<p>...</p>在字符串中包含一个段落(),而

    while (my ($tabs) = /<p>(\t+)/g) { ... }
    
    Run Code Online (Sandbox Code Playgroud)

    (而不是if (...))似乎适用于多个段落。需要更多测试

  • 计数本身使用=()=“ operator”。它在右侧添加了列表上下文,因此正则表达式返回匹配项列表,该匹配项分配给左侧的标量。这样我们得到了计数。

    在这种情况下,$tabs仅由制表符组成,就可以

     my $nt = split '', $tabs;
    
    Run Code Online (Sandbox Code Playgroud)

    (更新:真的my $nt = length $tabs;,和其他答案一样)

    我仍然使用正则表达式,因为它不仅可以用于制表符,而且可以用于字符串,而不仅仅是制表符

  • 该代码仅替换开头,之后的连续制表符<p>,而不替换字符串后面可能出现的任何制表符(我如何看待需求)。

    这是通过在图案(以下选项卡提供了一种用于\t+)配有一个单一非标签字符,然后任何字符,[^\t].*?。因此,这与具有更多选项卡的字符串匹配,但仅替换了选项卡的前导“块”