日期: 2021 年 5 月 22 日

Python常用数据类型的基本操作

Python常用数据类型的基本操作(长文系列*篇)

Python基础系列会将基础内容大致分为四到五个板块,每篇文章着重讲一方面,知识不会很难,主要是以小例子的形式解读,如果你已经入门Python,希望可以帮你温习一下;如果你想入门Python,希望可以帮你越过这个门槛。

Python原生数据类型主要有Number、String、Byte、Boolean、None、List、Tuple、Set、Dict这九种,这篇文章主要讲一下字符串、列表、元祖、集合、字典这五种,剩下的四种大家可以自己了解一下。

字符串
初始化一个字符串,方便后面在字符串上做一些操作。

In [5]: s1 = ‘naitangmao’
In [6]: s1

Out[6]: ‘naitangmao’

字符串的索引,需要注意的是索引下标从0开始。

In [9]: s1[2]
Out[9]: ‘i’

字符串的切片,以冒号分隔首尾索引位置,是[start:end]结构,注意不包含end对应元素;冒号左边不写入索引表示从头开始,同理右边不写入索引表示截取到字符串末尾。

In [8]: s1[:3]#从0开始,0,1,2三个索引
Out[8]: ‘nai’

还可以利用切片结合负索引实现翻转字符串。

In [35]: s1[::-1]
Out[35]: ‘oamgnatian’

利用加法实现合并字符串。

In [49]: print(‘ab’+’%’+’cd’)
ab%cd

查找,利用find方法查询元素时,会返回在字符串*次出现的下标,未找到会返回-1。

In [11]: s1.find(‘a’)
Out[11]: 1

替换,replace可以实现字符串中元素的替换,比如将’tang’替换成空格。

In [13]: s1.replace(‘tang’,’ ‘)
Out[13]: ‘nai mao’

去空格,使用strip方法可以删除一个字符串首尾的空格,然后也支持指定要删除内容。

In [23]: s2 = ‘ aaabbbccc ‘
In [24]: s2
Out[24]: ‘ aaabbbccc ‘

In [25]: s2.strip()
Out[25]: ‘aaabbbccc’

In [26]: s2 = s2.strip()
In [27]: s2.strip(‘a’)#可以指定删除首尾的元素
Out[27]: ‘bbbccc’

切割,split方法可以根据元素切割字符串,并存入列表;如果不输入参数,会直接将原字符串存入列表。

In [30]: s1 = ‘naitangmao’
In [31]: s1.split(‘t’)
Out[31]: [‘nai’, ‘angmao’]

In [32]: s1.split()
Out[32]: [‘naitangmao’]

判断一个元素是否存在于字符串中。

In [37]: ‘nai’ in s1
Out[37]: True

分隔,利用join方法可以将一个字符串作为分隔符,分隔另一个字符串。

In [38]: s1.join(‘!!!’)
Out[38]: ‘!naitangmao!naitangmao!’

利用%向字符串中传入参数,%s传入字符串、%d传入整数、%f传入浮点数,且可以控制小数点后的位数。

In [40]: print(‘naitangmao是%s!’ % ‘靓仔’)
naitangmao是靓仔!
In [41]: print(‘naitangmao %d ‘% 66)
naitangmao 66
In [44]: print(‘naitangmao %.2f’% 3.1415)
naitangmao 3.14

也可以利用format向字符串中传入参数,且不需要在意参数类型。

In [46]: ‘{0}ai{1}ang{2}ao’.format(‘n’,’66’,’3.14′)
Out[46]: ‘nai66ang3.14ao’

利用反斜杠对字符串进行转义。

In [47]: print(‘\t’)

In [48]: print(‘\\t’)
\t

列表
同样初始化一个列表,然后方便对列表做一系列操作。

In [52]: list1
Out[52]: [1, 3, 5, 7, 9, 11]
1
2
首先同样是列表的索引,列表也支持负索引。

In [53]: list1[2]
Out[53]: 5

In [55]: list1[-2]#负索引
Out[55]: 9

再拓展一下带有间隔的切片,字符串同样适用,就是在end之后再加上一个双引号,用来写入切片的间隔,这才是切片*完整的结构。

In [58]: list1[0:6:2]
Out[58]: [1, 5, 9]
1
2
利用index方法可以获取某个元素在列表中的位置索引,未找到的则会报错。

In [60]: list1.index(3)
Out[60]: 1
1
2
利用join方法将列表中的各个元素合并为字符串。

In [121]: list1 = [‘a’,’b’,’c’]
In [122]: ”.join(list1)
Out[122]: ‘abc’

count方法可以统计一个元素在列表中出现的次数。

In [63]: list1.count(5)
Out[63]: 1
1
2
enumerate可以直接获取列表的索引和对应元素。

In [133]: index_ = []
In [134]: value_ = []
In [135]: for i,v in enumerate(list1):
…: index_.append(i)
…: value_.append(v)

In [136]: index_
Out[136]: [0, 1, 2, 3, 4]
In [137]: value_
Out[137]: [3, 1, 4, 2, 5]

利用zip方法合并两个列表。

In [139]: list3 = list(zip(index_,value_))
In [140]: list3
Out[140]: [(0, 3), (1, 1), (2, 4), (3, 2), (4, 5)]]

扩大列表的四种方法:

1、append:将一个元素添至列表尾部
2、insert:将一个元素插入至指定位置
3、extend:将一个列表的所有元素都添加至另一个列表中
4、+:将两个列表合并成一个新列表
In [68]: list1.append(12)
In [69]: list1
[1, 3, 5, 7, 9, 11, 12]

In [78]: list1.insert(0,0)
In [79]: list1
Out[79]: [0, 1, 3, 5, 7, 9, 11, 12]

In [80]: list1.extend([2,4])
In [81]: list1
Out[81]: [0, 1, 3, 5, 7, 9, 11, 12, 2, 4]

