day18
笔记来源于:黑马程序员python教程,8天python从入门到精通,学python看这套就够了
目录
7、Socket服务端开发
Socket基础概念
socket (简称 套接字) 是进程之间通信一个工具,好比现实生活中的插座,所有的家用电器要想工作都是基于插座进行,进程之间想要进行网络通信需要socket。
Socket负责进程之间的网络数据传输,好比数据的搬运工。
客户端和服务端
2个进程之间通过 Socket 进行相互通讯,就必须有服务端和客户端。
Socket服务端:等待其它进程的连接、可接受发来的消息、可以回复消息;
Socket客户端:主动连接服务端、可以发送消息、可以接收回复。
Socket 服务端编程
主要分为如下几个步骤:
-
创建 socket 对象
import socket # 创建 Socket 对象 socket_server = socket.socket()
-
绑定 socket_server 到指定 IP 和地址
socket_server.bind(host, port) # 示例 socket_server.bind(("localhost", 8888 ))
-
服务端开始监听端口
# 监听端口 # backlog 为 int 整数,表示允许的连接数量,超出的会等待,可以不填,不填会自动设置一个合理值 socket_server.listen(backlog) # 示例 socket_server.listen(1) # listen 方法接受一个整数,表示接受的连接数量
-
接收客户端连接,获得连接对象
# 等待客户端链接 # result: tuple = socket_server.accept() # conn = result[0] # 客户端和服务端的链接对象 # address = result[1] # 客户端的地址信息 # 简便写法 conn, address = socket_server.accept() # accept 方法返回的是二元元组(链接对象, 客户端地址信息) # 可以通过 变量1, 变量2 = socket_server.accept() 的形式,直接接受二元元组内的两个元素 # accept() 方法是阻塞的方法,等待客户端的链接,如果没有连接,就卡在这一行不再向下执行了 print(f"接收到了客户端的链接,客户端信息是:{address}")
-
客户端连接后,通过recv方法,接收客户端发送的消息
# 接收客户端信息, 要使用客户端和服务端本次的链接对象,而非 socket_server 对象 data = conn.recv(1024).decode("UTF-8") # recv 接收的参数是缓冲区大小:一般给 1024 即可 # recv 方法的返回值是一个字节数组,也就是 bytes 对象,不是字符串,可以通过decode方法通过 UTF-8 编码,将字符数组转换为字符串对象 print(f"客户端发来的消息是: {data}")
-
通过conn(客户端当次连接对象),调用send方法可以回复消息
# 发送回复消息 msg = input("请输入你和客户端要回复的消息:").encode("UTF-8") # encode可以将字符串编码为字节数组对象 conn.send(msg)
-
conn(客户端当次连接对象)和socket_server对象调用close方法,关闭连接
# 关闭链接 conn.close() socket_server.close()
示例代码:
"""
演示 Socket 服务端开发
"""
import socket
# 创建 Socket 对象
socket_server = socket.socket()
# 绑定ip和端口
# socket_server.bind(host, port)
socket_server.bind(("localhost", 8888))
# 监听端口
# socket_server.listen(backlog)
socket_server.listen(1)
# listen 方法接受一个整数,表示接受的连接数量
# 等待客户端链接
# result: tuple = socket_server.accept()
# conn = result[0] # 客户端和服务端的链接对象
# address = result[1] # 客户端的地址信息
# 简便写法
conn, address = socket_server.accept()
# accept 方法返回的是二元元组(链接对象, 客户端地址信息)
# 可以通过 变量1, 变量2 = socket_server.accept() 的形式,直接接受二元元组内的两个元素
# accept() 方法是阻塞的方法,等待客户端的链接,如果没有连接,就卡在这一行不再向下执行了
print(f"接收到了客户端的链接,客户端信息是:{address}")
while True:
# 接收客户端信息, 要使用客户端和服务端本次的链接对象,而非 socket_server 对象
data = conn.recv(1024).decode("UTF-8")
# recv 接收的参数是缓冲区大小:一般给 1024 即可
# recv 方法的返回值是一个字节数组,也就是 bytes 对象,不是字符串,可以通过decode方法通过 UTF-8 编码,将字符数组转换为字符串对象
print(f"客户端发来的消息是: {data}")
# 发送回复消息
msg = input("请输入你和客户端要回复的消息:")
if msg == 'exit':
break
conn.send(msg.encode("UTF-8")) # encode可以将字符串编码为字节数组对象
# 关闭链接
conn.close()
socket_server.close()
网络调试助手
下载网络调试助手作为客户端
示例:
8、Socket客户端编程
主要分为如下几个步骤:
-
创建 socket 对象
import socket # 创建 Socket 对象 socket_client = socket.socket()
-
连接到服务端
# 连接到服务器 socket_client.connect(("localhost", 8888))
-
发送消息
while True: # 发送信息 msg = input("请输入要给服务端发送的消息:") if msg == 'exit': break socket_client.send("你好呀".encode("UTF-8"))
-
接收返回消息
while True: # 发送信息 msg = input("请输入要给服务端发送的消息:") if msg == 'exit': break socket_client.send("你好呀".encode("UTF-8")) # 接收返回消息 recv_data = socket_client.recv(1024) # 1024是缓冲区的大小,一般1024即可.同样recv方法是阻塞的 print(f"服务端回复的消息是: {recv_data.decode('UTF-8')}")
-
关闭连接
# 关闭链接 socket_client.close()
示例代码:
"""
演示 Socket 客户端开发
"""
import socket
# 创建 Socket 对象
socket_client = socket.socket()
# 连接到服务器
socket_client.connect(("localhost", 8888))
while True:
# 发送信息
msg = input("请输入要给服务端发送的消息:")
if msg == 'exit':
break
socket_client.send("你好呀".encode("UTF-8"))
# 接收返回消息
recv_data = socket_client.recv(1024) # 1024是缓冲区的大小,一般1024即可.同样recv方法是阻塞的
print(f"服务端回复的消息是: {recv_data.decode('UTF-8')}")
# 关闭链接
socket_client.close()
9、正则表达式—— 基础方法
正则表达式,又称规则表达式(Regular Expression),是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则)的文本。
简单来说,正则表达式就是使用:字符串定义规则,并通过规则去验证字符串是否匹配。
比如,验证一个字符串是否是符合条件的电子邮箱地址,只需要配置好正则规则,即可匹配任意邮箱。
比如通过正则规则: (^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$)
即可匹配一个字符串是否是标准邮箱格式。
但如果不使用正则,使用if else来对字符串做判断就非常困难了。
正则的三个基础方法
Python正则表达式,使用re模块,并基于re模块中三个基础方法来做正则匹配。
分别是:match、search、findall 三个基础方法
- re.match(匹配规则, 被匹配字符串)
从被匹配字符串开头进行匹配, 匹配成功返回匹配对象(包含匹配的信息),匹配不成功返回空。
# match
s= "python itheima python python"
# match 从头匹配
result = re.match("python", s)
print(result)
print(result.span())
print(result.group())
# 结果
<re.Match object; span=(0, 6), match='python'>
(0, 6)
python
s= "1python itheima python python"
# match 从头不匹配
result = re.match("python", s)
print(result)
# 结果
None
-
search(匹配规则, 被匹配字符串)
搜索整个字符串,找出匹配的。从前向后,找到第一个后,就停止,不会继续向后,整个字符串都找不到,返回 None
# search 搜索匹配 s= "1python itheima python python" result = re.search("python", s) print(result) # 结果 <re.Match object; span=(1, 7), match='python'> # search 搜索不匹配 s= "1python itheima python python" result = re.search("python2", s) print(result) # 结果 None
-
findall(匹配规则, 被匹配字符串)
匹配整个字符串,找出全部匹配项
找不到返回空 list: []
# findall 搜索全部匹配 s= "1python itheima python python" result = re.findall("python", s) print(result) # 结果 ['python', 'python', 'python'] # findall 搜索全部匹配 s= "1python itheima python python" result = re.findall("python2", s) print(result) # 结果 []
10、正则表达式—— 元字符匹配
正则最强大的功能在于元字符匹配规则。
单字符匹配
示例:
字符串 s = "itheima1 @@python2 !!666 ##itcast3"
- 找出全部数字:
re.findall(r‘\d’, s)
字符串的r标记,表示当前字符串是原始字符串,即内部的转义字符无效而是普通字符
-
找出特殊字符:
re.findall(r‘\W’, s)
-
找出全部英文字母:
re.findall(r’[a-zA-Z]’, s)
[]
内可以写:[a-zA-Z0-9]
这三种范围组合或指定单个字符如[aceDFG135]
示例代码:
"""
演示 Python 正则表达式使用元字符进行匹配
"""
import re
# 单字符匹配
s = "itheima1 @@python2 !!666 ## itccast3"
# 找出所有数字
result = re.findall(r'\d', s) # 字符串前面带上 r 的标记, 表示字符串转义字符无效,就是普通字符的意思
print(result)
# 结果
['1', '2', '6', '6', '6', '3']
# 找出所有的非单词字符
result = re.findall(r'\W', s)
print(result)
# 结果
[' ', '@', '@', ' ', '!', '!', ' ', '#', '#', ' ']
# 找出所有的单词字符 和 数字
result = re.findall(r'[a-zA-Z]', s)
print(result)
result = re.findall(r'[0-9]', s)
print(result)
# 结果
['i', 't', 'h', 'e', 'i', 'm', 'a', 'p', 'y', 't', 'h', 'o', 'n', 'i', 't', 'c', 'c', 'a', 's', 't']
['1', '2', '6', '6', '6', '3']
数量匹配
边界匹配
分组匹配
案例
-
匹配账号,只能由字母和数字组成,长度限制6到10位
规则为:
^[0-9a-zA-Z]{6, 10}$
示例代码:
# 匹配账号, 只能由字母和数字组成,长度限制 6 到 10 位 r = '^[a-zA-Z0-9]{6,10}$' # 注意正则表达式没有多余的空格 s = '123456' print(re.findall(r, s)) # 结果 ['123456']
-
匹配QQ号,要求纯数字,长度5-11,第一位不为0
规则为:
^[1-9][0-9]{4, 10}&
[1-9]匹配第一位,[0-9]匹配后面4到10位
示例代码:
# 匹配 QQ 号:要求纯数字,长度 5-11,第一位不为 0 r = '^[1-9][0-9]{4,10}$' s = '012345678' print(re.findall(r, s)) # 结果 []
-
匹配邮箱地址,只允许qq、163、gmail这三种邮箱地址
规则为:
^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+&
-
[\w-]+
表示出现a-z A-Z 0-9 _
和-
字符最少一个,最多不限 -
(\.[\w-]+)*
,表示出现组合.
和a-z A-Z 0-9 _ -
的组合最少0次,最多不限,用于匹配:abc.ced.efg@123.com
中的ced.efg
这部分 -
@
表示匹配@
符号 -
(qq|163|gmail)
表示只匹配这3个邮箱提供商 -
(\.[\w-]+)+
表示a-z A-Z 0-9 _ -
的组合最少1次,最多不限用于匹配
abc.ced.efg@123.com.cn
中的.com.cn
这种最后使用
+
表示最少一次,即比如:.com
多了可以是:
.com.cn.eu
这样
示例代码:
# 匹配邮箱地址,只允许 qq、163、gmail这三种邮箱地址 # {内容}.{内容}.{内容}.{内容}.{内容}.{内容}.{内容}@{内容}.{内容}.{内容} r = '(^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$)' s = 'a.b.c.d.e@163.com.a.z.c.d.e' print(re.findall(r, s)) print(re.match(r, s)) # 结果 [('a.b.c.d.e@163.com.a.z.c.d.e', '.e', '163', '.e')] <re.Match object; span=(0, 27), match='a.b.c.d.e@163.com.a.z.c.d.e'>
-
11、递归
递归概念
递归在编程中是一种非常重要的算法
递归: 即方法(函数)自己调用自己的一种特殊编程写法
如:
函数调用自己,即称之为递归调用。
递归找文件
最典型的递归场景为找出一个文件夹中全部的文件。
os 模块的 3个方法
有如下文件结构:
$ tree -L 3
.
├── 1.txt
├── 2.txt
├── 3.txt
├── a
│ ├── a1.txt
│ ├── a2.txt
│ └── a3.txt
└── b
├── b1.txt
├── b2.txt
└── b3.txt
2 directories, 9 files
示例代码:
import os
def Test_os():
"""演示 os 模块的 3个基础方法"""
# 列出文件下的内容 (给出一个文件夹内的所有文件和文件夹)
print(os.listdir("/home/yin-roc/hf_all"))
# 判断指定路径是否是文件夹 (判断所给的路径是否是一个文件夹,是则返回 True,否则返回 False)
print(os.path.isdir("/home/yin-roc/hf_all"))
# 判断指定路径是否存在 (判断一个路径是否存在,是则返回 True,否则返回 False)
print(os.path.exists("/home/yin-roc/hf_all"))
if __name__ == '__main__':
Test_os()
# 结果
['a', '1.txt', '3.txt', 'b', '2.txt']
True
True
案例
有如下文件结构:
$ tree -L 3
.
├── 1.txt
├── 2.txt
├── 3.txt
├── a
│ ├── a1.txt
│ ├── a2.txt
│ └── a3.txt
└── b
├── b1.txt
├── b2.txt
└── b3.txt
2 directories, 9 files
示例代码:
"""
演示 Python 的操作
需求:通过递归,找出一个指定文件夹内的全部文件
思考:写一个函数,列出文件夹的全部内容,如果是文件就收集到 list
如果是文件夹,就递归调用自己,再次判断
"""
import os
def get_files_recursion_from_dir(path):
"""
从指定的文件夹中使用递归模式,获取全部的文件列表
:param path: 被判断的文件夹
:return: 包含全部的文件,如果目录不存在或者无文件就返回一个list
"""
print(f"当前判断的文件夹是: {path}")
file_list = []
if os.path.exists(path):
for f in os.listdir(path):
new_path = path + "/" + f
if os.path.isdir(new_path):
# 进入到这里,表明这个目录是文件夹不是文件
file_list += get_files_recursion_from_dir(new_path)
else:
file_list.append(new_path)
else:
print(f"指定的目录{path}, 不存在")
return []
return file_list
if __name__ == '__main__':
print(get_files_recursion_from_dir("/home/yin-roc/test"))
# 结果
当前判断的文件夹是: /home/yin-roc/test
当前判断的文件夹是: /home/yin-roc/test/a
当前判断的文件夹是: /home/yin-roc/test/b
['/home/yin-roc/test/a/a3.txt', '/home/yin-roc/test/a/a2.txt', '/home/yin-roc/test/a/a1.txt', '/home/yin-roc/test/1.txt', '/home/yin-roc/test/3.txt', '/home/yin-roc/test/b/b3.txt', '/home/yin-roc/test/b/b1.txt', '/home/yin-roc/test/b/b2.txt', '/home/yin-roc/test/2.txt']