华为手机通讯录

在恢复出厂设置时,把老爸的华为麦芒通讯录搞丢了,但老人家保留了一个比较早的一个通讯录电子表格,自己录入的格式没有固定。我就想有没有比较方便的方法,用代码写一个通讯录,再导入到手机里面。

1.华为麦芒vcard格式

如上图vcard代码格式,一个联系人有一个片段, 中文人名是通过Quoted-printable方式编码

Quoted-printableQP encoding,没有规范的中文译名,可译为可打印字符引用编码使用可打印字符的编码。Quoted-printable是使用可打印的ASCII字符(如字母、数字与“=”)表示各种编码格式下的字符,以便能在7-bit数据通路上传输8-bit数据, 或者更一般地说在非8-bit clean媒体上正确处理数据[注 1]。这被定义为MIME content transfer encoding,用于e-mail

看不懂!其实更简单的是这样:

URL编码后把%换成=, 或者用utf-8编码把\x换成=如下:

1
2
3
4
5
>>> import urllib.parse as up
>>> up.quote('黄学')
'%E9%BB%84%E5%AD%A6'
>>> '黄学'.encode('utf-8')
b'\xe9\xbb\x84\xe5\xad\xa6'

看起来用url编码更好一点。如果几个号码,就会用多行TEL;CELL:连接,电话号码的格式为:XXX XXXX XXXX

2.Python 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
'''本程序是通过输入联系人和电话号码然后转为为华为手机vcard的vcf文件,vcf文件可以直接上传到华为手机通讯录。'''
import urllib.parse as up
import pickle
import os

'''定义函数返回一个联系人的字典'''
def contacts():
print('|--- 欢迎进入通讯录 ---|')
print('|--- 1:查找联系人 ---|')
print('|--- 2:增加或修改联系人 ---|')
print('|--- 3:删除联系人 ---|')
print('|--- 4:显示所有通讯录 ---|')
print('|--- 5:保存并退出通讯录 ---|')

#从内存中的二进制文件中读取联系人信息
try:
with open('contact.txt', 'rb') as f2:
contact = pickle.load(f2)
except:#如果内存中没有文件者则赋值为一个空的字典
contact = {}

while 1:
no = input('请选择:')#input输入的都是字符串
#查找联系人
if no == '1':
name = input('输入联系人姓名:')
if name in contact:#联系人存在则打印他的号码
NO = contact[name]
print('号码是: %s' % NO)
else:
print('联系人不存在!')
#添加联系人
if no == '2':
name = input('输入联系人姓名:')
if name in contact:#如果联系人已经存在,打印联系电话,并询问是否要修改
print(' 联系人%s 的电话是: %s ' % (name, contact[name]))
change = input('修改原号码回复y, 增加号码回复z, 什么都不做回复n:')
if change == 'y':
NO = input('输入新号码\(多个号码请用+连接\):')
contact[name] = NO
elif change == 'z':
NO = input('输入号码\(多个号码请用+连接\):')
contact[name] += NO
else:
NO = contact[name]
print('号码是: %s' % NO)
else:
NO = input('输入联系人号码:')
contact[name] = NO
#删除联系人
if no == '3':
name = input('输入联系人姓名:')
if name in contact:
del(contact[name])
else:
print('联系人不存在.')
#显示所有通讯录
if no == '4':
if contact == {}:
print('通讯录是空的.')
else:
for i in contact:
print(i, ':', contact[i])
#保存并退出
if no == '5':
print('保存并退出.')
break

#写入一个二进制文件保存数据
with open('contact.txt', 'wb') as f1:
pickle.dump(contact, f1)
return contact #返回字典数据


'''清除之前的通讯录'''
try:
os.remove('newdata.vcf')
except:
pass

#华为通讯录vcard代码字符串
info1 = 'BEGIN:VCARD\nVERSION:2.1\nN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;'
info2 = ';;;\nFN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:'
info3 = '\nTEL;CELL:'
info4 = '\nEND:VCARD\n'