In [82]: list2 = [6,8]
In [83]: list3 = list1+list2
In [84]: list3
Out[84]: [0, 1, 3, 5, 7, 9, 11, 12, 2, 4, 6, 8]

删除列表元素的三种方法:

1、pop:从列表指定位置删除元素,并将其返回。如果没有指定索引,pop()返回*后一个元素,并从列表中删去。
2、remove:从列表中删去指定元素,没有则会报错。
3、del:也是利用索引删去列表中的某部分。
In [91]: list1.pop(3)
Out[91]: 7
In [92]: list1
Out[92]: [1, 3, 5, 9, 11]

In [94]: list1.remove(5)
In [95]: list1
Out[95]: [1, 3, 9, 11]

In [96]: del list1[1:3]
In [97]: list1
Out[97]: [1, 11]

翻转列表的三种方式:

1、reverse:就地倒排列表中的元素。
2、reversed:函数对列表进行反转,并返回一个新的迭代器,需要用list转换
3、切片结合负索引
In [99]: list1 = [1,3,5,7,9,11]
In [100]: print(list1.reverse())
[11, 9, 7, 5, 3, 1]

In [102]: list2 = list(reversed(list1))
In [103]: print(list2)
[11, 9, 7, 5, 3, 1]

In [105]: list1[::-1]
Out[105]: [11, 9, 7, 5, 3, 1]

实现列表排序的两种方式:

1、sort:对列表中的元素就地进行排序。
2、sorted:函数对列表进行排序,形成一个新列表
3、利用lambda自定义函数
这两种方法默认为升序,通过参数reverse可以更改排序方式。

In [106]: list2 = [3,5,2,7,1]
In [108]: list2.sort()
In [109]: list2
Out[109]: [1, 2, 3, 5, 7]

In [116]: list3 = sorted(list2,reverse = True)
In [117]: list3
Out[117]: [7, 5, 3, 2, 1]
#按照元祖中第二个元素的大小排序
In [141]: list4 = [(0, 3), (1, 1), (2, 4), (3, 2), (4, 5)]
In [142]: print(sorted(list4,key = lambda x: x[1]))
[(1, 1), (3, 2), (0, 3), (2, 4), (4, 5)]

sort和reverse这类就地处理列表的操作,针对可变的列表是可以的,但如果是不可变的元祖,只能用sorted和reversed这两种方式。

拷贝列表的三种方式:

1、利用切片直接赋值,浅拷贝
2、copy方法,浅拷贝
3、deepcopy方法,深拷贝
In [25]: list2 = list1[:]
In [26]: list3 = list1.copy()
In [27]: import copy
In [29]: list4 = copy.deepcopy(list1)

深拷贝和浅拷贝的区别因为涉及到数据结构,口头叙述不容易理解,网上有很多图例讲解的博客,大家可以了解一下。

元组
元组和列表是非常相似的,有一种类似近亲的关系,也就是说列表中很多操作同样适用于元组,比如索引、切片等等,但也有一部分不同,这里主要来说一下元组的特别之处。

首先元组又被称作带锁的列表,就是元组内的元素是不能随意更改的,比如你不能给元组中的一个元素随意赋值。

In [2]: tuple1 = (1,2,3)
In [3]: tuple1[2] = 4
#会发生报错,告诉你不支持这样的操作
TypeError: ‘tuple’ object does not support item assignment

元组的标志并不是单纯的小括号,而是逗号,或者小括号与逗号的结合,看下面这个例子。

In [31]: tuple2 = (1)
In [32]: type(tuple2)
Out[32]: int
In [33]: tuple3 = (1,)
In [34]: type(tuple3)
Out[34]: tuple
In [35]: tuple4 = 1,2,
In [36]: type(tuple4)
Out[36]: tuple

那如何初始化一个空元组呢?

In [39]: tuple5 = ()
In [40]: type(tuple5)
Out[40]: tuple

上面刚刚说过元组是不可变对象,自然也不会有append、insert、pop这类的操作。元组中增添可以利用”+”实现,删除则可以利用del,因为这是python自带的回收机制。

In [42]: tuple5 = tuple5[:] + (1,2,3,4,)
In [43]: tuple5
Out[47]: (1, 2, 3, 4)

In [50]: del tuple5 #不支持切片
In [51]: tuple5
NameError: name ‘tuple5’ is not defined

“*”在数值型之间为乘积运算符,而在列表和元组之间可以表示为重复运算符。

In [53]: tuple5 = (1,2)
In [54]: tuple5 * 3

Out[54]: (1, 2, 1, 2, 1, 2)

集合
集合是一个无序不重复元素的集。基本功能包括关系测试和消除重复元素。集合对象还支持联合、交、差和对称差集等数学运算。

首先可以利用大括号或set()函数创建集合,如果想要创建空集合,你必须使用set()而不是{},{}用来创建字典。

In [57]: set1 = set()
In [58]: type(set1)
Out[58]: set

集合会本身会带有去重功能。

In [55]: set1 = {1,1,2,2,3,3,4}
In [56]: set1
Out[56]: {1, 2, 3, 4}

将集合转化为列时,会自动排序。

In [74]: set2 = {5,5,4,2,2,0}
In [75]: list_ = list(set2)
In [76]: list_
Out[76]: [0, 2, 4, 5]

集合之间的一些运算操作。

In [60]: set1 = {1,2,3,4}
In [61]: set2 = {3,4,5}
#差
In [62]: set1 – set2
Out[62]: {1, 2}
#并
In [63]: set1 | set2
Out[63]: {1, 2, 3, 4, 5}
#交
In [64]: set1 & set2
Out[64]: {3, 4}
#只在set1或只在set2中
In [65]: set1 ^ set2
Out[65]: {1, 2, 5}

利用add向集合中增添元素,利用remove删除元素。

