【发布时间】:2011-06-17 10:35:09
【问题描述】:
问题
我正在尝试寻找一种灵活的方式来解析电子邮件内容。下面是我正在使用的虚拟电子邮件文本的示例。如果可能的话,我还想避免使用正则表达式。然而,在我解决问题的过程中,我开始认为这是不可避免的。请注意,这只是完整电子邮件的一小部分虚拟子集。我需要将每个字段(例如票号、手机)解析为各自的数据类型。最后,不保证某些字段会出现在电子邮件中(您将在下面显示的我当前的解决方案中看到为什么这是一个问题)。
Header Code:EMERGENCY
Ticket No: 123456789 Seq. No: 2
Update of:
Original Call Date: 01/02/2011 Time: 11:17:03 AM OP: 1102
Second Call Date: 01/02/2011 Time: 12:11:00 AM OP:
Company: COMPANY NAME
Contact: CONTACT NAME Contact Phone: (111)111-1111
Secondary Contact: SECONDARY CONTACT
Alternate Contact: Altern. Phone:
Best Time to Call: AFTER 4:30P Fax No: (111)111-1111
Cell Phone: Pager No:
Caller Address: 330 FOO
FOO AVENUE 123
当前解决方案
对于这个简单的示例,我可以使用以下函数成功解析大多数字段。
private T BetweenOperation<T>(string emailBody, string start, string end)
{
var culture = StringComparison.InvariantCulture;
int startIndex =
emailBody.IndexOf(start, culture) + start.Length;
int endIndex =
emailBody.IndexOf(end, culture);
int length = endIndex - startIndex;
if (length < 0) return default(T);
return (T)Convert.ChangeType(
emailBody.Substring(startIndex, length).Trim(),
typeof(T));
}
基本上,我的想法是我可以解析两个字段之间的内容。例如,我可以通过做标题代码
// returns "EMERGENCY"
BetweenOperation<string>("email content", "Header Code:", "Ticket No:")
然而,这种方法有很多缺陷。一大缺陷是end 字段并不总是存在。如您所见,有一些具有相同关键字的相似键无法正确解析,例如“Contact”和“Secondary Contact”。这会导致解析器获取太多信息。此外,如果我的 end 字段不存在,我会得到一些不可预测的结果。最后,我可以解析整行,然后使用它传递给BetweenOperation<T>。
private string LineOperation(string startWithCriteria)
{
string[] emailLines = EmailBody.Split(new[] { '\n' });
return
emailLines.Where(emailLine => emailLine.StartsWith(startWithCriteria))
.FirstOrDefault();
}
在字段名称不唯一(例如时间)的某些情况下,我们会使用LineOperation,并将结果提供给BetweenOperation<T>。
问题
如何根据键解析上面显示的内容。例如,键是“标题代码”和“手机”。请注意,我不认为基于制表符空格进行解析,因为某些字段可能有几行长(例如呼叫者地址)或根本不包含任何值(例如备用电话)。
谢谢。
【问题讨论】:
-
这可能超出了您的需要,但诸如 ANTLR 之类的语言解析器是一种选择。
-
您是否事先知道字段名称,即有一组固定的字段名称,或者它们可以变化吗?
-
@Eric - 我只知道我可以遇到哪些可能的字段名称。但我无法提前知道(解析不足)哪些电子邮件将包含哪些字段。存在的字段非常一致,只有少数情况下可能会丢失 1-2 个字段。
-
对,但是你知道所有可能字段的集合,这样解析就简单多了。
标签: c# text-parsing