复杂的文本替换算法或设计模式

Complex text substitution algorithm or design pattern

我需要对来自数据库的文本进行多次替换,然后再将其显示给用户。

我的示例适用于最有可能在 CRM 上找到的数据,网络的输出是 HTML,但该问题可推广到任何其他文本替换需求。这个问题对于任何编程语言都是通用的。在我的例子中,我使用 PHP 但它更像是一个算法问题而不是 PHP 问题。

问题

我在下面写的 3 个示例中的每一个都非常容易通过正则表达式来完成。但是,即使我进行多步替换,将它们组合在一个镜头中也不是那么直接。他们干扰。

问题

是否有进行多个干扰文本替换的设计模式?

替换示例 #1:ID。

我们使用 ID。 ID 是 sha-1 摘要。 ID 是通用的,可以代表公司中的任何实体,从用户到机场,从发票到汽车。

所以在数据库中我们可以找到要显示给用户的文本:

User d19210ac35dfc63bdaa2e495e17abe5fc9535f02 paid 50 EUR
in the payment 377b03b0b4e92502737eca2345e5bdadb1262230. We sent
an email a49c6737f80eadea0eb16f4c8e148f1c82e05c10 to confirm.

我们希望将所有 ID 翻译成 link,以便观看信息的用户可以点击。有一个通用的 URL 用于解码 ID。假设它是 http://example.com/id/xxx

转换后的文本将是这样的:

User <a href="http://example.com/id/d19210ac35dfc63bdaa2e495e17abe5fc9535f02">d19210ac35dfc63bdaa2e495e17abe5fc9535f02</a> paid 50 EUR
in the payment <a href="http://example.com/id/377b03b0b4e92502737eca2345e5bdadb1262230">377b03b0b4e92502737eca2345e5bdadb1262230</a>. We sent
an email <a href="http://example.com/id/a49c6737f80eadea0eb16f4c8e148f1c82e05c10">a49c6737f80eadea0eb16f4c8e148f1c82e05c10</a> to confirm

替换示例 #2:链接

我们希望任何类似于 URI 的东西都可以点击。让我们只关注 http 和 https 协议,其余的忘记。

如果我们在数据库中找到这个:

Our website is http://mary.example.com and the info
you are requesting is in this page http://mary.example.com/info.php

会被转换成这样:

Our website is <a href="http://mary.example.com">http://mary.example.com</a> and the info
you are requesting is in this page <a href="http://mary.example.com/info.php">http://mary.example.com/info.php</a>

替换示例 #3:HTML

当原始文本包含 HTML 时,不得将其发送 raw,因为它会被解释。我们想将 <> 字符更改为转义形式 &lt;&gt;。 HTML-5 的翻译 table 还包含要转换为 &amp;& 符号,例如,这也会影响电子邮件的消息 ID 的翻译。

例如,如果我们在数据库中找到这个:

We need to change the CSS for the <code> tag to a pure green.
Sent to John&Partners in Message-ID: <aaa@bbb.ccc> this morning.

最终替换为:

We need to change the CSS for the &lt;code&gt; tag to a pure green.
Sent to John&amp;Partners in Message-ID: &lt;aaa@bbb.ccc&gt; this morning.

好吧...但是...组合?

到目前为止,“本身”的每个更改都非常容易。

但是当我们组合事物时,我们希望它们对用户来说仍然是“自然的”。假设原文包含HTML。其中一个标签是 <a> 标签。我们仍然希望看到完整的标签“显示”并且 HREF 可以点击。还有锚的文本,如果它是 link。

组合样本:#2(注入 links)然后 #3(压平 HTML)

假设我们在数据库中有这个:

Paste this <a class="dark" href="http://example.com/data.xml">Download</a> into your text editor.

如果我们首先应用 #2 来转换 link,然后应用 #3 来编码 HTML,我们将有:

在原件上应用规则 #2(注入 links) link http://example.com/data.xml<a href="http://example.com/data.xml">http://example.com/data.xml</a>

检测和替换
Paste this <a class="dark" href="<a href="http://example.com/data.xml">http://example.com/data.xml</a>">Download</a> into your text editor.