In [69]: set1 = {1,2,3}
In [70]: set1.add(5)
In [71]: set1
Out[71]: {1, 2, 3, 5}

In [72]: set1.remove(2)
In [73]: set1
Out[73]: {1, 3, 5}

字典
字典是Python中一个非常有用的原生数据类型,一般序列是用连续的整数作为索引,而字典是以关键字作为索引,且关键字要是任意不可变类型。理解字典时可以把它看做无序的键:值对(key:value对)集合,同一个字典中键必须互不相同,利用{}可以初始化一个空的字典。

In [77]: dict1 = {}
In [78]: type(dict1)
Out[78]: dict

如果确保一个键不在字典中,可以利用下面的方法向字典中添加元素。

In [81]: dict1 = {‘a’:1,’b’:2}
In [82]: dict1[‘c’] = 3
In [83]: dict1
Out[83]: {‘a’: 1, ‘b’: 2, ‘c’: 3}

而如果这个键已经存在于字典中了,就表示为这个键赋值。

In [84]: dict1[‘b’]=4
In [85]: dict1
Out[85]: {‘a’: 1, ‘b’: 4, ‘c’: 3}

keys()方法能够一次性获得字典中所有的键,values()方法则用来获取值,items()则是获取键值对的元组形式。

In [86]: list(dict1.keys())
Out[86]: [‘a’, ‘b’, ‘c’]

In [87]: list(dict1.values())
Out[87]: [1, 4, 3]

In [88]: list(dict1.items())
Out[88]: [(‘a’, 1), (‘b’, 4), (‘c’, 3)]

元组形式或者列表中嵌套的元组的形式都可以转换成字典,因为要保持不可变性。

In [89]: dict2 = dict([(‘e’,5),(‘f’,6)])
In [90]: dict2
Out[90]: {‘e’: 5, ‘f’: 6}

In [91]: dict3 = dict(((‘g’,7),(‘h’,8)))
In [92]: dict3
Out[92]: {‘g’: 7, ‘h’: 8}

当然也可以通过’=’赋值的形式创建一个字典。

In [93]: dict5 = dict(i = 8,j = 9)
In [94]: dict5
Out[94]: {‘i’: 8, ‘j’: 9}

查询一个键是否存在一个列表中。

In [96]: ‘i’ in dict5
Out[96]: True

In [97]: ‘a’ in dict5
Out[97]: False

根据键查询对应值的两种方式:

1、直接利用键的名字索引,不足的是如果字典中没有这个键则会发生报错。
2、利用get方法,可以设置不存在键名的情况下的返回值,默认返回None。
In [98]: dict5[‘i’]
Out[98]: 8
In [99]: dict5[‘a’]
KeyError: ‘a’

In [101]: dict5.get(‘i’)
Out[101]: 8
In [103]: dict5.get(‘a’,”没有”)
Out[103]: ‘没有’

字典中的几种删除方式:

1、pop()方法,与列表不同的是必须要传入一个字典中已有键的参数。
2、popitem(),类似于列表中的pop(),随机删除一组键值对而非删除*后一个,因为字典本身无序。
3、del方法,用于删除整个字典
In [107]: dict3
Out[107]: {‘g’: 7, ‘h’: 8}

In [109]: dict3.pop(‘g’)
Out[109]: 7
In [110]: dict3.popitem()
Out[110]: (‘h’, 8)

7
clear()方法可以清楚字典中所有的键值对。

In [104]: dict5.clear()
In [105]: dict5
Out[105]: {}
1
2
3
setdefault()方法可以传入一组键值对,如果字典中已有同名键,则返回键在字典中对应的值,否则将传入的键值对存入字典中。

In [115]: dict2
Out[115]: {‘e’: 5, ‘f’: 6}

In [117]: dict2.setdefault(‘e’,1)
Out[117]: 5

In [118]: dict2.setdefault(‘g’,7)
Out[118]: 7
In [119]: dict2
Out[119]: {‘e’: 5, ‘f’: 6, ‘g’: 7}

update()方法可以用来更新字典:

如果字典中已有传入的键,则更新键对应的值。
如果没有,则将传入的键值对存入字典中。
In [121]: dict2.update({‘g’:10})
In [122]: dict2
Out[122]: {‘e’: 5, ‘f’: 6, ‘g’: 10}

In [123]: dict2.update(dict1)
In [124]: dict2
Out[124]: {‘e’: 5, ‘f’: 6, ‘g’: 10, ‘a’: 1, ‘b’: 4, ‘c’: 3}

总结
上面就是五种数据类型基本操作的一个概括,其中比较常用的三种就是字符串、列表和字典,应该重点掌握,然后有的伙伴一定好奇为什么没写表达式?莫急,在后面文章中一定是会有的。上面的基操应该是非常基础的,一定要熟悉,并且注意每个数据类型的特点,不要相互混淆啦。

Python的循环、判断和各种表达式

Python的循环、判断和各种表达式(长文系列第二篇)

流程控制是python语法很重要的一个分支,主要包括我们经常用到的判断语句、循环语句以及各种表达式,这也是上一篇文章没有介绍表达式的原因,在这篇文章中会更加系统全面的讲解这三方面的基础知识。

判断语句(if)
判断语句中*有名的应该就是if-else的组合,并且很多语言都通用这种格式,但是对于elif而言,不同语言表达形式可能会不同:

In [1]: x = 5
In [2]: if x>0:
…: print(‘正整数’)
…: elif x<0:
…: print(‘负整数’)
…: else:
…: print(‘零’)
…:

一组判断语句可能会有零到多个 elif 部分,else 是可选的。关键字 elif 是 else if 的缩写,由于python对缩进的要求很严格,而这种写法恰巧可以有效地避免过深的缩进。 if … elif … elif … 序列用于替代其它语言中的 switch 或 case 语句。

