[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被打开的时候,会获得一个文件描述符。计算机资源是有限的,所有操作系统都会做限制;
就是为了保护计算机的资源要被完全耗尽,计算资源是共享的,不是独占的;
一般情况下,除非特别明确的知道资源情况,否则不要提高资源的限制值来解决问题,不好的情况下只会让系统资源崩溃;
格言
为求善聚不如善散,善始不如善终