python文件处理、路径处理、序列化和反序列化

2018-09-14  本文已影响0人  vampire6

文件IO常用操作

一般说IO操作,指的是文件IO,如果指的是网络IO,会直接说。

把文件存储到磁盘上的这个过程,叫做落地。

column column
open 打开
read 读取
write 写入
close 关闭
readline 行读取
readlines 多行读取
seek 文件指针操作
tell 指针位置
open打开操作
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True,opener=None)

f = open("file名字")   #文件对象
print(f.read())     #读取文件
f.close()       #关闭文件

打开一个文件,返回一个文件对象(流对象)和文件描述符。打开文件失败,则返回异常。
基本使用: 创建一个文件test,然后打开它,用完关闭。

文件操作中最常用的就是读和写。文件访问的模式有两种:文本模式和二进制模式。

注:windows中使用codepage代码页。可以认为每一个代码页就是一张编码表 cp936和gbk等价。

mode模式
描述字符
r 缺省的,表示只读打开
w 只写打开,有的话就清除重新写
x 创建并写入一个新文件
a 写入打开,如果文件存在,则追加
b 二进制模式
t 缺省的,文本模式
+ 读写打开一个文件,给原来只读、只写的增加缺失的功能

open默认是只读模式r打开已经存在的文件。

t和b:

t/b不能单独存在,要和a/w/x/r配合使用。

seek文件指针

文件指针,指向当前字节位置。

mode = r,指针起始在0 ,mode = a 指针起始在EOF。

tell():显示指针当前位置。

seek(offset[, whence]) 移动文件指针位置。offest偏移多少字节,whence从哪里开始。

buffering缓冲区

-1 表示使用缺省大小的buffer。如果是二进制模式,使io.DEFAULT_BUFFER_SIZE值,默认是<font color= red>4096或者8192</font>。如果是文本模式,如果是终端设备,是行缓存方式,如果不是,则使用二进制模式的策略。

buffer 缓冲区

缓冲区一个内存空间,一般来说是一个FIFO队列,到缓冲区满了或者达到阈值,数据才会flush到磁盘。

flush() 将缓冲区数据写入磁盘 close() 关闭前会调用flush()。

io.DEFAULT_BUFFER_SIZE 缺省缓冲区大小,字节。

buffering 说明
buffering = -1 t和b,都是io.DEFAULT_BUFFER_SIZE
buffering = 0 b 关闭缓冲区
t 不支持
buffering = 1 b 就一个字节
t 行缓冲,遇到换行符才flush
buffering > 1 b模式表示行缓冲大小。缓冲区的值可以超过io.DEFAULT_BUFFER_SIZE,直到设定的值超出后才把缓冲区flush 。
t模式,是io.DEFAULT_BUFFER_SIZE字节,flush完后把当前字符串也写入磁盘

一般来说:

  1. 文本模式,一般都用默认缓冲区大小
  2. 二进制模式,是一个个字节的操作,可以指定buffer的大小
  3. 一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它
  4. 一般编程中,明确知道需要写磁盘了,都会手动调用一次flush,而不是等到自动flush或者close的时候
其他参数

编码:windows下缺省GBK(0xB0A1),Linux下缺省UTF-8(0xE5 95 8A)

errors :编码错误将被捕获 None和strict表示有编码错误将抛出ValueError异常;ignore表示忽略

newline:文本模式中,换行的转换。可以为None、'' 空串、'\r'、'\n'、'\r\n' 。

closefd:关闭文件描述符,True表示关闭它。False会在文件关闭后保持这个描述符。fileobj.fileno()查看。

文件描述符:Linux一切皆文件,文件打开后都会有一个位于的文件描述符,在计算机系统中是一个有限的资源。0,1,2,标准输入,标准输出,标准错误输出。

对于类似于文件对象的IO对象,一般来说都需要在不使用的时候关闭、注销,以释放资源。
IO被打开的时候,会获得一个文件描述符。计算机资源是有限的,所以操作系统都会做限制。就是为了保护计算机的资源不要被完全耗尽,计算资源是共享的,不是独占的。