循环语句
1、for循环
如果C语言是你*熟悉的需要,要注意python和C语言中的for语句表达形式完全不同。Python 的 for 语句依据任意序列(字符串、列表)中的子项,按照它们在序列中的顺序来进行迭代。

In [3]: str1 = ‘mao’

In [4]: for i in str1:
…: print(i)
…:
m
a
o

对列表的循环与字符串几乎一致,只是子项不同。

In [5]: list1 = [‘a’,’aa’,’aaa’]
In [6]: for i in list1:
…: print(i,len(i))
…:
a 1
aa 2
aaa 3

for语句与range函数结合是一种很常见的组合,range函数主要提供一个数值序列。

In [8]: for j in range(len(list1)):
…: print(j,list1[j])
…:
0 a
1 aa
2 aaa

range提供的数值索引方式可以参考列表的切片,同样默认以0开始,且不包括*后一位,上面这种场合利用enumerate()函数表达会更加简单。

In [9]: for i,v in enumerate(list1):
…: print(i,v)
…:
0 a
1 aa
2 aaa

2、while语句
In [10]: i = 1
In [11]: list2 = []
In [12]: while i<=5:
…: list2.append(i)
…: i+=1
In [14]: list2
Out[14]: [1, 2, 3, 4, 5]

3、break、continue、pass
break语句的作用就是用来跳出一个for或while循环。

In [15]: for i in range(0,5):
…: if i == 3:
…: break
…: else:
…: print(i)
0

可以看到在for循环中加了一个判断语句,当i等于3时会跳出for循环,不再继续执行,输出与语义符合。

continue语句的作用就是表示继续执行下一次迭代,可以结合判断语句使用,在什么条件下继续执行,或者什么条件下跳出循环。

In [20]: for i in range(2,7):
…: if i%2==0:
…: print(‘An even number’,i)
…: continue
…: if i>4:
…: print(‘beyond 4’)
…: break
…:
An even number 2
An even number 4
beyond 4

pass语句主要起到一个占位的作用,而有的语句后面是不能为空的,比如if、for、while,可以利用pass占位,不会发生报错。

In [1]: while True:
…: pass

迭代器、生成器
我们通常接触到的迭代器有序列,比如字符串、列表等等,利用iter()方法可以构建一个迭代器,与之匹配的next()方法可以迭代返回迭代器内的值,并将返回值弹出迭代器。

In [1]: it = iter(‘python’)
In [2]: print(it)
<str_iterator object at 0x00000187ADA75A00>

In [3]: next(it)
Out[3]: ‘p’
In [4]: next(it)
Out[4]: ‘y’

当next方法迭代到*后一个值时,继续调用next方法会弹出报错。当然也可以利用for循环迭代输出:

In [6]: for i in it:
…: print(i)
t
h
o
n

如果你深入了解Python之后会发现生成器用起来会更加方便,结合函数即可。生成器的关键字为yield,生成器也具有next()方法,并且可以利用for循环迭代输出。

In [8]: def reverse(data):
…: for i in range(len(data)-1,-1,-1):
…: yield data[i]
In [9]: gen = reverse(‘mao’)
In [10]: print(gen)
<generator object reverse at 0x00000187AD99FDD0>

In [11]: next(gen)
Out[11]: ‘o’
In [12]: for i in gen:
…: print(i)
a
m

用小括号括起来的一个表达式可以用来创建一个生成器,下面将讲述如何来书写各种的表达式。

In [14]: gen2 = (i for i in range(0,5))

In [15]: print(gen2)
<generator object <genexpr> at 0x00000187ADA18D60>

表达式
列表表达式
各种表达式为我们创建相关数据类型提供了一种更加简单的方法,首先是列表表达式,普通的方法是通过将一些操作应用于序列的每个成员并通过返回的元素创建列表,或者通过满足特定条件的元素创建子序列。比如我们想获取10以内的平方数,利用普通方法如下:

In [16]: squares = []
In [17]: for i in range(10):
…: squares.append(i ** 2)

In [18]: squares
Out[18]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

但一行语句就足以囊括上面的内容,可见列表表达式的简洁性。

In [20]: squares = [x**2 for x in range(10)]

In [21]: squares
Out[21]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

上面这个列表表达式应该是比较简单的,我们还可以在表达式中加入判断语句作为筛选条件。

In [22]: list2 = [x*2 for x in range(10) if x*2>3]

In [23]: list2#0和2被过滤掉了
Out[23]: [4, 6, 8, 10, 12, 14, 16, 18]

进一步可以结合两个列表的元素组成一个列表表达式:

In [24]: list3 = [(x,y) for x in [1,2] for y in [2,3]if x!=y]

In [25]: list3
Out[25]: [(1, 2), (1, 3), (2, 3)]

在复杂一点的列表表达式就是嵌套类型的,但是这样的表达式缺点就是可读性不太好,因为你需要去思考他的一些细节,比如将一个23的矩阵转变为32的矩阵。

In [28]: mat = [[1,2,3],[4,5,6]]
In [29]: list3 = [[row[i] for row in mat] for i in range(3)]

In [31]: list3
Out[31]: [[1, 4], [2, 5], [3, 6]]

集合表达式
集合表达式与列表表达式有两处不同:

1、集合表达式需要用{}
2、集合表达式返回的是集合,所以会对元素去重
In [32]: set1 = {i for i in ‘abcdddddd’ if i not in ‘abc’}

In [33]: set1
Out[33]: {‘d’}

可以看到for语句中的字符串原本有很多d,经过条件过滤和集合去重*后只剩下了一个。

当然也可以通过set()方法将一个列表表达式转换为集合的形式。

In [34]: set2 = set([(x,y) for x in range(2) for y in range(2)])

In [35]: set2
Out[35]: {(0, 0), (0, 1), (1, 0), (1, 1)}

字典表达式
字典是python中很常用的一种数据类型,所以字典表达式也显得尤为重要。字典表达式同样也用{}构建,只不过表达式还要用(key:value对)的形式。

