用Python的Vcard解析器

Pau*_*ino 4 python parsing vcf-vcard

我正在解析我的vcard信息(复制到txt文件)以提取name:number并将其放入字典中.

数据样本:

BEGIN:VCARD  
VERSION:2.1  
N:MEO;Apoio;;;  
FN:Apoio MEO  
TEL;CELL;PREF:1696  
TEL;CELL:162 00  
END:VCARD  
BEGIN:VCARD  
VERSION:2.1  
N:estrangeiro;Apoio MEO;no;;  
FN:Apoio MEO no estrangeiro  
TEL;CELL;PREF:+35196169000  
END:VCARD  
import re
file = open('Contacts.txt', 'r')
contacts = dict()

    for line in file:
            name = re.findall('FN:(.*)', line)
            nm = ''.join(name)

            if len(nm) == 0:
                continue
            contacts[nm] = contacts.get(nm)
    print(contacts)
Run Code Online (Sandbox Code Playgroud)

有了这个,我得到一个带有名字的字典,但是对于数字,我得到的是无.{'name': None, 'name': None}.

我可以这样做吗?使用相同的re.findall表达式提取名称和数字?

zmo*_*zmo 16

您应该更好地使用现有的库,而不是尝试重新发明轮子:

pip install vobject
Run Code Online (Sandbox Code Playgroud)

然后在python中

>>> import vobject
>>> s = """\
... BEGIN:VCARD
... VERSION:2.1
... N:MEO;Apoio;;;
... FN:Apoio MEO
... TEL;CELL;PREF:0123456789
... TEL;CELL:0123456768
... END:VCARD
... BEGIN:VCARD
... VERSION:2.1
... N:estrangeiro;Apoio MEO;no;;
... FN:Apoio MEO no estrangeiro
... TEL;CELL;PREF:+0123456789
... END:VCARD """
>>> vcard = vobject.readOne(s)
>>> vcard.prettyPrint()
 VCARD
    VERSION: 2.1
    TEL: 1696
    TEL: 162 00
    FN: Apoio MEO
    N:  Apoio  MEO 
Run Code Online (Sandbox Code Playgroud)

你完成了!

因此,如果您想要制作一本字典,您需要做的就是:

>>> {vcard.contents['fn'][0].value: [tel.value for tel in vcard.contents['tel']] }
{'Apoio MEO': ['1696', '162 00']}
Run Code Online (Sandbox Code Playgroud)

所以你可以把所有这些都变成一个函数:

def parse_vcard(path):
    with open(path, 'r') as f:
        vcard = vobject.readOne(f.read())
        return {vcard.contents['fn'][0].value: [tel.value for tel in vcard.contents['tel']] }
Run Code Online (Sandbox Code Playgroud)

从那里,您可以改进代码以vcard在单个vobject文件中处理多个s ,并dict使用更多手机更新.

注意:我把你作为一个练习来改变上面的代码,从一个文件中只读一个vcard,到一个可以读取几个vcards的代码.提示:阅读文档vobject.

注意:我正在使用你的数据,我正在考虑你写的任何东西,它都没有意义.但有疑问,我修改了电话号码.


为了好玩,让我们来看看你的代码.首先是缩进问题,但我认为这是因为复制/粘贴不好☺.

? import re
? file = open('Contacts.txt', 'r')
? contacts = dict()

? for line in file:
?     name = re.findall('FN:(.*)', line)
?     nm = ''.join(name)

?     if len(nm) == 0:
?         continue
?     contacts[nm] = contacts.get(nm)

? print(contacts)
Run Code Online (Sandbox Code Playgroud)

首先,第②行有两个问题.您正在使用打开文件open(),但是您没有关闭该文件.如果您正在调用此函数来打开十亿个文件,那么您将因系统的可用文件描述符而饿死,因为您没有关闭文件.作为一个好习惯,你应该总是使用with构造:

with open('...', '...') as f:
    … your code here …
Run Code Online (Sandbox Code Playgroud)

为您处理fd,并更好地显示您可以在哪里使用您打开的文件.

第二个问题是你正在调用你的变量file,这是一个阴影file类型.希望这种file类型很少使用,但这是一个坏习惯,因为有一天你可能不会理解因为你用一个变量隐藏类型而发生的错误.只是不要使用它,它会让你有一天省事.

行⑤和⑥,你re.findall在每一行都应用了一个正则表达式.您应该更好地使用re.match(),因为您已经遍历每一行,并且您不会FN: something在该行内.这将使你避免不必要的''.join(name)但不是使用正则表达式来做这么简单的事情,你最好使用str.split():

if 'FN:' in line:
    name = line.split(':')[-1]
Run Code Online (Sandbox Code Playgroud)

线⑦不仅是多余的 - 如果使用if上述,但实际上是错误的.因为那时你将跳过其中没有的所有行,FN:这意味着你永远不会提取电话号码,只提取名称.

最后,Line⑧绝对没有意义.基本上,你所做的相当于:

if nm in contacts.keys():
    contacts[nm] = contacts[nm]
else:
    contacts[nm] = None
Run Code Online (Sandbox Code Playgroud)

总而言之,在您的代码中,您所做的只是提取名称,而您甚至不需要打扰电话号码.所以当你说:

有了这个,我得到一个带有名字的字典,但是对于数字,我得到的是无

这没有任何意义,因为你实际上并没有尝试提取电话号码.

我可以这样做吗?使用相同的re.findall表达式提取名称和数字?

是的,您可以使用看起来像(未经测试的正则表达式很可能不起作用)的内容,整个文件,或者至少每个vcard:

FN:(?P<name>[^\n]*).*TEL[^:]*:(?P<phone>[^\n])
Run Code Online (Sandbox Code Playgroud)

但是,当你有一个完美适合你的lib时,为什么还要烦恼呢!

  • 感谢您的时间@zmo!你的回答很棒!此时我正在尝试获取所有 vcard。感谢您对我的代码的分析。我开始研究 Python 的这种数据结构......我必须更深入地研究字典 (2认同)
  • 出色地。我对 vobject 只有一个问题:看起来它不理解版本 2.1,只理解版本 3.0。这就是为什么“TEL;WORK;VOICE:XXX”和“TEL;CELL;VOICE:YYY”都只是“tel”,而不是“tel.work”和“tel.cell”。我看到解决方法只是将字符串从“TEL;WORK”更改为“TEL;TYPE=WORK”,而不是通过“v.tel.type_param”访问类型。 (2认同)

Sch*_*nix 8

我的答案基于 zmos 答案(您需要安装 vobject)。

要从 vcf 文件中获取所有 vobjects,您可以执行以下操作:

import vobject
with open(infile) as inf:
    indata = inf.read()
    vc = vobject.readComponents(indata)
    vo = next(vc, None)
    while vo is not None:
        vo.prettyPrint()
        vo = next(vc, None)
Run Code Online (Sandbox Code Playgroud)

The documentation of vobject (on GitHub) is a little bit crappy so I looked into their code and figured out that readOne is just calling a next on readComponents. So you can use readComponents to get a collection.