一般情况下,除非特别明确的知道资源情况,否则不要提高资源的限制值来解决问题。

read()

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

readline(size=-1)
一行行读取文件内容。size设置一次能读取行内几个字符或字节。

readlines(hint=-1)
读取所有行的列表。指定hint则返回指定的行数。

write()

write(s),把字符串s写入到文件中并返回字符的个数 writelines(lines),将字符串列表写入文件。

close()

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

其他
名称 说明
seekable() 是否可seek
readable() 是否可读
writeable() 是否可写
closed() 是否已经关闭
上下文管理

1、异常处理
当出现异常的时候,拦截异常。但是,因为很多代码都可能出现OSError异常,还不好判断异常就是应为资源限制产生的。

f = open('test')
try:
    f.write("abc") # 文件只读,写入失败
finally:
    f.close() # 这样才行

使用finally可以保证打开的文件可以被关闭。

上下文管理

  1. 使用with ... as 关键字

  2. 上下文管理的语句块并不会开启新的作用域

  3. with语句块执行完的时候,会自动关闭文件对象

StringIO操作

io模块中的类

from io import StringIO

内存中,开辟的一个文本模式的buffer,可以像文件对象一样操作它

当close方法被调用的时候,这个buffer会被释放

from io import StringIO
# 内存中构建
sio = StringIO() # 像文件对象一样操作
print(sio.readable(), sio.writable(), sio.seekable())# True True True
sio.write("magedu\nPython")
sio.seek(0)  
print(sio.readline())  #magedu
print(sio.getvalue()) # 无视指针,输出全部内容  magedu   Python
sio.close()

好处

一般来说,磁盘的操作比内存的操作要慢得多,内存足够的情况下,一般的思路是少落地,减少磁盘IO的过程,可以大大的提高程序的运行效率。

BytesIO操作

io模块中的类

from io import BytesIO

内存中,开辟的一个二进制模式的buffer,可以像文件对象一样操作它

当close方法被调用的时候,这个buffer会被释放

from io import BytesIO # 内存中构建
bio = BytesIO()
print(bio.readable(), bio.writable(), bio.seekable()) #True True True
bio.write(b"magedu\nPython")
bio.seek(0)
print(bio.readline())  # b'magedu\n'
print(bio.getvalue()) # 无视指针,输出全部内容   b'magedu\nPython'
bio.close()

file-like对象

类文件对象,可以像文件对象一样操作。

socket对象,输入输出对象(stdin、stdout)都是类文件对象

from sys import stdout, stderr
f = stdout
print(type(f))  #<class 'ipykernel.iostream.OutStream'>
f.write('magedu.com')  #magedu.com

路径操作

os.path模块

3.4版本之前

from os import path
p = path.join('d:/','tmp')
print(type(p), p)               #<class 'str'> d:/tmp
print(path.exists(p))     #判断是否存在该路径  True
print(path.split(p)) # (head,tail)     ('d:/', 'tmp')
print(path.abspath('.'))   # 打印当前的绝对路径      C:\Users\vampire\python
p = path.join('D:/', p, 'test.txt')   #  'd:/tmp\\test.txt'
print(path.dirname(p)) # 目录名
print(path.basename(p)) #基名,就是文件名
print(path.splitdrive(p)) #二元组  ('d:', '/tmp\\test.txt')

p1 = path.abspath(".")  #“文件路径”
print(p1, path.basename(p1))
while p1 != path.dirname(p1):
    p1 = path.dirname(p1)
    print(p1, path.basename(p1))

​```
C:\Users\vampire\python python
C:\Users\vampire vampire
C:\Users Users
C:\ 
​```
pathlib模块

提供Path对象来操作。包括目录和文件。

导入模块:from pathlib import Path

目录操作初始化

p = Path() # 当前目录    WindowsPath('.')
p.absolute()#   WindowsPath('C:/Users/vampire/python')
p = Path('a','b','c/d') # 当前目录下的 WindowsPath('C:/Users/vampire/python/a/b/c/d')
p = Path('/etc') # 根下的etc目录

