从学术大括号格式中提取电子邮件地址
Extract email addresses from academic curly braces format
我有一个文件,其中每一行都包含一个代表一个或多个电子邮件地址的字符串。
多个地址可以在大括号内分组,如下所示:
{name.surname, name2.surnam2}@something.edu
这意味着地址name.surname@something.edu
和name2.surname2@something.edu
都有效(这种格式通常用于科学论文)。
此外,单行也可以多次包含大括号。示例:
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com
结果:
a.b@uni.somewhere
c.d@uni.somewhere
e.f@uni.somewhere
x.y@edu.com
z.k@edu.com
关于如何解析此格式以提取所有电子邮件地址的任何建议?我正在尝试使用正则表达式,但我目前正在苦苦挣扎。
备注
我对JavaScript比Python更熟悉,基本逻辑都是一样的(不同的是语法),所以我把我的解决方案写在这里[=154] =].欢迎翻译成 Python.
问题
这个问题比简单的单行脚本或正则表达式要复杂一些,但根据具体要求,您可能能够解决一些基本问题。
对于初学者来说,解析电子邮件并不能简单地归结为单个正则表达式。 This website 有几个匹配 "many" 电子邮件的正则表达式示例,但解释了权衡(复杂性与准确性)并继续包括理论上应该匹配的 RFC 5322 标准正则表达式 any 电子邮件,后跟一段说明您不应该使用它的原因。然而,即使 that 正则表达式也假设采用 IP 地址形式的域名只能由 0 到 4 个整数的元组组成255 -- 它不允许 IPv6
甚至像这样简单的东西:
{a, b}@domain.com
可能会出错,因为从技术上讲,根据电子邮件地址规范,电子邮件地址可以包含 ANY ASCII 字符并用引号括起来。以下是有效的(单个)电子邮件地址:
"{a, b}"@domain.com
要准确解析一封电子邮件,您需要一次读取一个字母的字符,并构建一个有限状态机来跟踪您是否在双引号内、大括号内、[= 之前24=],在@
之后,解析域名,解析IP等。这样你就可以标记地址,找到你的大括号标记,并独立解析它。
基本的东西
正则表达式不是获得 100% 准确度和支持所有电子邮件的方法,*尤其是* 如果您想支持多个电子邮件一条线。但我们将从它们开始,并尝试从那里开始构建。
您可能尝试过如下正则表达式:
/\{(([^,]+),?)+\}\@(\w+\.)+[A-Za-z]+/
- 匹配单个大括号...
- 后跟一个或多个实例:
- 一个或多个非逗号字符...
- 后跟零个或一个逗号
- 后跟一个右花括号...
- 后跟一个
@
- 后跟一个或多个实例:
- 一个或多个 "word" 个字符...
- 后跟一个
.
- 后跟一个或多个字母字符
这应该大致匹配以下形式:
{one, two}@domain1.domain2.toplevel
这里处理验证,接下来是提取的问题 所有有效的电子邮件。请注意,我们在电子邮件地址的名称部分嵌套了两组括号:(([^,]+),?)
。这给我们带来了一个问题。在这种情况下,许多正则表达式引擎不知道如何 return 匹配。考虑一下当我 运行 在 JavaScript 中使用我的 Chrome 开发者控制台时会发生什么:
var regex = /\{(([^,]+),?)+\}\@(\w+\.)+[A-Za-z]+/
var matches = "{one, two}@domain.com".match(regex)
Array(4) [ "{one, two}@domain.com", " two", " two", "domain." ]
那是不对的。它找到了 two
两次,但没有找到 one
一次!要解决此问题,我们需要消除嵌套并分两步完成。
var regexOne = /\{([^}]+)\}\@(\w+\.)+[A-Za-z]+/
"{one, two}@domain.com".match(regexOne)
Array(3) [ "{one, two}@domain.com", "one, two", "domain." ]
现在我们可以使用匹配并单独解析它了:
// Note: It's important that this be a global regex (the /g modifier) since we expect the pattern to match multiple times
var regexTwo = /([^,]+,?)/g
var nameMatches = matches[1].match(regexTwo)
Array(2) [ "one,", " two" ]
现在我们可以 trim 这些并得到我们的名字:
nameMatches.map(name => name.replace(/, /g, "")
nameMatches
Array(2) [ "one", "two" ]
为了构建电子邮件的 "domain" 部分,我们需要对 @
之后的所有内容使用类似的逻辑,因为这可能会像名称部分一样重复重复的可能性。我们的最终代码(在 JavaScript 中)可能看起来像这样(您必须自己转换为 Python):
function getEmails(input)
{
var emailRegex = /([^@]+)\@(.+)/;
var emailParts = input.match(emailRegex);
var name = emailParts[1];
var domain = emailParts[2];
var nameList;
if (/\{.+\}/.test(name))
{
// The name takes the form "{...}"
var nameRegex = /([^,]+,?)/g;
var nameParts = name.match(nameRegex);
nameList = nameParts.map(name => name.replace(/\{|\}|,| /g, ""));
}
else
{
// The name is not surrounded by curly braces
nameList = [name];
}
return nameList.map(name => `${name}@${domain}`);
}
多条电子邮件线路
这是事情开始变得棘手的地方,如果我们不想构建完整的词法分析器/分词器,我们需要接受稍低的准确性。因为我们的电子邮件包含逗号(在姓名字段内),所以我们无法准确地按逗号分隔——除非这些逗号不在大括号内。以我对正则表达式的了解,我不知道这是否可以轻松完成。前瞻或后视运算符可能是可行的,但其他人必须填写我的内容。
然而,使用正则表达式可以轻松完成的任务是查找包含 post-& 逗号的文本块。类似于:@[^@{]+?,
在字符串 a@b.com, c@d.com
中,这将匹配整个短语 @b.com,
- 但重要的是它为我们提供了一个拆分字符串的位置。棘手的一点是找出如何在此处拆分您的字符串。大部分时间都可以使用类似的方法:
var emails = "a@b.com, c@d.com"
var matches = emails.match(/@[^@{]+?,/g)
var split = emails.split(matches[0])
console.log(split) // Array(2) [ "a", " c@d.com" ]
split[0] = split[0] + matches[0] // Add back in what we split on
如果您在列表中有两封具有相同域的电子邮件,这可能存在错误:
var emails = "a@b.com, c@b.com, d@e.com"
var matches = emails.match(@[^@{]+?,/g)
var split = emails.split(matches[0])
console.log(split) // Array(3) [ "a", " c", " d@e.com" ]
split[0] = split[0] + matches[0]
console.log(split) // Array(3) [ "a@b.com", " c", " d@e.com" ]
但同样,在不构建词法分析器/分词器的情况下,我们接受我们的解决方案仅适用于 大多数 情况,而不是所有情况.
然而,由于将一行拆分为多封电子邮件的任务比深入研究电子邮件、提取名称和解析名称更容易:我们也许可以为此编写一个非常愚蠢的词法分析器部分:
var inBrackets = false
var emails = "{a, b}@c.com, d@e.com"
var split = []
var lastSplit = 0
for (var i = 0; i < emails.length; i++)
{
if (inBrackets && emails[i] === "}")
inBrackets = false;
if (!inBrackets && emails[i] === "{")
inBrackets = true;
if (!inBrackets && emails[i] === ",")
{
split.push(emails.substring(lastSplit, i))
lastSplit = i + 1 // Skip the comma
}
}
split.push(emails.substring(lastSplit))
console.log(split)
再说一次,这不会是完美的解决方案,因为可能存在如下电子邮件地址:
","@domain.com
但是,对于 99% 的用例,这个简单的词法分析器就足够了,我们现在可以构建一个 "usually works but not perfect" 解决方案,如下所示:
function getEmails(input)
{
var emailRegex = /([^@]+)\@(.+)/;
var emailParts = input.match(emailRegex);
var name = emailParts[1];
var domain = emailParts[2];
var nameList;
if (/\{.+\}/.test(name))
{
// The name takes the form "{...}"
var nameRegex = /([^,]+,?)/g;
var nameParts = name.match(nameRegex);
nameList = nameParts.map(name => name.replace(/\{|\}|,| /g, ""));
}
else
{
// The name is not surrounded by curly braces
nameList = [name];
}
return nameList.map(name => `${name}@${domain}`);
}
function splitLine(line)
{
var inBrackets = false;
var split = [];
var lastSplit = 0;
for (var i = 0; i < line.length; i++)
{
if (inBrackets && line[i] === "}")
inBrackets = false;
if (!inBrackets && line[i] === "{")
inBrackets = true;
if (!inBrackets && line[i] === ",")
{
split.push(line.substring(lastSplit, i));
lastSplit = i + 1;
}
}
split.push(line.substring(lastSplit));
return split;
}
var line = "{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com";
var emails = splitLine(line);
var finalList = [];
for (var i = 0; i < emails.length; i++)
{
finalList = finalList.concat(getEmails(emails[i]));
}
console.log(finalList);
// Outputs: [ "a.b@uni.somewhere", "c.d@uni.somewhere", "e.f@uni.somewhere", "x.y@edu.com", "z.k@edu.com" ]
如果您想尝试实施完整的词法分析器/分词器解决方案,您可以查看我构建的简单/哑词法分析器作为起点。一般的想法是你有一个状态机(在我的例子中我只有两个状态:inBrackets
和 !inBrackets
)并且你一次读一个字母但是根据你当前的状态不同地解释它。
Pyparsing 是一个 PEG 解析器,它为您提供了一个嵌入式 DSL 来构建可以读取像这样的表达式的解析器,生成的代码比正则表达式更具可读性(和可维护性),并且足够灵活以添加事后思考(等等,电子邮件的某些部分可以用引号引起来?)。
pyparsing 使用“+”和“|”运算符从较小的位构建您的解析器。它还支持命名字段(类似于正则表达式命名组)和解析时回调。在下面查看这一切如何组合在一起:
import pyparsing as pp
LBRACE, RBRACE = map(pp.Suppress, "{}")
email_part = pp.quotedString | pp.Word(pp.printables, excludeChars=',{}@')
# define a compressed email, and assign names to the separate parts
# for easier processing - luckily the default delimitedList delimiter is ','
compressed_email = (LBRACE
+ pp.Group(pp.delimitedList(email_part))('names')
+ RBRACE
+ '@'
+ email_part('trailing'))
# add a parse-time callback to expand the compressed emails into a list
# of constructed emails - note how the names are used
def expand_compressed_email(t):
return ["{}@{}".format(name, t.trailing) for name in t.names]
compressed_email.addParseAction(expand_compressed_email)
# some lists will just contain plain old uncompressed emails too
# Combine will merge the separate tokens into a single string
plain_email = pp.Combine(email_part + '@' + email_part)
# the complete list parser looks for a comma-delimited list of compressed
# or plain emails
email_list_parser = pp.delimitedList(compressed_email | plain_email)
pyparsing 解析器附带一个 runTests
方法来针对各种测试字符串测试您的解析器:
tests = """\
# original test string
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com
# a tricky email containing a quoted string
{x.y, z.k}@edu.com, "{a, b}"@domain.com
# just a plain email
plain_old_bob@uni.elsewhere
# mixed list of plain and compressed emails
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com, plain_old_bob@uni.elsewhere
"""
email_list_parser.runTests(tests)
打印:
# original test string
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com
['a.b@uni.somewhere', 'c.d@uni.somewhere', 'e.f@uni.somewhere', 'x.y@edu.com', 'z.k@edu.com']
# a tricky email containing a quoted string
{x.y, z.k}@edu.com, "{a, b}"@domain.com
['x.y@edu.com', 'z.k@edu.com', '"{a, b}"@domain.com']
# just a plain email
plain_old_bob@uni.elsewhere
['plain_old_bob@uni.elsewhere']
# mixed list of plain and compressed emails
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com, plain_old_bob@uni.elsewhere
['a.b@uni.somewhere', 'c.d@uni.somewhere', 'e.f@uni.somewhere', 'x.y@edu.com', 'z.k@edu.com', 'plain_old_bob@uni.elsewhere']
披露:我是 pyparsing 的作者。
使用 re 的快速解决方案:
用一个文本行测试:
import re
line = '{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com, {z.z, z.a}@edu.com'
com = re.findall(r'(@[^,\n]+),?', line) #trap @xx.yyy
adrs = re.findall(r'{([^}]+)}', line) #trap all inside { }
result=[]
for i in range(len(adrs)):
s = re.sub(r',\s*', com[i] + ',', adrs[i]) + com[i]
result=result+s.split(',')
for r in result:
print(r)
列表结果输出:
a.b@uni.somewhere
c.d@uni.somewhere
e.f@uni.somewhere
x.y@edu.com
z.k@edu.com
z.z@edu.com
z.a@edu.com
使用文本文件进行测试:
import io
data = io.StringIO(u'''\
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com, {z.z, z.a}@edu.com
{a.b, c.d, e.f}@uni.anywhere
{x.y, z.k}@adi.com, {z.z, z.a}@du.com
''')
result=[]
import re
for line in data:
com = re.findall(r'(@[^,\n]+),?', line)
adrs = re.findall(r'{([^}]+)}', line)
for i in range(len(adrs)):
s = re.sub(r',\s*', com[i] + ',', adrs[i]) + com[i]
result = result + s.split(',')
for r in result:
print(r)
列表结果输出:
a.b@uni.somewhere
c.d@uni.somewhere
e.f@uni.somewhere
x.y@edu.com
z.k@edu.com
z.z@edu.com
z.a@edu.com
a.b@uni.anywhere
c.d@uni.anywhere
e.f@uni.anywhere
x.y@adi.com
z.k@adi.com
z.z@du.com
z.a@du.com
我有一个文件,其中每一行都包含一个代表一个或多个电子邮件地址的字符串。 多个地址可以在大括号内分组,如下所示:
{name.surname, name2.surnam2}@something.edu
这意味着地址name.surname@something.edu
和name2.surname2@something.edu
都有效(这种格式通常用于科学论文)。
此外,单行也可以多次包含大括号。示例:
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com
结果:
a.b@uni.somewhere
c.d@uni.somewhere
e.f@uni.somewhere
x.y@edu.com
z.k@edu.com
关于如何解析此格式以提取所有电子邮件地址的任何建议?我正在尝试使用正则表达式,但我目前正在苦苦挣扎。
备注
我对JavaScript比Python更熟悉,基本逻辑都是一样的(不同的是语法),所以我把我的解决方案写在这里[=154] =].欢迎翻译成 Python.
问题
这个问题比简单的单行脚本或正则表达式要复杂一些,但根据具体要求,您可能能够解决一些基本问题。
对于初学者来说,解析电子邮件并不能简单地归结为单个正则表达式。 This website 有几个匹配 "many" 电子邮件的正则表达式示例,但解释了权衡(复杂性与准确性)并继续包括理论上应该匹配的 RFC 5322 标准正则表达式 any 电子邮件,后跟一段说明您不应该使用它的原因。然而,即使 that 正则表达式也假设采用 IP 地址形式的域名只能由 0 到 4 个整数的元组组成255 -- 它不允许 IPv6
甚至像这样简单的东西:
{a, b}@domain.com
可能会出错,因为从技术上讲,根据电子邮件地址规范,电子邮件地址可以包含 ANY ASCII 字符并用引号括起来。以下是有效的(单个)电子邮件地址:
"{a, b}"@domain.com
要准确解析一封电子邮件,您需要一次读取一个字母的字符,并构建一个有限状态机来跟踪您是否在双引号内、大括号内、[= 之前24=],在@
之后,解析域名,解析IP等。这样你就可以标记地址,找到你的大括号标记,并独立解析它。
基本的东西
正则表达式不是获得 100% 准确度和支持所有电子邮件的方法,*尤其是* 如果您想支持多个电子邮件一条线。但我们将从它们开始,并尝试从那里开始构建。
您可能尝试过如下正则表达式:
/\{(([^,]+),?)+\}\@(\w+\.)+[A-Za-z]+/
- 匹配单个大括号...
- 后跟一个或多个实例:
- 一个或多个非逗号字符...
- 后跟零个或一个逗号
- 后跟一个右花括号...
- 后跟一个
@
- 后跟一个或多个实例:
- 一个或多个 "word" 个字符...
- 后跟一个
.
- 后跟一个或多个字母字符
这应该大致匹配以下形式:
{one, two}@domain1.domain2.toplevel
这里处理验证,接下来是提取的问题 所有有效的电子邮件。请注意,我们在电子邮件地址的名称部分嵌套了两组括号:(([^,]+),?)
。这给我们带来了一个问题。在这种情况下,许多正则表达式引擎不知道如何 return 匹配。考虑一下当我 运行 在 JavaScript 中使用我的 Chrome 开发者控制台时会发生什么:
var regex = /\{(([^,]+),?)+\}\@(\w+\.)+[A-Za-z]+/
var matches = "{one, two}@domain.com".match(regex)
Array(4) [ "{one, two}@domain.com", " two", " two", "domain." ]
那是不对的。它找到了 two
两次,但没有找到 one
一次!要解决此问题,我们需要消除嵌套并分两步完成。
var regexOne = /\{([^}]+)\}\@(\w+\.)+[A-Za-z]+/
"{one, two}@domain.com".match(regexOne)
Array(3) [ "{one, two}@domain.com", "one, two", "domain." ]
现在我们可以使用匹配并单独解析它了:
// Note: It's important that this be a global regex (the /g modifier) since we expect the pattern to match multiple times
var regexTwo = /([^,]+,?)/g
var nameMatches = matches[1].match(regexTwo)
Array(2) [ "one,", " two" ]
现在我们可以 trim 这些并得到我们的名字:
nameMatches.map(name => name.replace(/, /g, "")
nameMatches
Array(2) [ "one", "two" ]
为了构建电子邮件的 "domain" 部分,我们需要对 @
之后的所有内容使用类似的逻辑,因为这可能会像名称部分一样重复重复的可能性。我们的最终代码(在 JavaScript 中)可能看起来像这样(您必须自己转换为 Python):
function getEmails(input)
{
var emailRegex = /([^@]+)\@(.+)/;
var emailParts = input.match(emailRegex);
var name = emailParts[1];
var domain = emailParts[2];
var nameList;
if (/\{.+\}/.test(name))
{
// The name takes the form "{...}"
var nameRegex = /([^,]+,?)/g;
var nameParts = name.match(nameRegex);
nameList = nameParts.map(name => name.replace(/\{|\}|,| /g, ""));
}
else
{
// The name is not surrounded by curly braces
nameList = [name];
}
return nameList.map(name => `${name}@${domain}`);
}
多条电子邮件线路
这是事情开始变得棘手的地方,如果我们不想构建完整的词法分析器/分词器,我们需要接受稍低的准确性。因为我们的电子邮件包含逗号(在姓名字段内),所以我们无法准确地按逗号分隔——除非这些逗号不在大括号内。以我对正则表达式的了解,我不知道这是否可以轻松完成。前瞻或后视运算符可能是可行的,但其他人必须填写我的内容。
然而,使用正则表达式可以轻松完成的任务是查找包含 post-& 逗号的文本块。类似于:@[^@{]+?,
在字符串 a@b.com, c@d.com
中,这将匹配整个短语 @b.com,
- 但重要的是它为我们提供了一个拆分字符串的位置。棘手的一点是找出如何在此处拆分您的字符串。大部分时间都可以使用类似的方法:
var emails = "a@b.com, c@d.com"
var matches = emails.match(/@[^@{]+?,/g)
var split = emails.split(matches[0])
console.log(split) // Array(2) [ "a", " c@d.com" ]
split[0] = split[0] + matches[0] // Add back in what we split on
如果您在列表中有两封具有相同域的电子邮件,这可能存在错误:
var emails = "a@b.com, c@b.com, d@e.com"
var matches = emails.match(@[^@{]+?,/g)
var split = emails.split(matches[0])
console.log(split) // Array(3) [ "a", " c", " d@e.com" ]
split[0] = split[0] + matches[0]
console.log(split) // Array(3) [ "a@b.com", " c", " d@e.com" ]
但同样,在不构建词法分析器/分词器的情况下,我们接受我们的解决方案仅适用于 大多数 情况,而不是所有情况.
然而,由于将一行拆分为多封电子邮件的任务比深入研究电子邮件、提取名称和解析名称更容易:我们也许可以为此编写一个非常愚蠢的词法分析器部分:
var inBrackets = false
var emails = "{a, b}@c.com, d@e.com"
var split = []
var lastSplit = 0
for (var i = 0; i < emails.length; i++)
{
if (inBrackets && emails[i] === "}")
inBrackets = false;
if (!inBrackets && emails[i] === "{")
inBrackets = true;
if (!inBrackets && emails[i] === ",")
{
split.push(emails.substring(lastSplit, i))
lastSplit = i + 1 // Skip the comma
}
}
split.push(emails.substring(lastSplit))
console.log(split)
再说一次,这不会是完美的解决方案,因为可能存在如下电子邮件地址:
","@domain.com
但是,对于 99% 的用例,这个简单的词法分析器就足够了,我们现在可以构建一个 "usually works but not perfect" 解决方案,如下所示:
function getEmails(input)
{
var emailRegex = /([^@]+)\@(.+)/;
var emailParts = input.match(emailRegex);
var name = emailParts[1];
var domain = emailParts[2];
var nameList;
if (/\{.+\}/.test(name))
{
// The name takes the form "{...}"
var nameRegex = /([^,]+,?)/g;
var nameParts = name.match(nameRegex);
nameList = nameParts.map(name => name.replace(/\{|\}|,| /g, ""));
}
else
{
// The name is not surrounded by curly braces
nameList = [name];
}
return nameList.map(name => `${name}@${domain}`);
}
function splitLine(line)
{
var inBrackets = false;
var split = [];
var lastSplit = 0;
for (var i = 0; i < line.length; i++)
{
if (inBrackets && line[i] === "}")
inBrackets = false;
if (!inBrackets && line[i] === "{")
inBrackets = true;
if (!inBrackets && line[i] === ",")
{
split.push(line.substring(lastSplit, i));
lastSplit = i + 1;
}
}
split.push(line.substring(lastSplit));
return split;
}
var line = "{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com";
var emails = splitLine(line);
var finalList = [];
for (var i = 0; i < emails.length; i++)
{
finalList = finalList.concat(getEmails(emails[i]));
}
console.log(finalList);
// Outputs: [ "a.b@uni.somewhere", "c.d@uni.somewhere", "e.f@uni.somewhere", "x.y@edu.com", "z.k@edu.com" ]
如果您想尝试实施完整的词法分析器/分词器解决方案,您可以查看我构建的简单/哑词法分析器作为起点。一般的想法是你有一个状态机(在我的例子中我只有两个状态:inBrackets
和 !inBrackets
)并且你一次读一个字母但是根据你当前的状态不同地解释它。
Pyparsing 是一个 PEG 解析器,它为您提供了一个嵌入式 DSL 来构建可以读取像这样的表达式的解析器,生成的代码比正则表达式更具可读性(和可维护性),并且足够灵活以添加事后思考(等等,电子邮件的某些部分可以用引号引起来?)。
pyparsing 使用“+”和“|”运算符从较小的位构建您的解析器。它还支持命名字段(类似于正则表达式命名组)和解析时回调。在下面查看这一切如何组合在一起:
import pyparsing as pp
LBRACE, RBRACE = map(pp.Suppress, "{}")
email_part = pp.quotedString | pp.Word(pp.printables, excludeChars=',{}@')
# define a compressed email, and assign names to the separate parts
# for easier processing - luckily the default delimitedList delimiter is ','
compressed_email = (LBRACE
+ pp.Group(pp.delimitedList(email_part))('names')
+ RBRACE
+ '@'
+ email_part('trailing'))
# add a parse-time callback to expand the compressed emails into a list
# of constructed emails - note how the names are used
def expand_compressed_email(t):
return ["{}@{}".format(name, t.trailing) for name in t.names]
compressed_email.addParseAction(expand_compressed_email)
# some lists will just contain plain old uncompressed emails too
# Combine will merge the separate tokens into a single string
plain_email = pp.Combine(email_part + '@' + email_part)
# the complete list parser looks for a comma-delimited list of compressed
# or plain emails
email_list_parser = pp.delimitedList(compressed_email | plain_email)
pyparsing 解析器附带一个 runTests
方法来针对各种测试字符串测试您的解析器:
tests = """\
# original test string
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com
# a tricky email containing a quoted string
{x.y, z.k}@edu.com, "{a, b}"@domain.com
# just a plain email
plain_old_bob@uni.elsewhere
# mixed list of plain and compressed emails
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com, plain_old_bob@uni.elsewhere
"""
email_list_parser.runTests(tests)
打印:
# original test string
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com
['a.b@uni.somewhere', 'c.d@uni.somewhere', 'e.f@uni.somewhere', 'x.y@edu.com', 'z.k@edu.com']
# a tricky email containing a quoted string
{x.y, z.k}@edu.com, "{a, b}"@domain.com
['x.y@edu.com', 'z.k@edu.com', '"{a, b}"@domain.com']
# just a plain email
plain_old_bob@uni.elsewhere
['plain_old_bob@uni.elsewhere']
# mixed list of plain and compressed emails
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com, plain_old_bob@uni.elsewhere
['a.b@uni.somewhere', 'c.d@uni.somewhere', 'e.f@uni.somewhere', 'x.y@edu.com', 'z.k@edu.com', 'plain_old_bob@uni.elsewhere']
披露:我是 pyparsing 的作者。
使用 re 的快速解决方案:
用一个文本行测试:
import re
line = '{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com, {z.z, z.a}@edu.com'
com = re.findall(r'(@[^,\n]+),?', line) #trap @xx.yyy
adrs = re.findall(r'{([^}]+)}', line) #trap all inside { }
result=[]
for i in range(len(adrs)):
s = re.sub(r',\s*', com[i] + ',', adrs[i]) + com[i]
result=result+s.split(',')
for r in result:
print(r)
列表结果输出:
a.b@uni.somewhere
c.d@uni.somewhere
e.f@uni.somewhere
x.y@edu.com
z.k@edu.com
z.z@edu.com
z.a@edu.com
使用文本文件进行测试:
import io
data = io.StringIO(u'''\
{a.b, c.d, e.f}@uni.somewhere, {x.y, z.k}@edu.com, {z.z, z.a}@edu.com
{a.b, c.d, e.f}@uni.anywhere
{x.y, z.k}@adi.com, {z.z, z.a}@du.com
''')
result=[]
import re
for line in data:
com = re.findall(r'(@[^,\n]+),?', line)
adrs = re.findall(r'{([^}]+)}', line)
for i in range(len(adrs)):
s = re.sub(r',\s*', com[i] + ',', adrs[i]) + com[i]
result = result + s.split(',')
for r in result:
print(r)
列表结果输出:
a.b@uni.somewhere
c.d@uni.somewhere
e.f@uni.somewhere
x.y@edu.com
z.k@edu.com
z.z@edu.com
z.a@edu.com
a.b@uni.anywhere
c.d@uni.anywhere
e.f@uni.anywhere
x.y@adi.com
z.k@adi.com
z.z@du.com
z.a@du.com