[toc]
python 文件的IO一
函数相关练习补充
- 实现Base64 解码
#!/usr/bin/env python
# base64 解码实现
alphabet = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
def base64decode(src:bytes):
ret = bytearray()
length = len(src)
step = 4 #对齐的,每次取4个
for offset in range(0,length, step):
tmp = 0x00
block = src[offset:offset + step]
# 开始移位计算
for i,c in enumerate(reversed(block)):
# 替换字符为序号
index = alphabet.find(c)
if index == -1:
continue # 找不到就是0.
tmp += index << i*6
ret.extend(tmp.to_bytes(3,'big'))
return bytes(ret.rstrip(b'\x00')) # 把最右的 \x00去掉,不可变
# base64的decode
#txt = "TWFu"
#txt = "TWE="
#txt = "TQ=="
#txt = "TWEuTWE="
txt = "TWEUTQ=="
txt = txt.encode()
print(txt)
print(base64decode(txt).decode())
# base64实现
import base64
print(base64.b64decode(txt).decode())
# 输出
b'TWEUTQ=='
MaM
MaM
还是有提高效率的地方
- revesed可以不需要
- alphabet.find效率底
from collections import OrderedDict
base_tbl = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
alphabet = OrderedDict(zip(base_tbl,range(64)))
def base64decode(src:bytes):
ret = bytearray()
length = len(src)
step = 4 # 对齐的,每次取4个
for offset in range(0,length, step):
tmp = 0x00
block = src[offset:offset + step]
# 开始移位计算
for i in range(4):
index = alphabet.get(block[-i-1])
if index is not None:
tmp += index << i*6
# 找不到,不用移位相加了
ret.extend(tmp.to_bytes(3,'big'))
return bytes(ret.rstrip(b'\x00')) # 把最右边的\x00 去掉,不可变
# base64的decode
#txt = "TWFu"
#txt = "TWE="
#txt = "TQ=="
#txt = "TWEuTWE="
txt = "TWEUTQ=="
txt = txt.encode()
print(txt)
print(base64decode(txt).decode())
# base64实现
import base64
print(base64.b64decode(txt).decode())
- 完善命令分发器,实现函数可以带任意参数(可变参数除外),解析参数并要求用户输入
即解决下面的问题:
def cmds_dispatcher():
#命令和函数存储的地方
commands = {}
# 注册
def reg(name):
def _reg(fn):
commands[name] =fn
return _reg
def defaultfunc():
print("Unknown command")
def dispatcher():
while True:
cmd = input(">> ")
if cmd.strip() == 'quit':
return
commands.get(cmd,defaultfunc)()
return reg,dispatcher
cr,cp = cmds_dispatcher()
# 自定久函数,注册
@cr('mag')
def foo1():
print("welcome ssjinyao")
@cr('py')
def foo2():
print("welcome python")
cp()
- 思路:
- 注册的时候,因定死,
@reg('py',200,100)
可以认为@reg('py',200,100)
和@reg('py',300,100)
是不同的函数,可以用partial函数; - 运行时,在输出cmd的时候,逗号分割 ,获取参数;
#!/usr/bin/env python
from functools import partial
# 自定义函数可以有任意参数,可变参数、keyword-only 除外
def command_dispatcher():
# 构建全局字典
cmd_tbl = {}
# 注册函数
def reg(cmd,*args,**kwargs):
def _reg(fn):
func = partial(fn,*args,**kwargs)
cmd_tbl[cmd] = func
return func
return _reg
# 缺省函数
def default_func():
print("Unknown command")
# 调度器
def dispatcher():
while True:
cmd = input('Pleaase input cmd >>> ')
# 退出条件
if cmd.strip() == '':
return
cmd_tbl.get(cmd,default_func)()
return reg,dispatcher
reg,dispatcher = command_dispatcher()
# 自定义函数
@reg('jy',z=200,y=300,x=100)
def foo1(x,y,z):
print('ssjinyao',x,y,z)
@reg('py',300,b=400)
def foo2(a,b=200):
print('python',a,b)
dispatcher()
文件IO常用操作
数据落地、不管怎样数据都要写到磁盘上固态上、这种称为数据落地、持久化;
回顾CPU组成及作用
- 运算器,完成各种算数运算、逻辑运算、数据传输等数据加工处理;
- 控制器,控制程序的执行;
- 存储器,用于记忆程序和数据,例如内存;
- 输入设备,将数据或者程序输入到计算机中,例如键盘、鼠标;
- 输出设备,将数据或程序的处理结果展示给用户,例如显示器、打印机等;
一般说IO操作,指的是文件IO,如果指的是网络IO,都会直接说网络IO。
open | 打开 |
---|---|
read | 读取 |
write | 写入 |
close | 关闭 |
readline | 行读取 |
readlines | 多行读取 |
seek | 文件指针操作 |
tell | 指针位置 |
文件操作中,最常用的就是读和写;
文件访问的模式有两种: 文本模式和二进制模式。不同模式上函数不尽相同。表现的结果也是不一样的;
open的参数
file
打开或者要创建的文件名。 如果不指定路径,默认是当前路径;
mode模式
描述字符 | 意义 |
---|---|
r | 缺省的,表示只读打开 |
w | 只写打开 |
x | 创建并写入一个新文件 |
a | 写入打开,如果文件存在,则追加 |
b | 二进制模式 |
t | 缺省的,文本模式 |
+ | 读写打开一个文件。给原来只读、只写方式打开提供缺失的读或者写能力 |
open 默认是只读模式r打开已经存在的文件。
r
只读打开文件,也果使用write方法,会抛异常;
如果文件不存在,抛出FileNotFoundError异常;
w
表示只写方式打开,如果读取则抛出异常;
如果文件不存在,则直接创建文件;
如果文件存在,则清空文件内容;
x
文件不存在,创建文件,并只写方式打开;
文件存在,抛出FileExistsError异常;
a
文件存在,只写打开,追回内容;
文件不存在,则创建后,只写打开,追加内容
r 是只读,wxa 是只写
wxa 都可以产生新文件,w不管文件存在与否,都会生成全新内容的文件;
a 不管文件是否存在,都能打开的文件尾部追加;x必须需求文件事先不存在,自己造一个新文件;
encoding: 编码, 仅文本模式使用
None 表示使用缺省编码,依赖操作系统。win、 linux下测试如下代码
f = open('test1',w)
f.write('写入')
f.close()
#windows下缺缺省GBK(0XBOA1), Linux下缺省UTF-8(0xE5 95 8a)
其它参数
- errors: 什么样的编码错误将被捕获;
- None和strict表示有编码错误将抛出ValueError异常;ignore表示忽略;
- newline: 文本模式中,换行的转换。可以为None、’’、’\r’、’\n’、’\r\n’
- 读时,None表示’\r’、’\n’、’\r\n’都被转换为’\n’; 表示不会自动转换通用换行符;
- 其它合法字符表示换行符就是指定字符,就会按照指定字符分行;
- 写时,None表示’\n’都会被替换为系统缺省分隔符os.linesep;’\n’或’’表示’\n’不替换; 其它合法字符表示’\n’会被替换为指定 的字符;
- colsefd: 关闭文件描述符,True表示关闭它。False会在文件关闭后保持这个描述符;
fileobj.fileno()
查看;
read
read(size=-1)
size表示读取多少个字符或字节; 负数或者None表示读取到EOF;
行读取
readline(size=-1)
一行行读取文件内容,size设置一次能读取行内几个字符或字节;readline(hint=-1)
读取所有行的列表。指定hint则返回指定的行数;
write
write(s)
把字符串s写入到文件中并返回字符的个数;
close
flush并关闭文件对象;
文件已经关闭,再次关闭没有任何效果
其它
seekable()
是否可seekreadable()
是否可读writeable()
是否可写closed
是否已经关闭
上下文件管理
问题的引出
在Linux中执行
In [13]: lst = []
In [14]: for _ in range(60000):
...: lst.append(open('test'))
...:
在linux上查看
# lsof | grep ipython | wc -l
120158
# ulimit -a | grep "open file"
open files (-n) 65535
In [18]: for x in lst:
...: x.close()
...:
关闭后对比
# lsof | grep ipython | wc -l
158
ulimit -a 查看所有限制。其中open files 就是打开文件数的限制,默认1024;
将文件一次关闭,然后就可以继续打开了。再看一次losf
如何解决 ?
1、 异常处理
当出现异常的时候,拦截异常。但是,因为很多代码可都可能出现OSError异常,还不好判断异常就是应为资源限制产生的。
f = open('test')
try:
f.write("abc") # 文件只读,写入失败
finally:
f.close() # 这样才行
2、 上下文管理
def f
with open('test') as f:
f.write("abc") # 文件只读,写入失败
#测试f是否关闭
f.closed # f 的作用域
一种特殊的语法、交级解释器去释放文件对象;
- 使用with .. as 关键字;
- 上下文管理的语句块并不会开启新的作用域;
- with语句块执行完的时候,会自动关闭文件;
另一种写法
f1 = open('test')
with f1:
f1.write("abc") #文件只读,写入失败
# 测试f是否关闭
f1.closed # f1的作用域
对于类似于文件对象的IO对象,一般来说都需要在不使用的时候关闭、注销,以释放资源;
IO被打开的时候,会获得一个文件描述符。计算机资源是有限的,所有操作系统都会做限制;
就是为了保护计算机的资源要被完全耗尽,计算资源是共享的,不是独占的;
一般情况下,除非特别明确的知道资源情况,否则不要提高资源的限制值来解决问题,不好的情况下只会让系统资源崩溃;
格言
为求善聚不如善散,善始不如善终