In [36]: dict1 = {x:x**2 for x in range(1,4)}
In [37]: dict1
Out[37]: {1: 1, 2: 4, 3: 9}

字典表达式也可以结合一些方法、语句实现更多的功能。

In [38]: dict2 = {x:len(x) for x in [‘aa’,’bb’,’ccc’]}
In [39]: dict2
Out[39]: {‘aa’: 2, ‘bb’: 2, ‘ccc’: 3}

利用items()方法很容易实现键值对转换,不过需要注意字典的键需要唯一。

In [40]: dict3 = {v:k for k,v in dict2.items()}
In [41]: dict3
Out[41]: {2: ‘bb’, 3: ‘ccc’}

通过上面几个例子可以看到,表达式是由包含一个表达式的括号组成,表达式后面会跟一个for语句,之后还可以跟零或者多个for或if语句,结果*终会返回一个指定的数据类型,其中的元素是通过表达式依据后面的for和if语句计算而得出的。

Python之错误和异常、模块

Python之错误和异常、模块(基础系列第四篇)

系列第四篇主要讲两方面,错误和异常以及模块。在编程时遇见错误信息在所难免,Python中会也有很多种错误信息,常见的两种就是语法错误和异常,这两个是完全不同的概念,下面就开始介绍一下这两个概念的相关知识。

错误和异常
语法错误
语法错误英文表示为SyntaxError,后面会跟着一些关于错误的解释信息,方便你查找语句中的bug,如下:

In [5]: print(‘naitangmao)
File “<ipython-input-5-d5b793a8884b>”, line 1
print(‘naitangmao)
^
SyntaxError: EOL while scanning string literal

语法错误顾名思义就是你的代码语句写错了,比如上面这个语句的错误就是少了一个引号。发生错误后,解释器会给出文件的名字和错误行号,以及在错误行下面有一个”^”,这个提示你代码出错的位置,一般会位于箭头前面,这些提示会便于编写者尽快找出错误。

异常
有的时候一行代码在语法上可能没有什么错误,但是执行的时候解释器也会报红,这种错误信息可以称为异常,和语法错误相比,异常的种类更多也更加常见。

举两个简单的例子:

In [6]: print(1/0)
—————————————————————————
ZeroDivisionError Traceback (most recent call last)
<ipython-input-6-2fc232d1511a> in <module>
—-> 1 print(1/0)

ZeroDivisionError: division by zero

都知道0是不能作为分母出现的,所以Python就会给出一个ZeroDivisionError,并提醒你这是一个零除错误。

In [9]: 1+’1′
—————————————————————————
TypeError Traceback (most recent call last)
<ipython-input-9-d3bd1e37a107> in <module>
—-> 1 1+’1’

TypeError: unsupported operand type(s) for +: ‘int’ and ‘str’

整形与字符串之间不存在加法运算,解释器分析出后会给出一个TypeError,这是一个类型错误,并且在后面给出错误的解释。

异常处理
对于Python解释器而言,如果一段程序中某个部分出现了异常,那么其后面的代码将不会被运行,但Python中有方法可以对异常进行处理,让异常不报红,进而帮助整个程序完成运行,这种行为称作捕获异常,以try … except语句组合实现。

In [11]: a = 1;b = ‘2’
In [12]: try:
…: print(a+b)
…: except TypeError:
…: print(‘类型错误!’)
…:
类型错误!

捕获异常实现的过程:

1.执行try和except关键字之间的部分
2.如果没有异常发生,except子句在try语句执行完毕后就被忽略了。
3.如果在 try 子句执行过程中发生了异常,那么该子句其余的部分就会被忽略。如果异常匹配于except关键字后面指定的异常类型,就执行对应的except子句。然后继续执行try/except语句之后的代码。
4.如果发生了一个异常,在except子句中没有与之匹配的分支,它就会传递到上一级try语句中。如果*终仍找不到对应的处理语句,它就成为一个未处理异常,终止程序运行,显示提示信息。
为了避免第4条情况产生,在except语句中可以使用所有异常的父类Exception,这样就囊括了所有异常可能发生的状况:

In [15]: try:
…: print(a+b)
…: except Exception as e:
…: print(e)
…:
unsupported operand type(s) for +: ‘int’ and ‘str’

抛出异常
利用raise语句可以主动抛出一个异常,但抛出的异常必须是要继承于Exception的异常类或者异常示例。

In [16]: raise NameError(‘naitangmao’)
—————————————————————————
NameError Traceback (most recent call last)
<ipython-input-16-b751158801b2> in <module>
—-> 1 raise NameError(‘naitangmao’)

NameError: naitangmao

除此上面介绍之外,用户还可以根据自己的需求自己定义异常,不再过多介绍,建议掌握的部分是每种异常出现的原因以及异常处理的方法。

模块
第二部分就是模块,我们有时可能会在不同文件之间利用同一个函数,笨一点的做法就是copy来copy去,Python提供了一个机制可以在将一个文件中的内容导入另一个文件中使用,这样的文件就可以称为模块,需要注意的是并不是任何一个文件都可以当做模块,而必须是包含Python定义和声明的文件。

举一个简单的小例子帮助理解上面这段话,首先可以创建一个odd_num.py文件,然后这个文件中只有一个函数,功能是实现过滤掉一个范围内的偶数:

In [18]: def odd(n):
…: result = []
…: for i in range(n):
…: if i % 2 != 0:
…: result.append(i)
…: return result
…:

然后我们可以在另一个文件中导入这个模块,如果我们想使用这个函数的话,就可以通过模块名.函数名的方法调用该函数,如下:

In [20]: import odd_num
In [21]: odd_num.odd(20)
Out[21]: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

如果你只想用一个模块中的某个子模块,那么在导入时就可以指明需要导入的部分,这样子模块就可以单独使用,不必在以模块名.函数名的形式:

