重新包装硬换行文本的算法?

7
假设我为公司编写了一款定制的电子邮件管理应用程序。它从公司的支持账户中读取电子邮件并将其清理成纯文本版本存储在数据库中,并在此过程中与客户账户和订单相关联。当员工回复邮件时,我的程序会生成一封电子邮件,其中包含讨论线程的格式化版本,发送给客户。如果客户回复,则该应用程序会查找主题行中的唯一编号以读取传入的消息,剥离先前的讨论,并将其作为新项目添加到线程中。
通常情况下,这一切都很好,但有一个地方我一直没有处理好,那就是文本换行。为了生成像上面那样漂亮的电子邮件格式,我需要重新包装客户最初发送的文本。
我已经编写了一个算法来实现这一点(尽管看着代码,我不确定它如何工作 - 它可能需要重构)。但是它不能区分硬换行符、"段落结束"换行符和"语义"换行符。例如,硬换行符是指电子邮件客户端在段落内插入的换行符,以便在79列处换行长行文本。段落结束换行符是用户在段落的最后一句话之后添加的换行符。而语义换行符则是像Fred上面输入的地址一样的br标记。
相反,我的算法只会将两个连续的换行符视为表示新段落的信号,因此客户的电子邮件将被格式化成以下内容:
每当我尝试编写一个可以按预期重新换行的版本时,我基本上会遇到难题,因为我需要知道文本的语义,硬换行和“我真的是想像br一样”的换行符之间的区别,例如客户地址中的换行符。(我使用两个连续的换行符来确定何时开始新段落,这与大多数人实际输入电子邮件的方式相吻合。)
有没有算法可以按预期重新换行文本?或者在权衡任何给定解决方案的复杂性时,这种实现是否“足够好”?
谢谢。
3个回答

3
你可以尝试检查是否插入了换行符以使行长保持在最大值以下(也称为硬换行):只需检查文本中最长的一行。然后,对于任何给定的行,将其后面一行的第一个单词附加到它上面。如果结果行超过了最大长度,则该行断点可能是硬换行。
甚至更简单的方法是将(maxlength - 15) <= length <= maxlength 中的所有断点视为硬换行(15只是一个经验性的猜测)。这肯定会过滤掉地址等意图分段的断点,而在此范围内未能捕捉到的断点也不会对结果产生太大影响。

感谢您的这个简单想法。请查看我的下面的答案,以获取一个快速而不太精确的示例实现。 - Nicholas Piasecki

2
我有两个建议,如下所示。
  • 注意标点符号:这将帮助您区分“硬换行”和“段落结尾”换行(因为如果一行以句号结束,则更可能是用户打算将其作为段落结尾。

  • 注意一行是否比最大行长度短得多:在上面的示例中,您可能有文本在79个字符处被“硬换行”,加上地址行只有30个字符长;因为30远小于79,所以您知道地址行是由用户而不是用户的文本换行算法打破的。

此外,注意缩进:从左侧用空格缩进的行可能应该是新段落,从前面的行中断开,就像在这个论坛上一样。


2
根据Ole的建议,我重新设计了我的实现来查看一个阈值。它似乎可以很好地处理我投入的大多数场景,而不需要我疯狂编写能够理解英语的代码。
基本上,我首先扫描输入字符串并记录变量中最长的行长度。然后在重新包装时,如果我遇到一个换行符,其索引在和85%之间,则我将该换行符替换为一个空格,因为我认为它是一个硬换行符——除非它紧接着另一个换行符,因为那么我假设它只是一个单行段落,恰好在该范围内。例如,如果有人键入一个短的项目列表,就会发生这种情况。
当然不完美,但对于我的场景来说“足够好”,考虑到文本通常已经被前一个电子邮件客户端半毁不成的情况下。
这里是一些代码,我的几个小时之前的实现,可能仍然在一些边缘情况下进行了包装(使用C#)。它比我的以前的解决方案要简单得多,这很好。

源代码

以下是一些使用MSTest测试该代码的单元测试:

测试代码

如果有人有更好的实现方法(毫无疑问,肯定存在更好的实现方法),我很乐意听听您的想法!谢谢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接