IO编程与正则表达式
- 正则表达式
- IO编程
-
- 文件读写
- StringIO与BytesIO
- 操作文件和目录
- 序列化
-
- pickle模块实现序列化与反序列化
-
- pickle.dumps()函数将对象转换为字节流
- [pickle.dump()将 Python 对象写入文件](#pickle.dump()将 Python 对象写入文件)
- pickle模块实现反序列化
-
- pickle.loads()函数从字节流反序列化
- [pickle.load()从文件读取并反序列化 Python 对象](#pickle.load()从文件读取并反序列化 Python 对象)
- 自定义序列化
- JSON实现序列化与反序列化
正则表达式
定义
正则表达式(Regular Expression,简称 regex)是一种用于匹配字符串的工具,它定义了一种字符串的模式,通常用于查找、替换、验证等操作。
组成
正则表达式是由普通字符(例如a到z之间的字母)和特殊字符(称为"元字符")组成的一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑。
普通字符
普通字符就是那些在正则表达式中直接表示其字面意义的字符。它们包括所有的ASCII字符和Unicode字符(取决于正则表达式的实现和使用的字符集)。例如,字母a到z、数字0到9、标点符号以及空格等都是普通字符。
在正则表达式中,普通字符匹配的就是它们自身。例如,正则表达式a匹配的就是字符串中的字符a。
元字符
.
:匹配除换行符\n之外的任何单个字符。例如'py.'可以匹配'pyc'、'pyo'、'py!'等等。
^
:匹配输入字符串的开始位置。如果设置了多行模式(multiline mode),则匹配每一行的开始。
$
:匹配输入字符串的结束位置。如果设置了多行模式,则匹配每一行的结束。
*
:匹配前面的子表达式零次或多次。例如,zo能匹配"z"以及"zoo"。
+
:匹配前面的子表达式一次或多次。例如,zo+能匹配"zo"以及"zoo",但不能匹配"z"。
?
:匹配前面的子表达式零次或一次。例如,do(es)?可以匹配"do"或"does"。
{n}
:n是一个非负整数,匹配前面的子表达式恰好n次。例如,o{2}不能匹配"Bob"中的"o",但能匹配"food"中的两个o。
{n,}
:n是一个非负整数,至少匹配n次。例如,o{2,}不能匹配"Bob"中的"o",但能匹配"foooood"中的所有o。o{1,}等价于o+。o{0,}等价于o 。
{n,m}
:m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,o{1,3}将匹配"fooooood"中的前三个o。o{0,1}等价于o?。请注意在逗号和两个数之间不能有空格。
[]
:字符集合。匹配所包含的任意一个字符。例如,[abc]可以匹配"plain"中的"a"。
[^]
:负字符集合。匹配未包含的任意字符。例如,[^abc]可以匹配"plain"中的"p"和"l"。
|
:或运算符。匹配左边或右边的表达式。例如,a|b可以匹配"a"或"b"。a|bc匹配"a"或"bc"。
()
:分组。将多个字符或表达式组合成一个单元,用于后续的引用或操作。例如,(abc)将"abc"作为一个整体来匹配。
\
:转义字符。用于匹配那些具有特殊含义的字符,或表示那些不易直接在正则表达式中表示的字符。例如,.匹配点字符.本身,而\ 匹配美元符号 匹配美元符号 匹配美元符号。
\b
:匹配一个单词边界,即单词和空格之间的位置。例如,er\b可以匹配"never"中的"er",但不能匹配"verb"中的"er"。
\B
:匹配非单词边界。例如,er\B可以匹配"verb"中的"er",但不能匹配"never"中的"er"。
\d
:匹配一个数字字符。等价于[0-9]。
\D
:匹配一个非数字字符。等价于[^0-9]。
\s
:匹配任何空白字符,包括空格、制表符、换页符等。等价于[ \f\n\r\t\v]。
\S
:匹配任何非空白字符。等价于[^ \f\n\r\t\v]。
\w
:匹配包括下划线的任何单词字符。等价于[A-Za-z0-9_]。
\W
:匹配任何非单词字符。等价于[^A-Za-z0-9_]。
应用示例
python
\d{3}\s+\d{3,8} # 匹配以任意个空格隔开的带区号的电话号码,例如:021 1234567
[0-9a-zA-Z\_] # 可以匹配一个数字、字母或者下划线;
[0-9a-zA-Z\_]+ # 可以匹配至少由一个数字、字母或者下划线组成的字符串,比如'a100','0_Z','Py3000'等等;
[a-zA-Z\_][0-9a-zA-Z\_]{0, 19} # 更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)。
py # 可以匹配'python'
^py$ # 整行匹配,只能匹配'py'。
re模块
re模块是Python标准库中的一个内置模块,用于支持正则表达式操作。
匹配:
以下方法中pattern表示正则表达式,string表示要匹配的字符串
re.match(pattern, string, flags=0):尝试从字符串的起始位置匹配一个模式,如果匹配成功则返回一个匹配的对象,否则返回None。
re.search(pattern, string, flags=0):扫描整个字符串并返回第一个成功的匹配,如果匹配成功则返回一个匹配的对象,否则返回None。
re.findall(pattern, string, flags=0):在字符串中找到正则表达式所匹配的所有子串,并返回一个列表。
re.finditer(pattern, string, flags=0):在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
替换:
re.sub(pattern, repl, string, count=0, flags=0):在字符串中替换匹配的文本。可以指定替换目标、替换内容和替换次数等参数。
re.subn(pattern, repl, string, count=0, flags=0):与re.sub相同,但返回一个包含新字符串和替换次数的元组。
分割:
re.split(pattern, string, maxsplit=0, flags=0):按照能够匹配的子串将字符串分割后返回列表。
编译:
re.compile(pattern, flags=0):编译一个正则表达式模式,返回一个模式的可重用对象。
切分字符串
python
print(re.split(r'\s+', 'a b c'))
['a', 'b', 'c']
python
s = re.split(r'[\s\,\;]+', 'a,b;; c d')
print(s)
['a', 'b', 'c', 'd']
分组
正则表达式用()表示的就是要提取的分组(Group)。
比如,提取一串电话号码的区号和本地号码:
python
m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
print(m.group(1))
print(m.group(2))
print(m.groups())
010
12345
('010', '12345')
贪婪匹配
正则表达式默认匹配模式是贪婪匹配,即尽可能多的匹配。
匹配出数字后面的0:
python
rt = re.match(r'^(\d+)(0*)$', '102300').groups()
print(rt)
('102300', '') # 这里\d+采用贪婪匹配,直接把后面的0全部匹配了,结果0*只能匹配空字符串了
rb = re.match(r'^(\d+?)(0*)$', '102300').groups()
print(rb)
('1023', '00') # \d+?添加了?让\d+采用非贪婪匹配
编译
先预编译一条正则表达式,后面可以重复使用
python
re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
print(re_telephone.match('010-12345').groups())
print(re_telephone.match('010-12345').group())
print(re_telephone.match('010-12345').group(1))
('010', '12345')
010-12346
010
IO编程
IO编程(输入输出编程)指的是在计算机程序中处理输入和输出的方式。输入可以是从用户、文件、网络或其他设备读取数据,而输出是将数据写入到用户界面、文件、网络或设备。IO操作在程序中扮演着至关重要的角色,尤其是在处理大数据、网络通信和文件操作时。
在Python中,IO操作包括文件读写、网络通信、用户输入输出等。常见的IO编程技术有同步IO和异步IO。
同步IO是最常见的IO方式。在这种模式下,程序在执行IO操作时会等待操作完成后再继续执行后续代码。每当程序执行一个IO操作时,它会被阻塞,直到该操作完成。
异步IO是一种更高效的方式,尤其适用于需要处理大量IO操作(如网络请求或文件读取)的应用。在这种模式下,程序不会被阻塞。相反,IO操作会在后台执行,当操作完成时,程序可以通过回调函数或者事件循环来获取结果。
同步和异步的区别就在于是否等待IO执行的结果。好比你去麦当劳点餐,你说"来个汉堡",服务员告诉你,对不起,汉堡要现做,需要等5分钟,于是你站在收银台前面等了5分钟,拿到汉堡再去逛商场,这是同步IO。
你说"来个汉堡",服务员告诉你,汉堡需要等5分钟,你可以先去逛商场,等做好了,我们再通知你,这样你可以立刻去干别的事情(逛商场),这是异步IO。
⚠️本文的IO编程是同步模式!!!
文件读写
读写文件的原理:
在磁盘上读写文件的功能都是由操作系统提供的,程序不能直接操控磁盘。所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。
读文件
打开文件对象:
python
f = open("D:/Code/project/PythonPractices/test.txt", "r") # "r"表示读
此时文件夹里没有test.txt文件,运行程序报错:
新建一个test.txt文件,内容为:IO编程测试
运行程序没有报错也没有输出,说明已经成功打开了文件对象,调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示:
python
f = open("D:/Code/project/PythonPractices/test.txt", "r", encoding="utf-8") # encoding="utf-8"设置编码方式,防止输出乱码
print(f.read())
文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的:
python
f = open("D:/Code/project/PythonPractices/test.txt", "r", encoding="utf-8")
print(f.read())
f.close()
为了保证无论是否出错都可以关闭文件,可以采用try...finally:
python
try:
f = open("D:/Code/project/PythonPractices/test.txt", "r", encoding="utf-8")
print(f.read())
finally:
if f:
f.close()
print("文件关闭")
Python引入了with语句来自动帮我们调用close()方法,可以简洁地实现上面try...finally的效果:
python
with open("D:/Code/project/PythonPractices/test.txt", "r", encoding="utf-8") as f:
print(f.read())
读取文件内容时可以根据文件大小选择合适的读取方式:
- 如果文件很小,read()一次性读取最方便;
- 如果不能确定文件大小,反复调用read(size)比较保险;
python
with open("D:/Code/project/PythonPractices/test.txt", "r", encoding="utf-8") as f:
print(f.read(3))
- 如果是配置文件,调用readlines()最方便:
python
with open("D:/Code/project/PythonPractices/test.txt", "r", encoding="utf-8") as f:
for line in f.readlines():
print(line.strip()) # 把末尾的'\n'删掉
要读取二进制文件,比如图片、视频等等,用'rb'模式打开文件即可:
python
f = open('/Users/michael/test.jpg', 'rb')
f.read()
写文件
python
f = open("D:/Code/project/PythonPractices/test.txt", "w", encoding="utf-8")
f.write("Hello World!")
f.close()
使用with语句,传入参数"a"在文件末尾追加写入:
python
with open("D:/Code/project/PythonPractices/test.txt", "a", encoding="utf-8") as f:
f.write("hello world!")
StringIO与BytesIO
StringIO
StringIO 是 Python 标准库 io 模块中的一个类,用于在内存中操作字符串,模拟文件的读取和写入操作。
getvalue()方法用于获得写入后的str
python
from io import StringIO
Sf = StringIO()
Sf.write("hello world!")
print(Sf.getvalue())
BytesIO
BytesIO用于在内存中处理字节流
python
from io import BytesIO
Bf = BytesIO()
Bf.write("中文".encode("utf-8"))
print(Bf.getvalue())
print(Bf.getvalue().decode("utf-8"))
注意:这里写入的不是str,而是经过UTF-8编码的bytes
操作文件和目录
在 Python 中,操作文件和目录通常使用内置的 os 和 shutil 模块。os 模块提供了文件和目录的基本操作功能,而 shutil 模块则提供了更高级的文件和目录操作工具。
操作文件
- open()函数读取和写入文件,如上面所述
- 复制文件
python
import shutil
# 复制文件
shutil.copy('example.txt', 'example_copy.txt')
# 复制并保留元数据
shutil.copy2('example.txt', 'example_copy2.txt')
- 删除文件
python
import os
# 删除文件
os.remove('example.txt')
- 移动文件
python
import shutil
# 移动文件
shutil.move('example.txt', 'new_directory/example.txt')
# 重命名文件
shutil.move('old_name.txt', 'new_name.txt')
- 检查文件是否存在
python
import os
# 检查文件是否存在
if os.path.exists('example.txt'):
print("File exists")
else:
print("File does not exist")
- 获取文件信息
python
import os
# 获取文件大小
file_size = os.path.getsize('example.txt')
print(f"File size: {file_size} bytes")
# 获取文件的最后修改时间
mod_time = os.path.getmtime('example.txt')
print(f"Last modified: {mod_time}")
操作目录
- 创建目录
python
import os
# 创建单个目录
os.mkdir('new_directory')
# 创建多层目录
os.makedirs('parent_directory/sub_directory')
- 删除目录
python
import os
import shutil
# 删除空目录
os.rmdir('empty_directory')
# 删除非空目录
shutil.rmtree('non_empty_directory')
- 列出目录中的文件
python
import os
# 列出目录中的所有文件和目录
files = os.listdir('some_directory')
print(files)
python
from pathlib import Path
# 获取目录内容
path = Path('some_directory')
for item in path.iterdir():
print(item)
- 检查目录是否存在
python
import os
# 检查目录是否存在
if os.path.exists('some_directory'):
print("Directory exists")
else:
print("Directory does not exist")
- 改变当前工作目录
python
import os
# 改变当前工作目录
os.chdir('some_directory')
# 获取当前工作目录
current_directory = os.getcwd()
print(current_directory)
- 获取父目录或祖父目录
python
from pathlib import Path
# 获取父目录
path = Path('some_directory/file.txt')
parent_dir = path.parent
print(parent_dir)
# 获取祖父目录
grandparent_dir = path.parent.parent
print(grandparent_dir)
其他文件和目录操作
- 获取文件的绝对路径、目录路径等
python
import os
# 获取绝对路径
absolute_path = os.path.abspath('example.txt')
print(f"Absolute path: {absolute_path}")
# 获取文件名
file_name = os.path.basename(absolute_path)
print(f"File name: {file_name}")
# 获取文件所在的目录
directory_name = os.path.dirname(absolute_path)
print(f"Directory: {directory_name}")
- 判断是否为文件或目录
python
import os
# 判断路径是否为文件
if os.path.isfile('example.txt'):
print("It is a file")
# 判断路径是否为目录
if os.path.isdir('some_directory'):
print("It is a directory")
- 复制目录
python
import shutil
# 复制目录及其内容
shutil.copytree('source_directory', 'destination_directory')
序列化
我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling。序列化的目的是将复杂的对象(如 Python 对象)转换为字节流、字符串或其他格式,以便在网络上传输、存储到磁盘或其他地方。常见的序列化格式包括 JSON、Pickle、YAML、XML 等。反过来,反序列化是将存储或传输的数据格式还原为原始数据结构的过程。
pickle模块实现序列化与反序列化
pickle方式只适用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。
pickle.dumps()函数将对象转换为字节流
python
import pickle
# Python 对象
data = {
'name': 'Alice',
'age': 30,
'is_student': False
}
print(pickle.dumps(data))
输出结果:
python
D:\Code\project\PythonPractices\venv\Scripts\python.exe D:\Code\project\PythonPractices\IOtest.py
b'\x80\x04\x95*\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x05Alice\x94\x8c\x03age\x94K\x1e\x8c\nis_student\x94\x89u.'
Process finished with exit code 0
pickle.dump()将 Python 对象写入文件
python
import pickle
# Python 对象
data = {
'name': 'Alice',
'age': 30,
'is_student': False
}
# print(pickle.dumps(data))
# 将对象序列化并写入文件
with open('data.txt', 'wb') as f:
pickle.dump(data, f)
data.txt文件中是一串乱码
pickle模块实现反序列化
pickle.loads()函数从字节流反序列化
python
import pickle
# Python 对象
data = {
'name': 'Alice',
'age': 30,
'is_student': False
}
p = pickle.dumps(data)
print(pickle.loads(p))
输出结果:
python
D:\Code\project\PythonPractices\venv\Scripts\python.exe D:\Code\project\PythonPractices\IOtest.py
{'name': 'Alice', 'age': 30, 'is_student': False}
Process finished with exit code 0
pickle.load()从文件读取并反序列化 Python 对象
python
import pickle
# 反序列化
with open('data.txt', 'rb') as f:
loaded_data = pickle.load(f)
print(loaded_data)
输出结果:
python
D:\Code\project\PythonPractices\venv\Scripts\python.exe D:\Code\project\PythonPractices\IOtest.py
{'name': 'Alice', 'age': 30, 'is_student': False}
Process finished with exit code 0
自定义序列化
python
import pickle
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __getstate__(self):
# 返回一个可序列化的字典
return {'name': self.name, 'age': self.age}
def __setstate__(self, state):
# 反序列化时重建对象
self.name = state['name']
self.age = state['age']
# 创建对象
person = Person('Alice', 30)
# 序列化
pickled_data = pickle.dumps(person)
print(pickled_data)
# 反序列化
loaded_person = pickle.loads(pickled_data)
print(loaded_person.name, loaded_person.age)
JSON实现序列化与反序列化
JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:
JSON类型 | Python类型 |
---|---|
{} | dict |
[] | list |
"string" | str |
123.4 | int/float |
true/false | True/False |
null | None |
JSON转换Python对象
python
import json
# Python 对象
data = {
'name': 'Alice',
'age': 30,
'is_student': False
}
# 序列化为 JSON 格式
json_data = json.dumps(data)
print("json_data:", json_data)
# 反序列化 JSON 格式为 Python 对象
data_parsed = json.loads(json_data)
print("data_parsed:", data_parsed)
python
json_data: {"name": "Alice", "age": 30, "is_student": false}
data_parsed: {'name': 'Alice', 'age': 30, 'is_student': False}
Process finished with exit code 0
JSON将Python对象序列化写入文件
python
import json
# Python 对象
data = {
'name': 'Alice',
'age': 30,
'is_student': False
}
with open('data.json', 'w') as f:
json.dump(data, f)
print("写入成功")
with open('data.json', 'r') as f:
print(f.read())
输出结果:
python
写入成功
{"name": "Alice", "age": 30, "is_student": false}
JSON读取文件反序列化
python
with open('data.json', 'r') as f:
data_parsed = json.load(f)
print(data_parsed)
print(type(data_parsed))
输出结果:
python
{'name': 'Alice', 'age': 30, 'is_student': False}
<class 'dict'>
自定义序列化及反序列化
python
import json
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def to_dict(self):
return {'name': self.name, 'age': self.age}
def dict_to_student(d):
return Student(d['name'], d['age'], d['score'])
s = Student('Alice', 30, 90)
print(json.dumps(s, default=lambda obj: obj.to_dict()))
# 输出:{"name": "Alice", "age": 30, "score": 90}
json_str = json.dumps(s, default=lambda obj: obj.to_dict())
print(json.loads(json_str, object_hook=dict_to_student))
# 输出:<__main__.Student object at 0x0000017DE0A73510>