In [22]: from odd_num import odd
In [23]: odd(20)
Out[23]: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

如果你还想偷懒的话,可以利用’*’的形式导入,这中写法是导入一个模块中的所有子模块:

In [24]: from odd_num import *
1
这种方法往往是不被推荐的,因为会让代码的可读性变差。

如果你有很多自定义的模块,为了更加规范、易找,那么你就可以将这些模块存入一个”包”中,需要注意的是,这个包中比如要有一个名为__init__.py的文件,这个文件可以为空但必须存在,然后导入包中模块的方式就是包名.模块名。

Python本身也有自带的模块库,有一部分模块是内置于解释器之中,然后用户可以直接访问这类模块的接口,很大程度上提高了效率,比如time、sys等等。如果你对于一个模块比较陌生,你可以利用dir()函数搜索某个模块的定义,返回的结果是一个列表,其中包括模块内的方法、可供调用的接口等等。

In [24]:dir(time)
Out[24]:[‘_STRUCT_TM_ITEMS’, ‘__doc__’, ‘__loader__’,’__name__’,’__package__’,’__spec__’,’altzone’,’asctime’,’ctime’,’daylight’,
‘get_clock_info’,’gmtime’,’localtime’,’mktime’,’monotonic’,’monotonic_ns’,’perf_counter’,’perf_counter_ns’,’process_time’,
‘process_time_ns’,’sleep’,’strftime’,’strptime’,’struct_time’,’thread_time’,’thread_time_ns’,’time’,’time_ns’,’timezone’,’tzname’]

综上是对错误和异常、模块两方面常用知识的一些概括,如果你对更高阶的使用感兴趣可以查找Python的官方文档,里面会有更加详细的介绍。

Android读取文本内容

一、读取文本文件

public static String readFileContent(InputStream inputStream) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader reader = null;
StringBuffer sbf = new StringBuffer();
try {
reader = new BufferedReader(inputStreamReader);
String tempStr;
while ((tempStr = reader.readLine()) != null) {
sbf.append(tempStr);
}
reader.close();
return sbf.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return sbf.toString();
}
二、以下这部分代码,先获取assets目录下test.txt文本文件的文件流,使用以上的读取InputStream的方法,获取文本内容

public WallBean getFaceWall() {
String content = null;
try {
content = FileUtils.readFileContent(getResources().getAssets().open(“test.txt”));
} catch (IOException e) {
e.printStackTrace();
}
if (TextUtils.isEmpty(content)) {
return null;
}
return new Gson().fromJson(content, new TypeToken<WallBean>() {
}.getType());
}
-END

Android & socket学习分享(二)

本次实现效果:

远程访问,动态显示,如下图

%title插图%num

1)首先是可以对idea端进行代码优化(可忽略,不影响结果)
即将所用到的类方法单独拿出写成工具类,如下图,我将分享一中的类方法取出改写成了以下形式

以NetFileData.java 为例,改写成单独的工具类,直接调用使用即可,方法过多以后,可避免结构复杂也可方便改写。

package lrz.data;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

public class NetFileData {

public static ArrayList<String> exeDir(String cmdBody) throws Exception {
// TODO Auto-generated method stub
ArrayList<String> backList=new ArrayList<String>();

File file = new File(cmdBody);
File[] listFiles = file.listFiles();
for(File mfile:listFiles){
String fileName = mfile.getName();
long lastModified = mfile.lastModified();//获取文件修改时间
SimpleDateFormat dateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);//给时间格式,例如:2018-03-16 09:50:23
String fileDate = dateFormat.format(new Date(lastModified));//取得文件*后修改时间,并按格式转为字符串
String fileSize=”0″;
String isDir=”1″;
if(!mfile.isDirectory()){//判断是否为目录
isDir=”0″;
fileSize=””+mfile.length();
}
backList.add(fileName+”>”+fileDate+”>”+fileSize+”>”+isDir+”>”);
}
return backList;
}
}
在主函数中以此方式调用

 

2)客户端在分享一的基础上对返回的数据进行ui设计,使用适配器填充数据,并设计点击事件实现一个客户端与服务端的动态互动效果。
废话不多说,我直接源码

MainActivity.java

package com.example.sockettest.app;

import androidx.appcompat.app.AppCompatActivity;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import android.Manifest;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.sockettest.R;
import com.example.sockettest.SocketClient;
import com.tbruyelle.rxpermissions2.RxPermissions;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

SwipeRefreshLayout swipeRefreshLayout;
public static final String KEY_SERVER_ACK_MSG = “KEY_SERVER_ACK_MSG”;
private Handler handler = null;
EditText url,way,dir;
ListView lv;
Button submit;
SocketClient socketClient=null;
String here;
private static final String KEY_HERE=”here”;
ArrayList<String> data;
int port;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

url=findViewById(R.id.url);
way=findViewById(R.id.way);
dir=findViewById(R.id.dir);
lv=findViewById(R.id.listview);
submit=findViewById(R.id.submit);

initdata();

handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {

Bundle data_bundle = msg.getData();
data=data_bundle.getStringArrayList(KEY_SERVER_ACK_MSG);

data=dataMaker();
printAdapter(data);

return false; }
});

submit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
port=Integer.parseInt(way.getText().toString());
here=dir.getText().toString();
savedata();
getdata();
}
});

lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
here=here+”/”+data.get(position);
savedata();
getdata();
}
});
}

private void getdata() {
socketClient=new SocketClient(url.getText().toString(),port,handler);
socketClient.work(here);
}

private void savedata() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(KEY_HERE,here);
editor.apply();
}

private void initdata() {
SharedPreferences sharedPreferences= PreferenceManager.getDefaultSharedPreferences(this);
here=sharedPreferences.getString(here,”d:/androidtest”);
}

