目录
一、文件和流
打开文件
文件模式
缓冲
读写文件
读写行
使用 fileinput 进行迭代
文件迭代器
二、数据库与网络编程
数据库支持
网络编程
一些场景下,程序需要和外部进行交互,在前面用到了 input、print 函数与外部进行交互,本文将更进一步,介绍两种新的交互方式:文件和流。
open(name[,mode[,buffering]]) 用于打开文件并返回一个文件对象,open 函数的参数中,文件名 name 是强制参数,模式(mode)和缓冲(buffering)都是可选的。如下实例:
#打开文件读取其中全部内容
f = open("D:/Users/data.txt")
print(f.read())
输出结果(data.txt 中的全部内容):
just for testing1
just for testing2
just for testing3
如上面例子所示,open 函数只带一个文件名参数,可以顺利读取文件的内容,但如果向文件中写入内容则会报错,欲写入内容,必须提供一个写模式参数(w)。除此以外,文件模式参数还有:
参数 | 说明 |
---|---|
r | 读模式 |
a | 追加模式 |
b | 二进制模式 |
+ | 读写模式 |
读、写、追加模式简单易懂,不再赘述,关于二进制模式,则有必要说明一下。在没有特别说明的情况下,Python 处理的文件默认是文本文件,如果处理二进制文件(如声音或者图像),则需要在模式参数中增加 b,如 rb 可用于读取一个二进制文件。
例子1:
#打开文件,写入文本
f = open("D:/Users/data.txt",'w')
f.write('12345-12345-12345')
#打开文件,读取文本
f = open("D:/Users/data.txt",'r')
print(f.read())
#关闭文件
f.close()
执行结果:
12345-12345-12345
例子2:
#打开文件,在现有文件后追加信息并换行
f = open("D:/Users/data.txt",'a')
f.write('\nappend information')
f.close()
#打开文件,读取文本
f = open("D:/Users/data.txt",'r')
print(f.read())
#关闭文件
f.close()
执行结果:
12345-12345-12345
append information
open 函数的第三个参数控制着文件缓冲,如果该参数为0(或者 False),对文件读写操作都是无缓冲的,直接读写硬盘;如果该参数为1(或者 True),对文件的读写操作就是有缓冲的,数据将缓冲于内存中,只有当调用 flush 或者 close 函数时,才会更新硬盘上的数据。
需要说明的是,该参数可以大于1(代表缓冲区的大小,单位为字节),也可以为负数(代表使用默认的缓冲区大小)。
open 函数返回的只是一个文件对象,读写文件需要调用 read 函数和 write 函数,完成读写后,需要调用 close 函数。
read 函数可以带参数,指定读取的字节数,不带参数则默认读取文件全部内容。write 函数参数为字符串类型,即写入文件的内容。
#打开文件,在现有文件后追加信息并换行
f = open("D:/Users/data.txt",'a')
f.write('\nappend information')
f.close()
#打开文件,读取文件前4个字节
f = open("D:/Users/data.txt",'r')
print(f.read(4))
#关闭文件
f.close()
执行结果:
app
file.readline(),读取文件中单独的一行内容(从当前位置开始直到一个换行符出现),不带参数时,默认读取一整行;带参数(非负整数)则表示 readline 可以读取当前行内容的最大字节数。此外,可以通过 readline 函数来遍历文件,如下实例:
例子1:不带参数
#打开文件,写入信息并换行
f = open("D:/Users/data.txt",'w')
f.write('append information1\n')
f.write('append information2\n')
f.write('append information3\n')
f.close()
#打开文件,按行读取文件内容
f = open("D:/Users/data.txt",'r')
while True:
line = f.readline()
#读取完毕退出
if(not line):
break
print('content:',line)
#关闭文件
f.close()
执行结果(省略空行):
content: append information1
content: append information2
content: append information3
例子2:带参数
#打开文件,并写入信息
f = open("D:/Users/data.txt",'w')
f.write('append information1\n')
f.write('append information2')
f.close()
#打开文件,按行及参数限制读取文件内容
f = open("D:/Users/data.txt",'r')
while True:
line = f.readline(10)
#读取完毕退出
if(not line):
break
print('content:',line)
#关闭文件
f.close()
执行结果(省略空行):
content: append inf
content: ormation1
content: append inf
content: ormation2
readline 函数每次读取一行,与之对应的 readlines 则可一次读取整个文件的所有行。如下实例:
#打开文件,并写入信息
f = open("D:/Users/data.txt",'w')
f.write('append information1\n')
f.write('append information2')
f.close()
#打开文件,并一次读取所有行
f = open("D:/Users/data.txt",'r')
for line in f.readlines():
print('content:',line)
#关闭文件
f.close()
执行结果(省略空行):
content: append information1
content: append information2
通过 writelines 函数。可以一次向文件写入多行内容,如下实例:
#打开文件,一次写入多行内容
f = open("D:/Users/data.txt",'w')
content = ['append information1\n','append information2']
f.writelines(content)
f.close()
fileinput 模块可以对一个或多个文件中的内容进行迭代、遍历等操作。该模块的 input() 函数有点类似文件 readlines 函数,但区别明显:readlines 是一次性读取文件中的全部内容,如果文件较大的话,会占用大量内存;input 返回的则是一个迭代对象,结合 for 循环使用,典型用法如下:
import fileinput
for line in fileinput.input(filename):
process(line)
实例:
import fileinput
for line in fileinput.input("D:/Users/data.txt"):
print(line)
注意,input函数有多个参数:分别设置读取文件路径、读写模式、编码方式、缓冲区大小、备份文件扩展名等。
此外,fileinput 模块还有很多常用的函数,这里介绍几个最常用的,其余读者可根据前一篇文章中介绍的模块分析方法自行挖掘。
函数名 | 说明 |
---|---|
fileinput.input() | 返回能够用于for循环遍历的对象 |
fileinput.filename() | 返回当前文件的名称 |
fileinput.lineno() | 返回当前已经读取的行的数量(或者序号) |
fileinput.filelineno() | 回当前读取的行的行号 |
fileinput.isfirstline() | 检查当前行是否是文件的第一行 |
fileinput.isstdin() | 判断最后一行是否从stdin中读取 |
fileinput.close() | 关闭队列 |
本文最后介绍一个优雅的特性:文件迭代器。在 Python 中,如果该特性出现早一点,前面介绍的 readlines 等函数可能就不会出现了,Python 从2.2开始,文件对象具备可迭代特性。
先来看一个例子:
#打开文件,一次写入多行内容
f = open("D:/Users/data.txt",'w')
content = ['append information1\n','append information2']
f.writelines(content)
f.close()
#打开文件,通过文件迭代器遍历文件
f = open("D:/Users/data.txt",'r')
for line in f:
print('content:',line)
#关闭文件
f.close()
执行结果(省略空行):
content: append information1
content: append information2
借助文件迭代器,我们可以执行和普通迭代器相同的操作,比如,将读取内容转化为字符串列表(效果类似前面介绍的 readlines)。
#打开文件,一次写入多行内容
f = open("D:/Users/data.txt",'w')
content = ['append information1\n','append information2']
f.writelines(content)
f.close()
#打开文件,将读取内容转换为字符串列表
f = open("D:/Users/data.txt",'r')
lines = list(f)
print(lines)
#关闭文件
f.close()
执行结果:
['append information1\n', 'append information2']
在大多数应用场景中,数据持久化都是重要的需求。一般地,数据持久化可以采用文件,数据库系统,以及一些混合类型。
使用简单的文本文件就可以实现数据的读写,辅助完成很多功能。但是,在一些场景下,我们需要更强大的特性,比如,同时采用多个字段或属性进行复杂的搜索,显然,使用文本文件难以实现,数据库倒是一个不错的选择。
Python 标准数据库接口为 Python DB-API,Python DB-API 为开发人员提供了数据库应用标准接口,可以支持大部分数据库,包括 MySQL、PostgreSQL、Microsoft SQL Server、Oracle、Sybase 等。详细信息可查阅官网说明。
操作不同的数据库需要下载不同的 DB API 模块,例如访问 Oracle 数据库和 MySQL 数据库,需要下载 Oracle 和 MySQL 数据库模块。DB-API 是一个规范,它定义一个系列必须的对象和数据库存取方式,以便为各种各样的底层数据库系统和数据库接口程序提供一致的访问接口。
Python DB-API 为大多数数据库实现了接口,使用它连接各种数据库后,就可以使用相同的方式操作各种数据库。
Python 操作 MySQL
虽然不同数据库差异显著,但使用 Python DB-API 操作各种数据库的方式大体一致,可以归纳为四个步骤:
接下来以关系型数据库 MySQL 为例介绍 Python 操作数据库的一般方式。
1. 搭建数据库系统。
既然要操作数据库,就必须要有现成的数据库系统作为服务端,因此,需安装 MySQL,启动服务(非本文重点,不做展开)。
2. 安装与 MySQL 对应的 Python 模块。
本节以 mysql-connector-python 为例(附:下载地址)下载解压到自定义路径下,通过命令行进入包路径,执行安装命令:sudo python setup.py install
(Linux或Mac),或 Python setup.py install
(Windows)。
3. 导入模块并创建连接。
请参考下面代码:
import mysql.connector
#数据库配置,据实填写
db_config={'host':'127.0.0.1',
'user':'root',
'password':'123**456',
'port':3306,
'database':'test',
'charset':'utf8'
}
try:
#connect方法加载config的配置进行数据库的连接,完成后用一个变量进行接收
cnn=mysql.connector.connect(**db_config)
except mysql.connector.Error as e:
print('operation failed!',str(e))
else:
print("operation succeeded!")
4. 执行数据库操作。
代码如下:
#创建表的sql语句
sql_create_table='CREATE TABLE`student`\
(`id`int(10)NOT NULL AUTO_INCREMENT,\
`name`varchar(10) DEFAULT NULL,\
`age`int(3) DEFAULT NULL,\
PRIMARY KEY(`id`))\
ENGINE=MyISAM DEFAULT CHARSET = utf8'
#buffered=True会把结果集保存到本地并一次性返回,这样可以提高性能
cursor = cnn.cursor(buffered = True)
#执行sql语句,创建表
try:
cursor.execute(sql_create_table)
except mysql.connector.Error as err:
print('operation failed!',str(err))
#执行数据库操作
try:
#插入
sql_insert1="insert into student(name,age) values ('zhang san',18)"
cursor.execute(sql_insert1)
except mysql.connector.Error as err:
print('operation failed!',str(err))
finally:
#关闭数据库相关链接
cursor.close()
cnn.close()
5. 补充说明。
数据库的操作非常多,不便展开,本节仅做简要介绍。
Python 集成了针对常用网络协议的库,对网络协议的各个层次都进行了抽象封装,如此,开发者可以集中精力处理程序逻辑,而不必关心网络实现的细节。
Python 提供了丰富的网络工具,接下来,本节将介绍 Python 标准库提供的一个最基础的网络设计模块:Socket,以点带面。
Socket
在网络编程中,最基本的组件就是套接字(Socket),套接字是两个端点的程序之间信息传递的通道。程序通常分布运行在不同的计算机上,通过套接字相互传递信息,在 Python 中,大多数网络编程模块都隐藏了 Socket 模块的基本细节,不直接和套接字交互。
Python 中,我们用 socket() 函数来创建套接字,语法格式如下:
socket.socket([family[, type[, proto]]])
1. 参数解释:
AF_INET
,可以是 AF_UNIX
;SOCK_STREAM
)或数据报(SOCK_DGRAM
);2. Socket 常用内建函数
函数名 | 描述 |
---|---|
s.bind() | 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。 |
s.listen() | 开始 TCP 监听。backlog 指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 |
s.accept() | 被动接受 TCP 客户端连接,(阻塞式)等待连接的到来。 |
函数名 | 描述 |
---|---|
s.connect() | 主动初始化 TCP 服务器连接。一般 address 的格式为元组(hostname,port),如果连接出错,返回 socket.error 错误。 |
s.connect_ex() | connect() 函数的扩展版本,出错时返回出错码,而不是抛出异常。 |
函数名 | 描述 |
---|---|
s.recv() | 接收 TCP 数据,数据以字符串形式返回,bufsize 指定要接收的最大数据量。flag 提供有关消息的其他信息,通常可以忽略。 |
s.send() | 发送 TCP 数据,将 string 中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于 string 的字节大小。 |
s.close() | 关闭套接字 |
Socket 实例
基于 Socket 模块搭建一个简单的 server-client 系统,服务端启动后一直监听来自客户端的连接并回应“Hello!”。
1. 服务端代码
import socket
#创建 socket 对象
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host=socket.gethostname()
port=6666
# 绑定端口号
s.bind((host,port))
# 设置最大连接数,超过后排队
s.listen(5)
while True:
# 建立客户端连接
client,addr=s.accept()
print("Got connection from %s"%str(addr))
#回应客户端信息:hello
msg="Hello!"
client.send(msg.encode('utf-8'))
client.close()
2. 客户端代码
import socket
# 创建 socket 对象
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名,设置端口
host=socket.gethostname()
port=6666
# 连接服务,指定主机和端口
s.connect((host,port))
# 接收小于 1024 字节的数据
msg=s.recv(1024)
print(msg.decode('utf-8'))
s.close()
先执行服务端代码,再执行客户端代码,执行结果:
服务端:Got connection from xxxxx
客户端:Hello!