抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

[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

还是有提高效率的地方

  1. revesed可以不需要
  2. 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()
  • 思路:
  1. 注册的时候,因定死,@reg('py',200,100)
    可以认为@reg('py',200,100)@reg('py',300,100)是不同的函数,可以用partial函数;
  2. 运行时,在输出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)

其它参数

  1. errors: 什么样的编码错误将被捕获;
  2. None和strict表示有编码错误将抛出ValueError异常;ignore表示忽略;
  3. newline: 文本模式中,换行的转换。可以为None、’’、’\r’、’\n’、’\r\n’
  4. 读时,None表示’\r’、’\n’、’\r\n’都被转换为’\n’; 表示不会自动转换通用换行符;
  5. 其它合法字符表示换行符就是指定字符,就会按照指定字符分行;
  6. 写时,None表示’\n’都会被替换为系统缺省分隔符os.linesep;’\n’或’’表示’\n’不替换; 其它合法字符表示’\n’会被替换为指定 的字符;
  7. colsefd: 关闭文件描述符,True表示关闭它。False会在文件关闭后保持这个描述符;
  8. fileobj.fileno()查看;

read

read(size=-1)
size表示读取多少个字符或字节; 负数或者None表示读取到EOF;

行读取

readline(size=-1)
一行行读取文件内容,size设置一次能读取行内几个字符或字节;
readline(hint=-1)
读取所有行的列表。指定hint则返回指定的行数;

write

write(s)把字符串s写入到文件中并返回字符的个数;

close

flush并关闭文件对象;
文件已经关闭,再次关闭没有任何效果

其它

seekable() 是否可seek
readable() 是否可读
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 的作用域

一种特殊的语法、交级解释器去释放文件对象;

  1. 使用with .. as 关键字;
  2. 上下文管理的语句块并不会开启新的作用域;
  3. with语句块执行完的时候,会自动关闭文件;

另一种写法

f1 = open('test')
with f1:
    f1.write("abc") #文件只读,写入失败
# 测试f是否关闭
f1.closed # f1的作用域

对于类似于文件对象的IO对象,一般来说都需要在不使用的时候关闭、注销,以释放资源;
IO被打开的时候,会获得一个文件描述符。计算机资源是有限的,所有操作系统都会做限制;
就是为了保护计算机的资源要被完全耗尽,计算资源是共享的,不是独占的;
一般情况下,除非特别明确的知道资源情况,否则不要提高资源的限制值来解决问题,不好的情况下只会让系统资源崩溃;

格言

为求善聚不如善散,善始不如善终

评论