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

[toc]

python 文件的IO三

CSV文件

csv文件简介

逗号分隔值 Comma-Separated Values。
CSV是一个被行分隔符、列分隔符分成行和列的文本文件;
没有指定字符编码。
参看RFC4180

行分隔符为\r\n,最后一行可以没有换行符;
列分隔符常为逗号或者制表符;
每一行为一条记录record。

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

表头可选,和段列对就就可以了。

csv生成测试

s = """
1,tom,20
2,jerry,16,
"""
with open('./test.csv','w') as f:
    for line in s.splitlines():
        f.write(line + '\n')

from pathlib import Path

p = Path('./tmp/myscv.test.csv')
parent = p.parent
if not parent.exists():
    parent.mkdir(parents=True)
csv_body= '''
id,name,age,comment
1,zs,18,"I'm18"
2,ls,20,"This is a ""test"" string."
3,ww,23,"中国

国庆
"
'''
p.write_text(csv_body)

scv 模块

reader(csvfile,dialect=’excel’,**fmtparams)
返回DictReader的实例,是个行迭代器;
delimiter 列分隔符逗号
lineterminator 行分隔符\r\n
quotechar 字段的引用符号,缺少为”, 双引号

双引号的处理:
doublequote比引号的处理,默认为True。如果和quoterchar为同一个,True则使用2个双引号表示;
False表示使用转义字符将作为双引号的前缀;
escapechar一个转义字符,默认为None;
quoting指定双引号的规则。QUOTE_ALL所有字段;QUOTE_MINIMAL特殊符字段;
QUOtE_NONNUMERIC非数字字段; QUOTE_NONE都不使用引号。

write(csvfile,dialect=’excel’,**fmtparams)
返回DictWriter的实例。
主要方法有writerow、writerows。

from pathlib import Path
import csv
path = './csv/test.csv'
p = Path(path)
if not p.parent.exists():
    p.parent.mkdir(parents=True)

line1 = [1,'tom',20,'']
line2 = [2,'tom',20,'']
line3 = [line1,line2]

s = """\
1,tom,20,
2,jerry,16,
3,,,
"""
with open(path,'w') as f:
    writer = csv.writer(f)
    writer.writerow(line1)
    writer.writerow(line2)
    writer.writerows(line3)
with open(path) as f :
    reader = csv.reader(f)
    for line in reader:
        if line:
            print(line)

ini 文件处理

作为配置文件,ini文件格式的很流行。

[DEFAULT]
a = test

[mysql]
default-character-set = utf8

[mysqld]
datadir = /dbserver/data
prot = 33060
character-set-sever=utf8
sql_mode=NO_ENGIN_SUBSITUTION,STRICT_TRANS_TABLES

configparser

configparser模块的ConfigParser类就是用来操作
read(filenames, endcoding=None)
读取ini文件,可以是单个文件,也可以是文件列表。可以指定文件编码;
sections()返回section列表。缺省section不包括在内;
add_section(section_name)增加一个section;
has_section(section_name)判断section是否存在;
opentions(section)返回section的所有option;

get(section,option,*,raw=False,vars=None[,fallback])

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

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