路径拼接和分解

操作符/

Path对象 / Path对象
Path对象 / 字符串 或者 字符串 / Path对象

分解

parts属性,可以返回路径中的每一个部分

p3.absolute()   #WindowsPath('C:/Users/vampire/python/c/a')
p3.absolute().parts   #('C:\\', 'Users', 'vampire', 'python', 'c', 'a')
joinpath

joinpath(*other) 连接多个字符串到Path对象中

p = Path()   #   WindowsPath('.')
p = p / 'a'   #  WindowsPath('a')
p.absolute()  #   WindowsPath('C:/Users/vampire/python/a')
p1 = 'b' / p  #   WindowsPath('C:/Users/vampire/python/b/a')
p2 = Path('c') #  WindowsPath('C:/Users/vampire/python/c')
p2.absolute()  #   WindowsPath('C:/Users/vampire/python/c')
p3 = p2 / p1  #  WindowsPath('c/b/a')
p3.absolute()  #  WindowsPath('C:/Users/vampire/python/c/b/a')
print(p3.parts)   #
p3.absolute().parts # ('C:\\', 'Users', 'vampire', 'python', 'c', 'b', 'a')
p3.joinpath('etc','init.d',Path('httpd'))
获取路径

str 获取路径字符串

bytes 获取路径字符串的bytes

p = Path('/etc')
print(str(p), bytes(p))

#  \etc   b'\\etc'
父目录

parent 目录的逻辑父目录

parents 父目录序列,索引0是直接的父

p = Path('/a/b/c/d')
print(p.absolute())   #C:\a\b\c\d
print(p.parent.parent)  #\a\b
for x in p.parents:
    print(x)
    
#\a\b\c
#\a\b
#\a
#\
目录的组合部分

name、stem、suffix、suffixes、with_suffix(suffix)、with_name(name)

name 目录的最后一个部分

suffix 目录中最后一个部分的扩展名

stem 目录最后一个部分,没有后缀

suffixes 返回多个扩展名列表

with_suffix(suffix) 有扩展名则替换,无则补充扩展名

with_name(name) 替换目录最后一个部分并返回一个新的路径

p = Path('mysqlinstall/mysql.tar.gz')
print(p.name)   #mysql.tar.gz
print(p.suffix)  #.gz
print(p.suffixes)  # ['.tar', '.gz']
print(p.stem)   # mysql.tar
print(p.with_name('mysql-5.tgz'))   #\mysqlinstall\mysql-5.tgz
print(p.with_suffix('.png'))   #\mysqlinstall\mysql.tar.png
p = Path('README')  #  README
print(p.with_suffix('.txt'))   #  README.txt
判断方法

is_dir() 是否是目录,目录存在返回True

is_file() 是否是普通文件,文件存在返回True

is_symlink() 是否是软链接

is_socket()是否是socket文件

is_block_device()是否是块设备

is_char_device() 是否是字符设备

is_absolute()是否是绝对路径

resolve()返回一个新的路径,这个新路径就是当前Path对象的绝对路径,如果是软链接则直接被解析

absolute() 获取绝对路径

exists()目录或文件是否存在

rmdir()删除空目录。没有提供判断目录为空的方法

touch(mode=0o666, exist_ok=True) 创建一个文件

as_uri() 将路径返回成URI,例如'file:///etc/passwd'

mkdir(mode=0o777, parents=False, exist_ok=False)

iterdir()迭代当前目录

匹配

match(pattern)

模式匹配,成功返回True。

Path('a/b.py').match('*.py') # True
Path('/a/b/c.py').match('b/*.py') # True
Path('/a/b/c.py').match('a/*.py') # False
Path('/a/b/c.py').match('a/*/*.py') # True
Path('/a/b/c.py').match('a/**/*.py') # True
Path('/a/b/c.py').match('**/*.py') # True

stat() 相当于stat命令 ,lstat() 同stat(),但如果是符号链接,则显示符号链接本身的文件信息

