我正在尝试找到一种灵活的解析电子邮件内容的方法。下面是我正在使用的虚拟电子邮件文本的示例。如果可能的话,我也想避免使用正则表达式。然而,在解决问题的过程中,我开始认为这是不可避免的。请注意,这只是完整电子邮件的一个小虚拟子集。我需要的是将每个字段(例如,Ticket No,Cell Phone)解析为它们各自的数据类型。最后,电子邮件中不保证某些字段存在(您将在下面显示的当前解决方案中看到为什么这是一个问题)。
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
字段并不总是存在。正如您所看到的,有一些类似的键具有相同的关键字,无法解析得恰到好处,例如“联系人”和“次要联系人”。这会导致解析器获取太多信息。此外,如果我的结束字段不存在,我将得到一些不可预测的结果。最后,我可以解析整行,然后使用它将其传递给BetweenOperation<T>
。
private string LineOperation(string startWithCriteria)
{
string[] emailLines = EmailBody.Split(new[] { '\n' });
return
emailLines.Where(emailLine => emailLine.StartsWith(startWithCriteria))
.FirstOrDefault();
}
我们会在字段名称不唯一的某些情况下使用LineOperation
(例如时间),并将结果提供给BetweenOperation<T>
。
如何根据键解析上面显示的内容。例如,键是“标题代码”和“手机”。请注意,我不认为基于制表符的空格进行解析,因为某些字段可能是多行(例如,来电者地址)或根本不包含任何值(例如,Altern电话)。
谢谢。
答案 0 :(得分:3)
在我看来,我会按特定顺序对其进行解析,然后相应地修改您的电子邮件正文。
特定序列
Contact: CONTACT NAME Contact Phone: (111)111-1111
Secondary Contact: SECONDARY CONTACT
Alternate Contact:
搜索字段的顺序应该以不是“字段”中任何其他关键字的子集的字开头(对于联系人,序列应为“次要联系人:”,“替代联系人:”然后最后“联系方式:”)
修改您的电子邮件正文,如果您找到了所需的字段信息,则需要修改电子邮件正文以将其删除。通过特定序列进行解析,将确保(我希望)您不会遇到整个不匹配问题,因为您最后删除子集。
现在还存在结束关键字字段的问题。由于结束字段并不总是保证存在(并且我不确定它们是否总是按特定顺序),您将不得不遍历所有关键字字段,并返回索引并根据索引确定最接近的关键字
答案 1 :(得分:2)
我必须在当天阅读来自Pick DB的报告时做类似的事情。如果您的字段是基于位置的,则只需创建电子邮件的XML Schema:
<message>
<line0>
<element name="Header Code" start="0" end="MAX" type="string"/>
<!-- MAX Indicates whole line -->
</line0>
<line1>
<element name="Ticket No" start="0" end="20" type="string"/>
<element name="Seq. No" start="22" end="40" type="int" />
</line1>
</message>
然后,要解析电子邮件,您将阅读所有文本文本行。 对于每一行(从0开始),您将在模式中找到“line”+索引号实体。
创建临时字符串。 “line”+ index实体中的Foreach元素在整个行上执行子字符串,从元素实体中定义的起始值到结束值开始....
根据元素的类型对子字符串进行转换。将实体保存到对象或其他内容。
您甚至可以通过类别对模式中的不同行+索引实体进行分组来获得更多创意:
<message>
<header>
<line0>
...
</line0>
</header>
</message>
答案 2 :(得分:2)
解决问题的一种方法是首先在整个文本中搜索键的出现次数。也就是说,构建一个看起来像这样的数组:
"Header Code:",1
"Contact Phone:",233
"Cell Phone:",-1 // not there
如果按位置对该数组进行排序,那么您就知道在哪里寻找东西。也就是说,你会知道每个字段都跟着它们。
您必须使用重复项(即通话日期中的“时间:”和“时间:”)执行某些操作。而你必须解决“联系方式:”和“次要联系方式:”,尽管这应该很容易。
如果使用标准字符串操作(即IndexOf
)执行此操作,则效率会稍低,因为您必须在整个文本中搜索每个字符串的所有匹配项。这对你来说是否有问题很难说。取决于你需要做多少这些。
如果它成为一个问题,你可能想要建立一个Aho-Corasick字符串匹配器,或类似的东西。或者你可以建立一个大丑陋的正则表达式:
"(Header Code:)|(Contact Phone:)|(Cell Phone)"
...等等可能是命名捕获,所以你知道你在捕捉什么。尽管可能难以维护,但它应该可以很好地工作。
答案 3 :(得分:2)
我首先将行中的邮件分开,方法是使用一个StringReader,一次解析一条线,跳过完全空行。由于您正在寻找的标签是注释,循环遍历每行中的潜在标签,如果您发现了一个密码提取正确的部分(您可以使用空格)。不知道如何使用正则表达式是一种选择,但如果它们在预先使用的基础上使用它将作为一种魅力。