#通过输入通讯录后导出联系人的字典数据
allno = contacts()
#把字典数据转化为vcard代码并写入newdata.vcf文件
for i in allno:
name = i
#通过列表解析式删除数据里面的空格
nostr = ''.join([x for x in allno[i] if x != ' '])
nolist = nostr.split('+') #如果有多个电话号码需要每个电话分离出来组成一个列表
list2 = []
for no in nolist:
#注意vcard电话号码的格式数字之间有空格
no = no[:3] + ' ' + no[3:7] + ' ' + no[7:]
list2 += [info3, no] #每个电话号码会形成单独一行
name = up.quote(name).replace('%', '=') #vcard联系人的名字是经过url编码后把%换成=
with open('newdata.vcf', 'a') as file: #把通讯录代码写入vcard
list1 = [info1, name, info2, name]
list3 = [info4]
data = ''.join(list1 + list2 + list3)
file.write(data)

3.测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> 
===================== RESTART: E:\python files\IDEL FILES\My py file\通讯录.py =====================
|--- 欢迎进入通讯录 ---|
|--- 1:查找联系人 ---|
|--- 2:增加或修改联系人 ---|
|--- 3:删除联系人 ---|
|--- 4:显示所有通讯录 ---|
|--- 5:保存并退出通讯录 ---|
请选择:4
通讯录是空的.
请选择:2
输入联系人姓名:sonic
输入联系人号码: 123 4567 8901
请选择:2
输入联系人姓名:joan
输入联系人号码: 23456789012 +34567890123 + 98765 432312
请选择:2
输入联系人姓名:黄晓明
输入联系人号码:34567890133
请选择:4
sonic : 123 4567 8901
joan : 23456789012 +34567890123 + 98765 432312
黄晓明 : 34567890133
请选择:5
保存并退出.
>>>

生成了两个文件,一个是保存了数据,一个是需要vcard文件可以直接上传到手机通讯录

生成的vcard文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;sonic;;;
FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:sonic
TEL;CELL:123 4567 8901
END:VCARD
BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;joan;;;
FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:joan
TEL;CELL:234 5678 9012
TEL;CELL:345 6789 0123
TEL;CELL:987 6543 2312
END:VCARD
BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;=E9=BB=84=E6=99=93=E6=98=8E;;;
FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E9=BB=84=E6=99=93=E6=98=8E
TEL;CELL:345 6789 0133
END:VCARD

可以看到我在输入电话号码时的空格自动删除了,中文名被编码显示,英文字母没有变。代码写好了,以后再遇到相同的情况就不需要一个名字一个数字的敲了。如果有提供有格式的联系人名单,还可以直接读取名单信息自动生成vcard文件。同时,不同的手机生成的vcard文件格式略有差别,特别是在中文人名的处理方式上不同,不过都可以通过字符串的一些操作来实现。

4.通讯录vcard逆向操作

如果我们得到一个vcard文件,需要马上知道里面是哪些人的电话号码就需要进行逆向操作了,这段代码不是我写的,来至记录一些最近用过的编码转换, 使用了正则表达式,还没有学到这个地方,先放在这里,后面再来研究。

1
2
3
4
5
6
7
8
9
10
11
12
import re
import urllib.request
with open('newdata.vcf') as file:
vcf = file.read()
vcf = vcf.replace('=\n=','=')
names = re.findall('(=[\w=]+)',vcf)
for name in names:
if name[3:4]=='=':
name_new = name.replace('=','%')
name_new = urllib.request.unquote(name_new)
vcf = vcf.replace(name,name_new,1)
print(vcf)

运行后就会把上一个代码保存的文件转译过来,英文字符还是不变,只看中文名部分。

1
2
3
4
5
6
BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;黄晓明;;;
FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:黄晓明
TEL;CELL:345 6789 0133
END:VCARD

参考:

廖雪峰-字符串和编码

python之urlencode(),quote()及unquote()

记录一些最近用过的编码转换

总结:

  1. 本文用了urllib.parse, quote, pickle的用法
  2. 用了try..except..语句,join, split, 切片等对字符串进行操作