private ArrayList<String> dataMaker() {
ArrayList<String> dataResult=new ArrayList<>();
int i=data.size();
for (int j = 0; j <i ; j++) {
String str=data.get(j);
str=str.substring(0,str.indexOf(“>”));
dataResult.add(str);
}

return dataResult;
}

private void printAdapter(ArrayList<String> data) {
ArrayAdapter<String> arrayAdapter=new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,data);
lv.setAdapter(arrayAdapter);
}

}

activity_main.xml

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
android:orientation=”vertical”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”horizontal”>
<EditText
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”3″
android:id=”@+id/url”
android:text=”10.218.216.10″/>
<EditText
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”3″
android:id=”@+id/way”
android:text=”8019″/>

</LinearLayout>
<EditText
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:id=”@+id/dir”
android:text=”d://”/>
<Button
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:text=”submit”
android:id=”@+id/submit”/>
<ListView
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:id=”@+id/listview”/>
<!– <TextView–>
<!– android:layout_width=”match_parent”–>
<!– android:layout_height=”wrap_content”–>
<!– android:id=”@+id/tv”–>
<!– android:text=”show”/>–>
</LinearLayout>
SocketClient.java与分享一中一样,不变,这里我图方便使用了*简单的适配器,大家可以参考上学期2-5,2-6中的内容,自定义适配器或者使用其他适配器,显示出额外的属性,进行不一样的布局,也可参考实验一天气预报app中的ui设计,美化界面。

本文内容仅为基础例子分享,实现基本功能,可拓宽思维,实现自己的小功能,本人水平有限,若有问题可在评论区提出~~

Android & socket学习分享(一)

本文仅为个人学习笔记,个人学习分享

%title插图%num

idea服务端代码:
package lrz.server;
import lrz.data.NetFileData;

import java.io.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;

public class ServerSocket01 {
int port = 8019;// 自定义一个端口,端口号尽可能挑选一些不被其他服务占用的端口,祥见http://blog.csdn.net/hsj521li/article/details/7678880
static int connect_count = 0;// 连接次数统计
ArrayList<String> msgBackList;

public ServerSocket01() {
// TODO Auto-generated constructor stub
}

public ServerSocket01(int port) {
super();
this.port = port;
}

private void printLocalIp(ServerSocket serverSocket) {// 枚举打印服务端的IP
try {
System.out.println(“服务端命令端口prot=” + serverSocket.getLocalPort());
Enumeration<NetworkInterface> interfaces = null;
interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
Enumeration<InetAddress> addresss = ni.getInetAddresses();
while (addresss.hasMoreElements()) {
InetAddress nextElement = addresss.nextElement();
String hostAddress = nextElement.getHostAddress();
System.out.println(“本机IP地址为:” + hostAddress);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

public void work() throws IOException {
// 注意:由于Socket的工作是阻塞式,Android端Socket的工作必须在新的线程中实现,若在UI主线程中工作会报错

ServerSocket serverSocket = new ServerSocket(port);
printLocalIp(serverSocket);
while (true) {// 无限循环,使之能结束当前socket服务后,准备下一次socket服务

System.out.println(“Waiting client to connect…..”);
Socket socket = serverSocket.accept();// 阻塞式,直到有客户端连接进来,才会继续往下执行,否则一直停留在此代码
System.out.println(“Client connected from: ”
+ socket.getRemoteSocketAddress().toString());

// eclipse 快捷键
// alt+/ 代码补全
// ctr+1 代码修正
// ctr+2,L 命名给局部变量

// TODO: As follows:

// 实现读客户端发送过来的命令,例如实现private ArrayList<String> readSocketMsg(Socket socket) throws IOException函数
// 调用 ArrayList<String> cmdList=readSocketMsg(socket);
// 定义一个全局变量 ArrayList<String> msgBackList,供服务端处理命令,并将返回结果赋值给msgBackList
// msgBackList=dealCmd(cmdList);//处理命令,例如dir命令,并将处理结果给msgBackList
// 实现服务端写回数据函数 private void writebackMsg(Socket socket) throws IOException
// 将msgBackList按规定的格式写回给客户端
// 实现 private void close(Socket socket) throws IOException,关闭socket
// 调用 close(socket);
ArrayList<String> cmdList=readSocketMsg(socket);

cmdList.forEach(s -> System.out.println(s));

String cmdbody=cmdList.get(0);
try {
msgBackList= exeDir(cmdbody);
} catch (Exception e) {
e.printStackTrace();
}
msgBackList.forEach(s -> System.out.println(s));
writeBackMsg(socket);
socket.close();
System.out.println(“当前Socket服务结束”);

}
}
public ArrayList<String> readSocketMsg(Socket socket) throws IOException {
// 读socket的输入流,传入的socket参数是已经连接成功未处于关闭的socket
//首先读取一行,并将读取的字符串内容转换为int型数据,已获得后续需要读取的行数
ArrayList<String> msgList=new ArrayList<String>();
InputStream inputStream = socket.getInputStream();
InputStreamReader reader = new InputStreamReader(inputStream, “utf-8″);
BufferedReader bufferedReader=new BufferedReader(reader);
String lineNumStr = bufferedReader.readLine();
int lineNum=Integer.parseInt(lineNumStr);
for(int i=0;i<lineNum;i++){
String str = bufferedReader.readLine();
msgList.add(str);
}
//读取结束后,输入流不能关闭,此时关闭,会将socket关闭,从而导致后续对socket写操作无法实现
return msgList;
}
private void writeBackMsg(Socket socket) throws IOException {
// TODO Auto-generated method stub
BufferedOutputStream os = new BufferedOutputStream(socket.getOutputStream());//socket.getOutputStream()是输出流,BufferedOutputStream则将其封装为带缓冲的输出流
OutputStreamWriter writer=new OutputStreamWriter(os,”UTF-8″);//尝试将字符编码改成”GB2312”
writer.write(“”+msgBackList.size()+”\n”);//未真正写入的输出流,仅仅在内存中
writer.flush();//写入输出流,真正将数据传输出去
// OutputStreamWriter writer=new OutputStreamWriter(os);//默认的字符编码,有可能是GB2312也有可能是UTF-8,取决于系统
// //建议不要用默认字符编码,而是指定UTF-8,以保证发送接收字符编码一致,不至于出乱码
//输出流是字节传输的,还不具备字符串直接写入功能,因此再将其封装入OutputStreamWriter,使其支持字符串直接写入
for(int i=0;i<msgBackList.size();i++){
writer.write(msgBackList.get(i)+”\n”);
writer.flush();
}
}

private ArrayList<String> exeDir(String cmdBody) throws Exception {
// TODO Auto-generated method stub
ArrayList<String> backList=new ArrayList<String>();

File file = new File(cmdBody);
File[] listFiles = file.listFiles();
for(File mfile:listFiles){
String fileName = mfile.getName();
long lastModified = mfile.lastModified();//获取文件修改时间
SimpleDateFormat dateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);//给时间格式,例如:2018-03-16 09:50:23
String fileDate = dateFormat.format(new Date(lastModified));//取得文件*后修改时间,并按格式转为字符串
String fileSize=”0″;
String isDir=”1″;
if(!mfile.isDirectory()){//判断是否为目录
isDir=”0″;
fileSize=””+mfile.length();
}
backList.add(fileName+”>”+fileDate+”>”+fileSize+”>”+isDir+”>”);
}
return backList;
}

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
new ServerSocket01().work();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

}
android stdio客户端代码:
MainActivity.java

package com.example.sockettest.app;

import androidx.appcompat.app.AppCompatActivity;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.example.sockettest.R;
import com.example.sockettest.SocketClient;
import com.tbruyelle.rxpermissions2.RxPermissions;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

public static final String KEY_SERVER_ACK_MSG = “KEY_SERVER_ACK_MSG”;
private Handler handler = null;
EditText url,way,dir;
TextView tv;
Button submit;
SocketClient socketClient=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

url=findViewById(R.id.url);
way=findViewById(R.id.way);
dir=findViewById(R.id.dir);
tv=findViewById(R.id.tv);
submit=findViewById(R.id.submit);

handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
Bundle data_bundle = msg.getData();
ArrayList<String> data=data_bundle.getStringArrayList(KEY_SERVER_ACK_MSG);
tv.setText(data.toString());
return false; }
});

