English 中文(简体)
重新包装硬换行文本的算法?
原标题:
  • 时间:2008-12-30 14:18:32
  •  标签:

假设我为我所工作的公司编写了一个定制的电子邮件管理应用程序。它从公司的支持账户中读取电子邮件,并以清理后的纯文本格式将它们存储在数据库中。在此过程中,它还会执行其他一些不错的操作,如将其与客户账户和订单相关联。当员工回复一条消息时,我的程序将生成一封电子邮件,以格式化的方式向客户发送讨论线程的版本。如果客户回复,该应用程序将查找主题行中的唯一编号以读取传入的消息,剥离前面的讨论,并将其作为新条目添加到线程中。例如:

This is a message from Contoso customer service.

Recently, you requested customer support. Below is a summary of your 
request and our reply.

--------------------------------------------------------------------
Contoso (Fred) on Tuesday, December 30, 2008 at 9:04 a.m.
--------------------------------------------------------------------
John:

I ve modified your address. You can confirm my work by logging into
"Your Account" on our Web site. Your order should ship out today.

Thanks for shopping at Contoso.

--------------------------------------------------------------------
You on Tuesday, December 30, 2008 at 8:03 a.m.
--------------------------------------------------------------------
Oops, I entered my address incorrectly. Can you change it to

Fred Smith
123 Main St
Anytown, VA 12345

Thanks!

--
Fred Smith
Contoso Product Lover

一般而言,这一切都很顺畅,但有一个领域我一直拖延着清理,它涉及文本换行。为了生成上面那样漂亮的电子邮件格式,我需要重新换行客户最初发送的文本。

我写了一个算法来实现这个功能(尽管看着代码,我不太确定它是如何工作的--它可能需要进行一些重构)。但它无法区分硬换行、段落结尾换行和"语义"换行。例如,硬换行是指电子邮件客户端在段落内插入的换行符,以将长文本行"包裹"在79个字符处。段落结尾换行是指用户在段落的最后一句话之后添加的换行符。而"语义"换行就像br标签一样,比如弗雷德上面输入的地址。

我的算法只将两个换行符连续出现视为新段落的标志,因此客户的电子邮件格式可能如下所示:

Oops, I entered my address incorrectly. Can you change it to

Fred Smith 123 Main St Anytown, VA 12345

Thanks!

-- Fred Smith Contoso Product Lover

每当我尝试写一篇能够重新换行这段文字的版本时,我基本上都会遇到难题,因为我需要知道文本的语义,"硬换行"和“我真的是想用br标签”的“类型换行”的区别,比如客户的地址。(我使用两个连续的换行符来确定何时开始新段落,这与大多数人实际输入电子邮件的方式相符。)

有人有一个可以按预期重新换行文本的算法吗?还是在衡量任何给定解决方案的复杂性时,此实现已经“足够好了”?

谢谢。

最佳回答

你可以尝试检查是否插入了新行,以保持行长度不超过最大值(也称为硬换行):只需检查文本中最长的行。然后,对于任何给定的行,你可以将下一行的第一个单词附加到它的末尾。如果结果行超过了最大长度,则换行很可能是硬换行。

更简单的方法是,您可以将(maxlength-15)<= length <= maxlength 中的所有断点视为硬换行(其中15只是一个经过研究的猜测)。这肯定会过滤掉地址和其他有意的断点,而在此范围内漏掉的任何断点也不会对结果产生太大影响。

问题回答

我有两个建议,如下所示。

  • 注意标点符号:这将帮助你区分“硬换行”和“段落结尾”(因为,如果一行以句号结尾,那么很可能用户打算将其作为段落结尾)。

  • 注意一下该行是否比最大行长要短很多:在上面的示例中,你可能会遇到文本在79个字符处被“硬换行”,并且你有地址行只有30个字符长度;因为30远小于79,所以你知道地址行是由用户断开的,而不是由用户的文本换行算法断开的。

此外,请注意缩进:左边用空格缩进的行可能被认为是新段落,与上一行断开,就像在此论坛上一样。

根据Ole的建议,我重新设计了我的实现以查看阈值。它似乎能够很好地处理我所提出的大多数情况,而无需我疯狂地编写真正理解英语的代码。

基本上,我首先扫描输入字符串并记录变量 inputMaxLineLength 中的最长行长度。然后随着我重新换行,如果我遇到索引位于 inputMaxLineLength inputMaxLineLength 的85%之间的换行符,则我替换该换行符成一个空格,因为我认为它是一个硬回车换行符--除非它紧跟着另一个换行符,因为那么我假设它只是一个在这个范围内的单行段落。这可能发生,例如,如果有人键入一个短的项目符号列表。

当然不完美,但对于我的情况来说“足够好”,考虑到文本通常在之前的电子邮件客户端中已经被切成两半。

这里有一些代码,我的实现只有几个小时,可能在一些边缘情况下仍然是封闭的(使用C#)。它比我的先前解决方案简单得多,这很好。

源代码

这里有一些单元测试来测试这段代码(使用MSTest):

测试代码

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





相关问题
热门标签