代码之家  ›  专栏  ›  技术社区  ›  Paulo Vitorino

使用Python的Vcard解析器

  •  11
  • Paulo Vitorino  · 技术社区  · 8 年前

    我正在解析我的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)
    

    有了这个,我得到了一本有名字但有数字的字典。 {'name': None, 'name': None} .

    我可以用re做这个吗?提取相同的名称和编号 re.findall 表示

    2 回复  |  直到 8 年前
        1
  •  26
  •   zmo    7 年前

    你最好使用已经 existing library 而不是试图重塑车轮:

    pip install vobject
    

    然后在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 
    

    你就完了!

    所以,如果你想用它编一本词典,你需要做的就是:

    >>> {vcard.contents['fn'][0].value: [tel.value for tel in vcard.contents['tel']] }
    {'Apoio MEO': ['1696', '162 00']}
    

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

    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']] }
    

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

    N、 我留给你一个练习,把上面的代码从一个文件中只能读取一张vcard改为可以读取多张vcard的代码。提示: read the documentation of vobject .

    N、 我在使用你的数据,我认为无论你写什么,都没有意义。但毫无疑问,我修改了电话号码。


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

    ① 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)
    

    首先,第行有两个问题。您使用 open() ,但你不会关闭文件。如果你调用这个函数来打开10亿个文件,你会因为没有关闭这些文件而耗尽系统的可用文件描述符。作为一个好习惯,您应该始终使用with结构:

    with open('...', '...') as f:
        … your code here …
    

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

    第二个问题是您调用了变量 file ,这是对 文件 类型希望 文件 类型很少被使用,但这是一个坏习惯,因为有一天你可能不理解会发生的错误,因为你用变量隐藏了一个类型。别用它,总有一天会给你省事的。

    行?和,您正在应用 re.findall 每行的正则表达式。你最好使用 re.match() ,因为您已经在每一行上迭代 FN: something 在这条线内。这将使你避免不必要的 ''.join(name) 但是,与其使用正则表达式来处理这么简单的事情,不如使用 str.split() :

    if 'FN:' in line:
        name = line.split(':')[-1]
    

    如果使用 if 以上,但实际上是错误的。因为这样你就会跳过所有没有 FN: 这意味着你永远不会提取电话号码,只提取姓名。

    最后,第§行完全没有意义。基本上,你所做的相当于:

    if nm in contacts.keys():
        contacts[nm] = contacts[nm]
    else:
        contacts[nm] = None
    

    总之,在你的代码中,你所做的就是提取姓名,你甚至不需要去管电话号码。所以当你说:

    有了这个,我得到了一本有名字但有数字的字典

    这毫无意义,因为你实际上并没有试图提取电话号码。

    我可以用re做这个吗?提取相同的名称和编号 re.findall公司 表示

    是的,您可以使用类似于(未经测试的正则表达式很可能无法工作)的方法,遍历整个文件,或者至少遍历每个vcard:

    FN:(?P<name>[^\n]*).*TEL[^:]*:(?P<phone>[^\n])
    

    但是,当你有一个为你做得很完美的lib时,何必费心呢!

        2
  •  12
  •   Schoenix    7 年前

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

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

    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)
    

    文件 vobject (在GitHub上)有点糟糕,所以我查看了他们的代码,发现 readOne 正在呼叫下一个 readComponents 。所以您可以使用 读取组件 来收集。