【问题标题】:Reading vcf file data using python and re library使用python和re库读取vcf文件数据
【发布时间】:2019-02-06 10:45:05
【问题描述】:

这是 .vcf 文件数据中的内容。

BEGIN:VCARD
VERSION:4.0
N:Muller;CCCIsabella;;;
FN:Muller
ORG:Bubba Gump Shrimp Co.
TITLE:Shrimp Man
PHOTO;MEDIATYPE=image/gif:http://www.example.com/dir_photos/my_photo.gif
TEL;TYPE=work,voice;VALUE=uri:tel:+16829185770
REV:20080424T195243Z
END:VCARD

BEGIN:VCARD
VERSION:4.0
N:Mraz;CCCEdwardo;;;
FN:Mraz
ORG:Bubba Gump Shrimp Co.
TITLE:Shrimp Man
PHOTO;MEDIATYPE=image/gif:http://www.example.com/dir_photos/my_photo.gif
TEL;TYPE=work,voice;VALUE=uri:tel:+18083155095
REV:20080424T195243Z
END:VCARD

BEGIN:VCARD
VERSION:4.0
N:Reynolds;CCCBrant;;;
FN:Reynolds
ORG:Bubba Gump Shrimp Co.
TITLE:Shrimp Man
PHOTO;MEDIATYPE=image/gif:http://www.example.com/dir_photos/my_photo.gif
TEL;TYPE=work,voice;VALUE=uri:tel:+15089473508
REV:20080424T195243Z
END:VCARD

我希望我的数据如下所示。

data = [{'name': 'Muller','phone': '+16829185770'}, {'name': 'Mraz', 'phone': '+18083155095'}, {'name': 'Reynolds','phone': '+15089473508'}]

但我没有得到上述数据。在这种情况下请帮助我。这里我使用re python包来解决。

import re
file = open('contacts.vcf', 'r')
contacts = []
for line in file:
    name = re.findall('FN:(.*)', line)
    tel = re.findall('tel:(.*)', line)
    nm = ''.join(name)
    tel = ''.join(tel)
    if len(nm) == 0 and len(tel) == 0:
        continue
    data = {'name' : nm, 'phone' : tel}
    contacts.append(data)
print(contacts)

得到下面的结果名称和电话正在添加不同的。

[{'name': 'Muller', 'phone': ''}, {'name': '', 'phone': '+16829185770'}, {'name': 'Mraz', 'phone': ''}, {'name': '', 'phone': '+18083155095'}, {'name': 'Reynolds', 'phone': ''}, {'name': '', 'phone': '+15089473508'}]

【问题讨论】:

  • 对于遇到这个问题的任何人,我建议遵循 Reid Ballard 的建议并使用专用模块来解析 VCF 文件,例如 PyVCFallel’s read_vcf function
  • vobject,最适合我的需求

标签: python vcf-vcard


【解决方案1】:

你可以试试下面的代码。

import re
file = open('vcards-2.vcf', 'r')
contacts = []
phone = []
for line in file:
    name = re.findall('FN:(.*)', line)
    nm = ''.join(name)
    if len(nm) == 0:
        continue

    data = {'name' : nm.strip()}
    for lin in file:
        tel = re.findall('pref:(.*)', lin)
        tel = ''.join(tel)

        if len(tel) == 0:
            continue

        tel = tel.strip()
        tel = ''.join(e for e in tel if e.isalnum())
        data['phone'] = tel
        break
    contacts.append(data)

print(contacts)

你会得到低于redults

[{'name': 'Muller','phone': '+16829185770'}, {'name': 'Mraz', 'phone': '+18083155095'}, {'name': 'Reynolds','phone': '+15089473508'}]

【讨论】:

  • 很棒,简单明了。谢谢!
【解决方案2】:

通常在调试时,在各个点使用print 来找出代码出错的地方很有用。例如,如果您在tel = ''.join(tel) 之后插入print(">",nm,tel),您应该得到以下输出:

>  
>  
>  
> Muller 
>  
>  
>  
>  +16829185770
>  
>  
>  
>  
>  
>  
> Mraz 
>  
[... continued...]

显然,这是因为您的 for 循环在文件中的每一行而不是每张卡上运行(从技术上讲,您甚至承认这一点:for line in file:)。

您可能有兴趣使用一个模块来解析这个文件(一个快速的谷歌提出了vobject 包),这将消除对re 的需要。如果你觉得雄心勃勃,你可以手动解析它(不是很熟悉格式,所以这里有一个现成的例子)。

CARDMATCHER = re.compile(r"""
^                 ## Match at Start of Line (Multiline-flag)
    BEGIN:VCARD   ## Match the string "BEGIN:VCARD" exactly
$                 ## Match the End of Line (Multiline-flag)
.*?               ## Match characters (.) any number of times(*),
                  ## as few times as possible(?), including new-line(Dotall-flag)
^                 ## Match at Start of Line (Multiline-flag)
    END:VCARD     ## Match the string "END:VCARD" exactly
$                 ## Match the End of Line (Multiline-flag)
""", re.MULTILINE|re.DOTALL|re.VERBOSE)

VALUERE = re.compile("""
^(?P<type>[A-Z]+) ## Match Capital Ascii Characters at Start of Line
(?P<sep>:|;)     ## Match a colon or a semicolon
(?P<value>.*)    ## Match all other characters remaining
""", re.VERBOSE)

class MyVCard():
    def __init__(self,cardstring):
        self.info = defaultdict(list)
        ## Iterate over the card like you were doing
        for line in cardstring.split("\n"):
            ## Split Key of line
            match = VALUERE.match(line)
            if match:
                vtype = match.group("type")
                ## Line Values are separated by semicolons
                values = match.group("value").split(";")

                ## Lines with colons appear to be unique values
                if match.group("sep") == ":":
                    ## If only a single value, we don't need the list
                    if len(values) == 1:
                        self.info[vtype] = values[0]
                    else:
                        self.info[vtype] = values

                ## Otherwise (Semicolon sep), the value may not be unique
                else:
                    ## Semicolon seps also appear to have multiple keys
                    ## So we'll use a dict
                    out = {}
                    for val in values:
                        ## Get key,value for each value
                        k,v = val.split("=",maxsplit=1)
                        out[k] = v
                    ## Make sure we havea list to append to
                    self.info[vtype].append(out)
    def get_a_number(self):
        """ Naive approach to getting the number """
        if "TEL" in self.info:
            number = self.info["TEL"][0]["VALUE"]
            numbers = re.findall("tel:(.+)",number)
            if numbers:
                return numbers[0]
        return None

def get_vcards(file):
    """ Use regex to parse VCards into dicts. """
    with open(file,'r') as f:
        finput = f.read()
    cards = CARDMATCHER.findall(finput)
    return [MyVCard(card) for card in cards]

print([{"fn":card.info['FN'], "tel":card.get_a_number()} for card in get_vcards(file)])

再一次,我不保证此代码,因为我不会查找 vcf 格式的所有规范,并建议使用专门为此设计的模块。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-26
    • 2015-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多