这显然是一个损坏的 HTML 并且没有任何意义,但是,此外,在 #2 的输出上应用规则 #3(展平 HTML)我们将得到:

Paste this &lt;a class="dark" href="&lt;a href="http://example.com/data.xml"&gt;http://example.com/data.xml&lt;/a&gt;"&gt;Download&lt;/a&gt; into your text editor.

这反过来又是损坏的 HTML 的平面 HTML 表示,不可点击。 错误 输出:#2 和#3 都不满意。

反向组合:首先#3(压平HTML)然后#2(注入links)

如果我首先将规则 #3 应用于“解码所有 HTML”,然后我将规则 #2 应用于“注入 links HTML”,则会发生以下情况:

原文(同上):

Paste this <a class="dark" href="http://example.com/data.xml">Download</a> into your text editor.

应用 #3 的结果(展平 HTML)

Paste this &lt;a class="dark" href="http://example.com/data.xml">Download&lt;/a&gt; into your text editor.

然后我们应用规则 #2(注入 links)它似乎有效:

Paste this &lt;a class="dark" href="<a href="http://example.com/data.xml">http://example.com/data.xml</a>">Download&lt;/a&gt; into your text editor.

这是可行的,因为 " 不是有效的 URL 字符,并且将 http://example.com/data.xml 检测为确切的 URL 限制。

但是...如果原始文本在 link 文本中也有一个 link 怎么办?这是一个非常常见的案例场景。喜欢这篇原文:

Paste this <a class="dark" href="http://example.com/data.xml">http://example.com/data.xml</a> into your text editor.

然后应用#2 会得到:

Paste this &lt;a class="dark" href="http://example.com/data.xml"&lt;http://example.com/data.xml&lt;/a&gt; into your text editor.

这里有一个问题

由于所有 &;/ 都是有效的 URL 字符,URL 解析器会发现:http://example.com/data.xml&lt;/a&gt;作为 URL 而不是在 .xml 点结束。

这将导致错误输出:

Paste this &lt;a class="dark" href="<a href="http://example.com/data.xml">http://example.com/data.xml</a>"&lt;<a href="http://example.com/data.xml&lt;/a&gt;">http://example.com/data.xml&lt;/a&gt;</a> into your text editor.

所以 http://example.com/data.xml&lt;/a&gt;<a href="http://example.com/data.xml&lt;/a&gt;">http://example.com/data.xml&lt;/a&gt;</a> 取代了,但问题是 URL 没有被正确检测到。

让我们将它与规则 #1 混合起来

如果规则 #2 和 #3 在一起处理时一团糟想象一下如果我们将它们与规则 #1 混合并且我们有一个 URL 其中包含一个像这个数据库条目的 sha-1:

Paste this <a class="dark" href="http://example.com/id/89019b16ab155ba1c19e1ab9efdb9134c8f9e2b9">http://example.com/id/89019b16ab155ba1c19e1ab9efdb9134c8f9e2b9</a> into your text editor.

你能想象吗??

分词器?

我想创建一个语法分词器。但是我觉得有点矫枉过正了。

有没有设计模式

我想知道是否有设计模式可供阅读和研究,它是如何调用的,它在哪里记录,当涉及到进行多个文本替换时。

如果没有任何模式……那么……构建语法分词器是唯一的解决方案吗?

我觉得必须是一种更简单的方法来做到这一点。我真的必须在语法树中标记文本,然后通过遍历树重新呈现吗?

设计模式是您已经拒绝的那种,从左到右的标记化。当然,在有代码生成器生成词法扫描器的语言中,这更容易做到。

无需解析或构建语法树。令牌的线性序列就足够了。实际上,扫描仪变成了换能器。每个令牌要么原封不动地通过,要么立即替换为所需的翻译。

分词器也不需要特别复杂。您当前拥有的三个正则表达式可以与代表任何其他字符的第四个标记类型结合使用。重要的部分是在每个点尝试所有模式,选择一个,执行指定的替换,并在匹配后恢复扫描。