pathlib模块下的文件操作

Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)

使用的方法类似内建函数open,返回一个文件对象。

3.5增加的新函数

Path.read_bytes()

以'rb'读取路径对应文件,并返回二进制流。看源码

Path.read_text(encoding=None, errors=None)

以'rt'方式读取路径对应文件,返回文本。

Path.write_bytes(data)

以'wb'方式写入数据到路径对应文件。

Path.write_text(data, encoding=None, errors=None)

以'wt'方式写入字符串到路径对应文件。

p = Path('my_binary_file')
p.write_bytes(b'Binary file contents')
p.read_bytes() # b'Binary file contents'
 
p = Path('my_text_file')
p.write_text('Text file contents')
p.read_text() # 'Text file contents'
 
 
from pathlib import Path
p = Path('o:/test.py')
p.write_text('hello python')
print(p.read_text())
with p.open() as f:
    print(f.read(5))

csv文件

逗号分隔值Comma-Separated Values。

CSV 是一个被行分隔符、列分隔符划分成行和列的文本文件。

CSV 不指定字符编码。

行分隔符为\r\n,最后一行可以没有换行符

列分隔符常为逗号或者制表符。

每一行称为一条记录record

字段可以使用双引号括起来,也可以不使用。如果字段中出现了双引号、逗号、换行符必须使用双引号括起来。如果字段的值是双引号,使用两个双引号表示一个转义。

表头可选,和字段列对齐就行了。

手动生成csv文件

from pathlib import Path

p = Path('D:/tmp/test.csv')
parent = p.parent
if not parent.exists():
    parent.mkdir(parents=True,exist_ok =True) #exist_ok 用在python3.5之后,如果文件目录存在,True则压制异常。
csv_body = '''\
id,name,age,comment
1,zs,18,"I'm 18"
2,ls,20,"this is a ""test"" string."
3,ww,23,"你好

计算机
"
'''
p.write_text(csv_body)

csv模块
def reader(iterable, dialect='excel', *args, **kwargs)

返回一个reader对象,是一个行迭代器

默认使用excel方言,如下:

def writer(fileobj, dialect='excel', *args, **kwargs)

返回DictWriter实例,主要的方法有writerow,writerows。

import csv
 
p = Path('d://tmp/tesr.csv')
with open(str(p)) as f:
    reader = csv.reader(f)    #返回一个迭代对象
    print(next(reader))     #不回头
    print(next(reader))     
    for line in reader:
        print(line)
 
rows = [
    [4,'tom',22,'tom'],
    (5,'jerry',24,'jerry'),
    (6,'justin',22,'just\t"in'),
    "abcdefghi",
    ((1,),(2,))
]
row = rows[0]
 
with open(str(p), 'a',newline="") as f:  #newline为了不换行
    writer = csv.writer(f)
    writer.writerow(row)   #一次写一条
    writer.writerows(rows)    #将所有的一次写入

ini文件

一般作为配置文件。

ini文件:

[DEFAULT]
a = test
 
[mysql]
default-character-set=utf8
 
[mysqld]
datadir =/dbserver/data
port = 33060
character-set-server=utf8
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

中括号里面的部分称为section,译作节、区、段。

每一个section内,都是key=value形成的键值对,key称为option选项。

这里的DEFAULT是缺省section的名字,必须大写。

configparser模块

configparser模块的ConfigParser类就是用来操作。

可以将section当做key,section存储着键值对组成的字典,可以把ini配置文件当做一个嵌套的字典。默认使用的是有序字典。

read(filenames, encoding=None) 
#读取ini文件,可以是单个文件,也可以是文件列表。可以指定文件编码。
sections()  #返回section列表。缺省section不包括在内。 
add_section(section_name)   #增加一个section。 

has_section(section_name)   #判断section是否存在 
options(section)   #返回section的所有option,会追加缺省section的option 

has_option(section, option)  #判断section是否存在这个option
get(section, option, *, raw=False, vars=None[, fallback]) 

