Как превратить вкладки в цитаты, используя регулярные выражения Perl - PullRequest
3 голосов
/ 11 октября 2019

Если у меня есть 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;
}

Ответы [ 5 ]

5 голосов
/ 11 октября 2019

Хитрость в том, чтобы как-то ввести столько тегов замены, сколько есть вкладок.

Я бы сделал несколько проходов, сначала посчитав вкладки, а затем снова пройдя по строке, чтобы заменить их нужным количеством открытых-закрытых замещающих тегов (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 (...)), кажется, работаетс несколькими параграфами. Требуется дополнительное тестирование

  • Для подсчета используется =()= «оператор» . Он накладывает контекст списка на его правую сторону, поэтому регулярное выражение возвращает список совпадений, назначенных скаляру слева. Таким образом, мы получаем количество.

    В этом случае, когда $tabs состоит только из символов табуляции, можно просто сделать

     my $nt = split '', $tabs;
    

    (Обновление: на самом деле просто my $nt = length $tabs;, так какв других ответах)

    Я все еще использую регулярное выражение, так как оно будет работать для строки с вещами, отличными от просто вкладок, а также

  • Код заменяет только последовательныйвкладки в начале, сразу после <p>, а не те, которые могут появиться позже в строке (как я вижу требование).

    Это обеспечивается путем следования вкладкам в шаблоне (\t+одиночным символом без табуляции и затем любыми символами [^\t].*?. Таким образом, это соответствует строке с большим количеством вкладок ниже, но заменяет только начальный «блок» вкладок

2 голосов
/ 11 октября 2019

Использование Mojo :: DOM , поэтому мы не ограничены организацией пробельных или граничных случаев:

use strict;
use warnings;
use Mojo::DOM;

my $html = <<"HTML";
<P>\tSome text</P>
<P>\t\tSome text</P>
<P>\tSome text</P>
HTML

my $dom = Mojo::DOM->new($html);
foreach my $p ($dom->find('p')->each) {
  my $content = $p->content;
  $content =~ s/^(\t*)//;
  my $count = length $1;
  $p->content($content);
  $p->wrap_content('<blockquote></blockquote>') for 1..$count;
}

print $dom->to_string;

Результат:

<p><blockquote>Some text</blockquote></p>
<p><blockquote><blockquote>Some text</blockquote></blockquote></p>
<p><blockquote>Some text</blockquote></p>

(Вы можете использовать режим XML , если вы действительно хотите, чтобы он чувствителен к регистру.)

2 голосов
/ 11 октября 2019

Может быть, с некоторым выражением, похожим на:

(?i)<p>\s{2,}(.*?)<\/p>

и заменить на

<p><BLOCKQUOTE>$1</BLOCKQUOTE></p>

Если вы хотите упростить / изменить / изучить выражение, это было объясненона верхней правой панели regex101.com . Если хотите, вы также можете посмотреть в эту ссылку , как она будет сопоставляться с некоторыми примерами ввода.


1 голос
/ 11 октября 2019
for $line (@lines)
{
   s{^(<P>(?:<BLOCKQUOTE>)*)(\t+)(.+?)((?:</BLOCKQUOTE>)*</P>)$}{$1 . "<BLOCKQUOTE>" x length($2) . $3 . "</BLOCKQUOTE>" x length($2) . $4}gie;
}
1 голос
/ 11 октября 2019

Я собирался сказать, что регулярное выражение недостаточно мощно, чтобы делать это само по себе, но похоже, что Perl может сделать больше, чем просто! Эта логика должна быть достаточно безболезненной для расширения для нескольких строк ввода.

#your input text
$string = "<P>      Some text</P>";

#get the number of tab characters present in the string - note that this assumes they're all at the start
my @matches = $string =~ /\t/g;

#get the other parts of the string
$string =~ /(<P>)(\t+)(.*)(<\/P>)/g;

#add <BLOCKQUOTE> once for each match at the locations you've specified, and also cut out the existing tabs
$out = $1 . "<BLOCKQUOTE>" x @matches . $3 . "<BLOCKQUOTE>" x @matches . $4;
print "$out"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...