【问题标题】:Complex text substitution algorithm or design pattern复杂的文本替换算法或设计模式
【发布时间】:2021-10-29 10:43:12
【问题描述】:

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

我的示例是最有可能在 CRM 上找到的数据,输出是用于 Web 的 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 转换为链接,以便观看信息的用户可以点击。有一个用于解码 ID 的通用 URL。假设它是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,因为它会被解释。我们想将&amp;lt;&amp;gt; 字符更改为转义形式&amp;lt;&amp;gt;。 HTML-5 的翻译表还包含要转换为 &amp;amp;&amp; 符号,例如,这也会影响电子邮件的 Message 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。其中一个标签是&lt;a&gt; 标签。我们仍然希望看到完整的标签“显示”并且 HREF 是可点击的。如果是链接,还有锚的文本。

组合示例:#2(注入链接)然后 #3(扁平化 HTML)

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

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

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

将规则#2(注入链接)应用于原始链接http://example.com/data.xml被检测并替换为&lt;a href="http://example.com/data.xml"&gt;http://example.com/data.xml&lt;/a&gt;

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(注入链接)

如果我首先将规则 #3 应用于“解码所有 HTML”,然后将规则 #2 应用于“注入链接 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(注入链接)它似乎工作:

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 限制。

但是...如果原始文本在链接文本中也有一个链接怎么办?这是一个非常常见的情况。喜欢这个原文:

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.

这里有问题

由于所有 &amp;;/ 都是有效的 URL 字符,因此 URL 解析器会发现:http://example.com/data.xml&amp;lt;/a&amp;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&amp;lt;/a&amp;gt;&lt;a href="http://example.com/data.xml&amp;lt;/a&amp;gt;"&gt;http://example.com/data.xml&amp;lt;/a&amp;gt;&lt;/a&gt; 替换,但问题是没有正确检测到 URL。

让我们把它和规则 #1 混合起来

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

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

你能想象吗??

分词器?

我曾想过创建一个语法标记器。但我觉得这有点矫枉过正。

是否有设计模式

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

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

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

【问题讨论】:

    标签: parsing token tokenize abstract-syntax-tree string-substitution


    【解决方案1】:

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

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

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

    【讨论】:

    • XRef:谢谢@rici,尝试一下。为了开始玩,我尝试做一个扫描器来扫描(xxxx),然后使用外部标记器库“任何其他”,尽管我可能会做我自己的标记器。此处为其他读者交叉引用问题stackoverflow.com/questions/69059734/…
    • XRef:添加对@Lone Learner 的一个旧问题的引用,该问题也在寻求如何匹配“其他所有内容”stackoverflow.com/questions/27217075/…
    猜你喜欢
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-28
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    相关资源
    最近更新 更多