#从指定的段的选项上取值,如果找到返回,如果没有找到就去找DEFAULT段有没有。

getint(section, option, *, raw=False, vars=None[, fallback]) 
getfloat(section, option, *, raw=False, vars=None[, fallback]) 
getboolean(section, option, *, raw=False, vars=None[, fallback]) 
#上面3个方法和get一样,返回指定类型数据。

items(raw=False, vars=None) 
items(section, raw=False, vars=None) 
#没有section,则返回所有section名字及其对象;如果指定section,则返回这个指定的section的键值对组成二元组。

set(section, option, value) 
#section存在的情况下,写入option=value,要求option、value必须是字符串。

remove_section(section) 
#移除section及其所有option 

remove_option(section, option) 
#移除section下的option。

write(fileobject, space_around_delimiters=True) 
#将当前config的所有内容写入fileobject中,一般open函数使用w模式。

代码示例:

from configparser import ConfigParser
from pathlib import Path

filename = Path("d://tmp/mysql.ini")
newfilename = Path("d://tmp/mysql111.ini")

cfg = ConfigParser()
read_ok = cfg.read(str(filename))
print(read_ok)
print(cfg.sections())
print(cfg.has_section("mysql"))
print("-"*30)

for k,v in cfg.items():   #未指定section
    print(k,type(k))
    print(v,type(v))
    print(cfg.items(k))
    print("~~~~~~~~~~~~~~~~~~")
print("-"*30)


for k,v in cfg.items("mysqld"):  #指定section
    print(k,type(k))
    print(v,type(v))
    print("~~~~~~~~~~")

tmp = cfg.get("mysqld","port")
print(tmp, type(tmp))
print(cfg.get("mysqld", "a"))
print(cfg.get("mysqld", "python" , fallback= "linux"))  #按照类型,fallbac:给与缺省值

tmp = cfg.getint("mysqld", "port")
print(type(tmp), tmp)

cfg.add_section("test")
cfg.set("test","test1","1")
cfg.set("test","test2","2")
with open(newfilename,"w+",newline="") as f:
    cfg.write(f)

print(cfg.getint("test" , "test1"))

cfg.remove_option("test", "test1")
# cfg.remove_section("test")
# print("x" in cfg["test2"])
#字典操作
cfg["test3"] = {"c":"1000"}   #没有落地,在内存中修改

print("x" in cfg["test"])
print("c" in cfg["test3"])

# 其他内部方式
print(cfg._dict)  # 返回默认的字典类型,默认使用有序字典

for k, v in cfg._sections.items():
    print(k, v)

for k,v in cfg._sections['mysqld'].items():
    print(k,v)
#重新写入文件
with open(newfilename, 'w') as f:
    cfg.write(f)

序列化和反序列化

要设计一套协议,按照某种规则,把内存中数据保存到文件中,文件是一个字节序列,所以必须把数据转换成<font color=red >字节</font>序列,输出到文件。这就是序列化。反之,从文件的字节序列恢复到内存。就是反序列化。
  1. serialization:序列化
    将内存中对象存储下来,变成一个个字节 --> 二进制

  2. deseiralization:反序列化
    将文件中的一个个字节恢复成内存中对象 <--二进制

序列化保存到文件就是持久化,可以将数据序列化后持久化,或者网络传输;也可以将文件中或者网络中接收到的字节序列反序列化。

pickle库

python中的序列化,反序列化模块。

dumps 对象序列化为bytes对象 dump 对象序列化到文件对象,就是存入文件

loads 从bytes对象反序列化 load 对象反序列化,从文件读取数据

序列化的应用

一般来说,本地序列化的情况,应用较少。大多数场景都应用在网络传输中。

将数据序列化后通过网络传输到远程节点,远程服务器上的服务将接收到的数据反序列化后,就可以使用了。

但是,要注意一点,远程接收端,反序列化时必须有对应的数据类型,否则就会报错。尤其是自定义类,必须远程的有一致的定义。

现在,大多数项目,都不是单机的,也不是单服务的。需要通过网络将数据传送到其他节点上去,这就需要大量的序列化、反序列化过程。

