MariaDB/Mediawiki 中的多行正则表达式匹配

Multiline regex match in MariaDB/Mediawiki

我正在尝试通过 MW 1.31 中的 Replace Text extension,服务器 运行 MariaDB 10.3.22 匹配多行文本(包含在 Mediawiki 模板中)。

模板示例如下(同一页面上可能存在其他模板):

{{WoodhouseENELnames
|Text=[[File:woodhouse_999.jpg|thumb|link={{filepath:woodhouse_999.jpg}}]]Αἰακός, ὁ, or say, son of Aegina.

<b class="b2">Of Aeacus</b>, adj.: Αἰάκειος.

<b class="b2">Descendant of Aeacus</b>: Αἰακίδης, -ου, ὁ.
}}

上面和下面可以是其他模板,换行符的数量不同,即

{{MyTemplatename
|Text=text, text, text
}}
{{WoodhouseENELnames
|Text=text, text, text
}}
{{OtherTemplatename
|Text= text, text, text
}}

模板中有不同行数 and/or 换行符。我想匹配完整的模板并删除它;这是从 {{WoodhouseENELnames 到结束 }} 的匹配,但不匹配任何进一步的模板,也就是说,如果遇到更多 {{,则停止匹配。

我得到的最接近的是使用类似的东西:

查找 ({{WoodhouseENELnames\n\|Text=)(.*?)\n+(.*?)\n+(.*?)\n+(.*?)(\n+}})

和 adding/removing (.*?)\n+ 在正则表达式中匹配行数较多或较少的情况。问题是此表达式可能会无意中匹配此模板之后的其他模板。

是否有一个正则表达式可以匹配同一页面中模板中包含的所有可能的 text/line 中断(以惰性方式,因为下面和上面可能还有其他模板)?模板由开始 {{ 和结束 }})?

分隔

编辑以消除任何混淆


这是用于
的递归模拟 Java,Python风格不支持函数调用(递归)的引擎

(?s)(?={{WoodhouseENELnames)(?:(?=.*?{{(?!.*?)(.*}}(?!.*).*))(?=.*?}}(?!.*?)(.*)).)+?.*?(?=)(?:(?!{{).)*(?=$)

Recursion Simulation demo

只需检查结果的匹配项


这是用于 Perl、PCRE 风格 引擎

的真正递归

(?s){{WoodhouseENELnames((?:(?>(?:(?!{{|}}).)+)|{{(?1)}})*)}}

Recursion demo


请注意,Dot-Net 的实现方式不同,此处不包含

我只能想到使用递归查询的强力迭代方法。

想法是遍历字符串,从第一次出现的字符串部分 '{{WoodhouseENELnames' 开始。从那里开始,我们可以设置一个计数器来跟踪有多少开括号和闭括号。当计数达到 0 时,我们知道模式已用完。最后一步是重建保留模式前后部分的字符串。

为此,您需要一个唯一的列来标识每一行。我假设 id.

with recursive cte as (
    select 
        n_open n0,
        n_open n1,
        1 cnt,
        mycol,
        id
    from (select t.*, locate('{{WoodhouseENELnames', mycol) n_open from mytable t) x
    where n_open > 0
    union all 
    select 
        n0,
        n1 + 2 + case when n_open > 0 and n_open < n_close then n_open else n_close end,
        cnt + case when n_open > 0 and n_open < n_close then 1 else -1 end,
        mycol,
        id
    from (
        select 
            c.*,
            locate('{{', substring(mycol, n1 + 2)) n_open,
            locate('}}', substring(mycol, n1 + 2)) n_close
        from cte c
    ) x
    where cnt > 0
)
select id, concat(substring(mycol, 1, min(n0) - 1), substring(mycol, max(n1) + 1)) mycol
from cte
group by id

Demo on DB Fiddle

设置 - 我在模式前后添加了字符串部分(包括双括号以增加乐趣):

create table mytable(id int, mycol varchar(2000));
insert into mytable values (
    1,
    '{{abcd{{WoodhouseENELnames
|Text=[[File:woodhouse_999.jpg|thumb|link={{filepath:woodhouse_999.jpg}}]]Αἰακός, ὁ, or say, son of Aegina.

<b class="b2">Of Aeacus</b>, adj.: Αἰάκειος.

<b class="b2">Descendant of Aeacus</b>: Αἰακίδης, -ου, ὁ.
}} efgh{{'
);

结果:

id | mycol        
-: | :------------
 1 | {{abcd efgh{{

MariaDB 使用 PCRE-Regex 引擎。

如果你能保证,

  1. 您的模板的开始标记 ({{WoodhouseENELnames) 另起一行
  2. 您模板的结束标记 (}}) 另起一行
  3. 中间没有其他结束标记(}})在新行开始,下面的正则表达式会做:
(?ms)^{{WoodhouseENELnames.+?^}}

描述:

  1. (?ms) 使正则表达式 ^ 匹配文本中的任何换行符并且 . 也匹配换行符。
  2. 然后在新行中搜索您的起始标签。
  3. 搜索最短的字符串,包括最多
  4. 的任何字符(包括换行符)
  5. 新行上的结束标记 (}})。

如果要捕获匹配项,请将正则表达式括在 ()

编辑:

由于 PCRE2 支持递归模式,因此将匹配以下更复杂的正则表达式,而不管上面的 beginnig-of-line-constraints:

(?msx)
({{WoodhouseENELnames       # group 1: Matching the whole template
    (                       # group 2: Mathing the contents of the Template, including subpatters.
        [^{}]*              # Search zewro or more characters except { or }
        {{                  # The beginning of a subpattern
        (                   # Containg if:
            [^{}]++         # Search zewro or more characters except { or }
        |   (?2)            # or the recursive pattern group 2
        )*                  # Zero or more times
        }}                  # The closing of the subpattern.
        [^{}]*              # Search zewro or more characters except { or }
    )
    }}
)

警告:不适合模板中的单个 {}

编辑 2

我讨厌在工作完成之前放弃 :-) 无论上面的所有约束如何,这个正则表达式都应该工作:

(?msx)                  # Note the additional 'x'-Option, allowing free spacing.
({{WoodhouseENELnames   # Searcdh group 1 - Top level template:
    (                   #  Search group 2 - top level template contents:
        (               #   Search-group 3 - Subtemplate contents:
            [^{}]*      #    Zero or more characters except { or }
        |   {(?!{)      #    or a single { not follwed by a {
        |   }(?!})      #    or a single } not follwed by a }
        )*              #   Closing search group 3
        {{              #  Opening subtemplate tag
        (               #  Search group 4:
            (?3)*       #   Reusing serach group 3, zero or more times
        |   (?2)        #   or Recurse search group 2 (of which, this is a part)
        )*              #  Group 4 zero or more times
        }}              #  Closing subtemplate tag
        (?3)*           #  Reusing search group 3, zero or more times
    )                   #  Closing Search group 2 - Template contents
    }}                  #  Top-level Template closing tag
)                       # Closing Search group 1

最后两个方案是基于PCRE2 documentation