submit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int port=Integer.parseInt(way.getText().toString());
socketClient=new SocketClient(url.getText().toString(),port,handler);
socketClient.work(dir.getText().toString());
}
});

}

}
activity_main.xml

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
android:orientation=”vertical”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:orientation=”horizontal”>
<EditText
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”3″
android:id=”@+id/url”
android:text=”10.218.216.10″/>
<EditText
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”3″
android:id=”@+id/way”
android:text=”8019″/>

</LinearLayout>
<EditText
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:id=”@+id/dir”
android:text=”d://”/>
<Button
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:text=”submit”
android:id=”@+id/submit”/>

<TextView
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:id=”@+id/tv”
android:text=”show”/>
</LinearLayout>
SocketClient.java

package com.example.sockettest;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;

public class SocketClient {
private String ip;
private int port;
private ArrayList<String> cmd;
private int time_out=10000;
private Handler handler;
private Socket socket;
public static final String KEY_SERVER_ACK_MSG = “KEY_SERVER_ACK_MSG”;
private OutputStreamWriter writer;
private BufferedReader bufferedReader;
public SocketClient(String ip, int port, Handler handler) {
this.port = port;
this.ip = ip;
this.handler = handler;
}
private void connect() throws IOException {
InetSocketAddress address=new InetSocketAddress(ip,port);
socket=new Socket();
socket.connect(address,time_out);
}
private void writeCmd(String cmd) throws IOException {
BufferedOutputStream os=new BufferedOutputStream(socket.getOutputStream());
writer=new OutputStreamWriter(os,”UTF-8″);
writer.write(“1\n”);
writer.write(cmd+”\n”);
writer.flush();
}
private ArrayList<String> readSocketMsg() throws IOException {
ArrayList<String> msgList=new ArrayList<>();
InputStreamReader isr=new InputStreamReader(socket.getInputStream(),”UTF-8″);
bufferedReader=new BufferedReader(isr);
String numStr = bufferedReader.readLine();
int linNum = Integer.parseInt(numStr);
for (int i = 0; i <linNum ; i++) {
String s = bufferedReader.readLine();
msgList.add(s);
}
return msgList;
}
private void close() throws IOException {
bufferedReader.close();
writer.close();
socket.close();
}
private void doCmdTask(String cmd){
ArrayList<String> msgList=new ArrayList<>();
try {
connect();
writeCmd(cmd);
msgList = readSocketMsg();
close();
} catch (IOException e) {
e.printStackTrace();
}
Message message = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putStringArrayList(KEY_SERVER_ACK_MSG,msgList);
message.setData(bundle);
handler.sendMessage(message);

}
public void work(final String cmd){
new Thread(new Runnable() {
@Override
public void run() {
doCmdTask(cmd);
}
}).start();
}
}

iOS本机号码一键登录

我们发现很多知名的App都有本机号码一键登录的功能,甚至你把他卸载了再安装,他依然能实现一键登录的功能,是不是感觉好方便,好想自己的App也有这个功能。那他们是怎么实现的呢?
方案一:阿里云号码认证服务

%title插图%num

我们只需要集成阿里云的SDK,按照帮助文档,就可以轻松实现,授权页面是人家写好了,开发者可以在他的基础上修改。

方案二:中国移动一键登录

%title插图%num

和阿里云的类似,也是需要集成SDK,按照帮助文档一步步集成即可

还有很多其他一键登录的第三方服务,原理基本一样,都支持全屏、弹窗样式

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速