items(raw=False,vars=None)
items(section,raw=Fase,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

cfg = ConfigParser()
cfg.read('./mysql.ini')
print(cfg.sections())

for section in cfg.sections():
    # for opt in cfg.options(section):
    #     print(section,opt)

    for k,v in cfg.items(section):
        print(k,v)
if not cfg.has_section('test'):
    cfg.add_section('test')
cfg.set('test','test1','123')
cfg.set('test','test2','abc')

with open('./mysql.ini','w') as f:
    cfg.write(f)

写入后如下

[DEFAULT]
a = test

[mysql]
default-character-set = utf8

[mysqld]
datadir = /dbserver/data
prot = 33060
character-set-sever = utf8
sql_mode = NO_ENGIN_SUBSITUTION,STRICT_TRANS_TABLES

[test]
test1 = 123
test2 = abc

获取

from configparser import ConfigParser

cfg = ConfigParser()
cfg.read('./mysql.ini')
print(cfg.sections())

for section in cfg.sections():
    # for opt in cfg.options(section):
    #     print(section,opt)

    for k,v in cfg.items(section):
        print(k,v)
# if not cfg.has_section('test'):
#     cfg.add_section('test')
# cfg.set('test','test1','123')
# cfg.set('test','test2','abc')
#
# with open('./mysql.ini','w') as f:
#     cfg.write(f)

print('!'*50)
a = cfg.get('test','test1')
print(a,type(a))
b = cfg.getint('test','test1')
print(b+1,type(b))
# 输出 
['mysql', 'mysqld', 'test']
a test
default-character-set utf8
a test
datadir /dbserver/data
prot 33060
character-set-sever utf8
sql_mode NO_ENGIN_SUBSITUTION,STRICT_TRANS_TABLES
a test
test1 123
test2 abc
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
123 <class 'str'>
124 <class 'int'>

序列化和反序列化

为什么要序列化

内存中的字典,链表如何保存到一个文件中 ?
如果是自己定义的类的实例,如何保存到一个文件中?
如何从文件中读取数据,并让它们的内存中再次变成自己对应的类的实例 ?

要设计一套协议,按照某种规则,把内存中数据保存到文件中。文件是一个字节序列,所以必须把数据转换成字节序列,输出到文件。这就是序列化。反之,从文件的字节序列恢复到内存,就是反序列化。

定义

serialization序列化
将内存中的对象存储下来,把它变成一个个字节。 -> 二进制

deserialization反序列化
将文件中的一个个字恢复成内存中对象。 <- 二进制

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

Python提供了pickle库。

pickle库

Python中的序列化、反序列化模块;
dumps对象序列化;
dump对象列化到文件对象,就是存入文件;
loads 对象反序列化;
load 对象反序列化,从文件读取数据

from pathlib import Path
import pickle
lst = 'a b c'.split(' ')
d = dict(zip('abc',range(3)))
class AA:
    def show(self):
        a = 'abcdefghijklmn1230456789'
        print(a)

aa = AA()
with open('./bin','wb') as f:
    # pickle.dump(lst,f)
    # pickle.dump(d,f)
    pickle.dump(aa,f)

with open('./bin','rb') as f:
    tmp = pickle.load(f)
    print(type(tmp))
    tt = tmp
    tmp.show()

文件序列化和反序列化

import pickle
class AA:
    def show(self):
        print('123')
with open('bin','rb') as f:
    t = pickle.load(f)
    t.show()

对象序列化

import pickle
class AA:
    tttt = 'ABC'
    def show(self):
        print('abc')
a1 = AA()
sr = pickle.dumps(a1)
print('sr={}'.format(sr)) # AA

a2 = pickle.loads(sr)
print(a2.tttt)
a2.show()

上面的例子中,其实就保存了一个类名,因为所有的东西都是类定义的东西,是不变的,所以只序列化一个AA类名。反序列化的时候找到类可以恢复一个对象。

import pickle

class AAA:
    def __ini__(self):
        self.tttt = 'abc'
b1 = AAA()

sr = pickle.dumps(bs)
print('sr={}'.format(sr))
b2 = pickle.loads(sr)
print(b2.tttt)

可以看出,保存了AAA、ttt 和abc,因为每一个对象每次都是变化的。但是反序列化的时候要找到AAA类的定义,才能成功。否则会抛出异常;
这样可以理解,反序列化的时候,类是模子,二进制序列就是铁水。

应用

本地序列化的情况应用较少;
一般来说大多场景都应用在网络中。将数据序列化通过网络传输到远程节点,远程服务器上的接收到数据反序列化后,就可以使用了;
但是,要注意一点,远程接收端,反序列化时必须有对应的数据类型,否则就会报错。尤其是自定义类型,必须远程得有。

实验

class AA:
    def __init__(self):
        self.tttt = 'abc'
aaa = AA()
sr = pickle.dumps(aaa)
print(len(sr))

file = './ser'
with open(file, 'wb') as f:
    pickle.dump(aaa,f)

将生产的充列化文件发往其它节点,运行:

with open('ser','rb') as f:
    a = pickle.load(f) 

增加类定义即可解决
现在大多数项目,都不是单机的,也不是单服务的,需要通过网络数据传送到其它节点上去,这就需要大量的序列化,反序列化。

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

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

Json

Json(Javascript Object Notation JS标记对象), 是一种轻量级的数据交互格式。它基于ECMASciprt(w3c制定的JS规范)的一个子集。采用完全独立的编程语言文本格式来存储和表示数据;

Json 数据类型


双引号引起来的字符串,数值。 True和False,null,对象,数组这些都是值;

字符串
由双引号包围起来的任意符号组合,可以有转义符;

数值
有负数、有整数、有浮点数;

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

数组

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

{
  "persion": [
  {
    "n": "tom",
    "a": 18
  },
  {
    "n": "jerry",
    "age": 16
  },
  {
    "name": "ben",
    "age": 24
  }
  ],
  "total":2

Python 与 Json

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

Python类型 Json类型
True True
False false
None null
str string
int integer
float float

常用方法

dumps json编码

dumps json编码并存入文件
loads json解码
load json解码,从文件读取数据

import json
d = {'name': 'Tom','age':'20','interset':['music','movie'],'e':False, 'f':None}
j = json.dumps(d)
print(j)

d1 = json.loads(j)
print(d1)
# 输出
{"name": "Tom", "age": "20", "interset": ["music", "movie"], "e": false, "f": null}
{'name': 'Tom', 'age': '20', 'interset': ['music', 'movie'], 'e': False, 'f': None}

一般json编码的数据很少落地,数据都是通过网络传输。传输的时候,要考虑压缩它;
本质上来说 它就是个文本,就是个字符串;
json很简单,几乎语言编程都支持json,所以应用范围十分广泛。

MesagePack

MessagePack 是一个基于二进制高效的对象序列化类库,可用于跨语言通信;
它可以像Json那样,在许多语言之间交换数据对象;
但是它比Json更快速也更轻巧;
支持Python、Ruby、 Java、 C/C++ 等众多语言。宣称比Google Protocol Buffer 还要快4倍;
兼容Json和pickle。

安装

pip install msgpack-python

常用方法

packb 序列化对象。提供了dumps来兼容pickle和json;
unpackb 反充列化对象。提供了loads 来兼容;
pack序列化对象保存到文件对象,提供了dump 来兼容;
unpack 反序列化对象保存到文件对象,提供laod来兼容。

import msgpack
import json
js = '{"person":[{"name":"tom","age":10},{"name":"jerry","age":16},{"name":"ben","age":24}],"total":2}'
d = json.loads(js)
print(d)
msg = msgpack.packb(d)
print(len(msg))
print(msg)
bts = msgpack.unpackb(msg)
print(type(bts))
print(bts)
# 输出
{'person': [{'name': 'tom', 'age': 10}, {'name': 'jerry', 'age': 16}, {'name': 'ben', 'age': 24}], 'total': 2}
63
b'\x82\xa6person\x93\x82\xa4name\xa3tom\xa3age\n\x82\xa4name\xa5jerry\xa3age\x10\x82\xa4name\xa3ben\xa3age\x18\xa5total\x02'
<class 'dict'>
{b'person': [{b'name': b'tom', b'age': 10}, {b'name': b'jerry', b'age': 16}, {b'name': b'ben', b'age': 24}], b'total': 2}

MessagePack简单易,高效压缩,支持语言丰富;
所以,用它序列化也是一种很好的选择;

格言

做一个出乎意料的决定:”好汉打脱呀和血吞”。这是一句湖南的土话,好汉被打家打掉了牙,不要吐出来让别人看到,要咽到肚子里,继续战斗。他不再和长沙官场纠缠争辩,而是卷起铺盖,带着自己募来的湘军。前往僻静的衡阳。全省官员都瞧不起我,我不争一日之短长。等到我在衡阳练成一支劲旅,打几个胜仗给你们看看,那时自会分出高。这才是挽回面子的最好办法–即逆境时不暴露也不让人看到,冷嘲热讽时也不争一日之短长,不争乃大争

评论