但是,问题是,Python程序之间还可以都是用pickle解决序列化、反序列化,如果是跨平台、跨语言、跨协议pickle就不太适合了,就需要公共的协议。例如XML、Json、Protocol Buffer等。

不同的协议,效率不同、学习曲线不同,适用不同场景,要根据不同的情况分析选型。

Json
Json(JavaScript Object Notation,JS对象标记)是一种轻量级的数据交换格式。它基于 ECMAScript (w3c组织制定的JS规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。网址: http://json.org/

Json的数据类型

值:双引号引起来的字符串,数值,true和false,null,对象,数组,这些都是值

字符串:有正负,有整数,浮点数。

对象:无序的键值对的集合,格式{key:value...},key必须是一个字符串,需要双引号包围这个字符串,value可以是任意合法的值。

数组:有序的值的集合 格式[val1,,,,valn]

{
  "person": [
    {
      "name": "tom",
      "age": 18
    },
    {
      "name": "jerry",
      "age": 16
    }
  ],
  "total": 2
}

Json模块

Python支持少量内建数据类型到Json类型的转换

Python类型 Json类型
True true
False false
None null
str string
int integer
float float
list array
dict object

常用方法

Python类型 Json类型
dumps Json编码
dump Json编码并存入文件
loads Json解码
load Json解码,从文件读取数据

一般Json编码的数据很少落地,数据都是通过网络传输,传输的时候,要考虑压缩它,节省流量。本质来说它就是个文本,就是个字符串。

MessagePack

MessagePack是一个基于二进制高效的对象序列化类库,可用于跨语言通信。

它可以像JSON那样,在许多种语言之间交换结构对象。

兼容 json和pickle。

MessagePack简单易用,高效压缩,支持语言丰富。

所以,用它序列化也是一种很好的选择。

安装:$pip install msgpack-python

常用方法:

packb 序列化对象。提供了dumps来兼容pickle和json。

unpackb 反序列化对象。提供了loads来兼容。

pack 序列化对象保存到文件对象。提供了dump来兼容。

unpack 反序列化对象保存到文件对象。提供了load来兼容。

import pickle
import json
import msgpack

d = {"person":[{"name":"tom","age":18},{"name":"jerry","age":16}],"total":2}
j = json.dumps(d)
print(j, type(j), len(j)) # 请注意引号的变化
print(len(j.replace(' ',''))) # 72 bytes 注意这样替换的压缩是不对的
print("-"*30)
p = pickle.dumps(d)
print(p)
print(len(p))  # 101 bytes
print("-"*30)
m = msgpack.dumps(d)
print(m)
print(len(m)) # 48 bytes
print("-"*30)
u = msgpack.unpackb(m)
print(type(u), u)
u = msgpack.loads(m, encoding='utf-8')
print(type(u), u)
{"person": [{"name": "tom", "age": 18}, {"name": "jerry", "age": 16}], "total": 2} <class 'str'> 82
72
------------------------------
b'\x80\x03}q\x00(X\x06\x00\x00\x00personq\x01]q\x02(}q\x03(X\x04\x00\x00\x00nameq\x04X\x03\x00\x00\x00tomq\x05X\x03\x00\x00\x00ageq\x06K\x12u}q\x07(h\x04X\x05\x00\x00\x00jerryq\x08h\x06K\x10ueX\x05\x00\x00\x00totalq\tK\x02u.'
101
------------------------------
b'\x82\xa6person\x92\x82\xa4name\xa3tom\xa3age\x12\x82\xa4name\xa5jerry\xa3age\x10\xa5total\x02'
48
------------------------------
<class 'dict'> {b'person': [{b'name': b'tom', b'age': 18}, {b'name': b'jerry', b'age': 16}], b'total': 2}
<class 'dict'> {'person': [{'name': 'tom', 'age': 18}, {'name': 'jerry', 'age': 16}], 'total': 2}
上一篇下一篇

猜你喜欢

热点阅读