如果我的 HTML 如下所示:( \t 表示制表符)

<P>\tSome text</P>
<P>\t\tSome text</P>
<P>\tSome text</P>

使用正则表达式,如何将上述内容转换为:
<P><BLOCKQUOTE>Some text</BLOCKQUOTE></P>
<P><BLOCKQUOTE><BLOCKQUOTE>Some text</BLOCKQUOTE></BLOCKQUOTE></P>
<p><BLOCKQUOTE>Some text></BLOCKQUOTE></P>

目前我有:
for $line (@lines)
{
   $line =~ s{^(<P>(?:<BLOCKQUOTE>)*)\t(.+?)((?:</BLOCKQUOTE>)*</P>)$}{$1<BLOCKQUOTE>$2</BLOCKQUOTE>$3}g;
}

最佳答案

这里的棘手之处是以某种方式输入与选项卡一样多的替换标签。

我会进行多次传递,首先计算选项卡,然后再次检查字符串以用正确数量的开闭替换标签( 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;
    }
}

打印

<p>             two tabs</p>
<p>     one tab</p>
<p>no tab</p>
<div>   not paragraph</div>

<p><BLOCKQUOTE><BLOCKQUOTE>two tabs</BLOCKQUOTE></BLOCKQUOTE></p>
<p><BLOCKQUOTE>one tab</BLOCKQUOTE></p>
<p>no tab</p>
<div>   not paragraph</div>

笔记
  • 就目前而言,这适用于字符串中最多一个段落( <p>...</p> ),而
    while (my ($tabs) = /<p>(\t+)/g) { ... }
    

    (而不是 if (...) )似乎适用于多个段落。需要更多测试
  • 计数本身使用 =()= "operator" 。它将列表上下文强加在其右侧,因此正则表达式返回匹配列表,分配给其左侧的标量。这样我们就得到了计数。

    在这种情况下,$tabs 只包含制表符,你可以简单地做
     my $nt = split '', $tabs;
    

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

    我仍然使用正则表达式,因为它可以用于字符串,而不仅仅是选项卡,还有
  • 代码仅替换开头的连续制表符,就在 <p> 之后,而不是字符串中后面可能出现的任何制表符(我如何看待要求)。

    这是通过在模式 ( \t+ ) 中的制表符之后使用单个非制表符和任何字符 [^\t].*? 来提供的。因此,这匹配具有更多制表符的字符串,但仅替换制表符的前导“块”
  • 关于regex - 如何使用 perl 正则表达式将制表符转换为块引号,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58334004/

    10-13 03:09