前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【AICAMP —— Python】入门系列!(4. 文件与存储)

【AICAMP —— Python】入门系列!(4. 文件与存储)

作者头像
灿视学长
发布2021-05-28 11:16:35
7020
发布2021-05-28 11:16:35
举报
文章被收录于专栏:灿视学长

文件与存储

1. 文件

  • 读写文件是最常见的IO操作。Python内置了读写文件的函数
  • 读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),再通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。
  1. 读文件

在我们的实际项目中,我们通常会有两个txt文件,一个是train.txt一个是test.txt,我们会读取这两个txt文件的内容,来找到训练数据以及测试数据。

python读文件,如下代码:

代码语言:javascript
复制
>>> f = open('C:/Users/DELL/Desktop/test.txt', 'r')

标示符'r'表示读,这样,我们就成功地打开了一个文件。如果文件不存在,open()函数就会抛出一个IOError的错误,并且给出错误码和详细的信息告诉你文件不存在:

代码语言:javascript
复制
>>> f=open('C:/Users/DELL/Desktop/test.txt', 'r')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'C:/Users/DELL/Desktop/test.txt'

若文件正常打开,再调用read()方法时可以一次性读取文件中的全部内容。python会把内容读到内存中,并且用一个str对象表示。

代码语言:javascript
复制
>>> f.read()
'Hello, world!'

当使用完这个文件之后,我们需要调用.close方法来关闭文件。

代码语言:javascript
复制
>>> f.close()

由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally来实现, 异常管理我们后面会说到。

代码语言:javascript
复制
try:
    f = open('./demo.txt', 'r')
    print(f.read())
finally:
    if f:
        f.close()

我们也可以使用with来进行代码简化,使用with时,python会自动帮我们执行close()方法。当使用with的时候,就可以将代码写的更加的整洁,好看!nice!

代码语言:javascript
复制
with open('demo.txt', r) as f:
    print(f.read())

但是read会一次性读取文件的全部内容,如果文件比较大,那么内存就会爆,因此我们可以选择readline()方法来每次读取一行内容。当如果调用readlines()时,一次性会读取所有内容并且返回list,因此我们需要决定怎么调用。

代码语言:javascript
复制
for line in f.readlines():
    print(line.strip()) # 把末尾的'\n'删掉

当有时候我们读取文件的时候,会遇到编码问题。这时候,会报出UnicodeDecodeError的错误,主要还是因为文件中夹杂了一些非法编码的字节。这时候,我们使用open的时候还需要接收一个errors参数,表示如果遇到编码错误后如何处理。其中,最简单的方式就是直接忽略。其中对于编码问题,windows遇到这些问题,确实很头疼,要是unix或者macos这些系统,就会好很多。

代码语言:javascript
复制
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
#其中gbk表示中文编码
  1. 写文件 写文件和读文件是一样的,唯一区别是调用open()函数时,传入标识符'w'或者'wb'表示写文本文件或写二进制文件:
代码语言:javascript
复制
>>> f = open('/Users/michael/test.txt', 'w')
>>> f.write('Hello, world!')
>>> f.close()

你可以反复调用write()来写入文件,但是务必要调用f.close()来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用with语句来得保险:

代码语言:javascript
复制
with open('/Users/michael/test.txt', 'w') as f:
    f.write('Hello, world!')

以'w'模式写入文件时,如果文件已存在,会直接覆盖(相当于删掉后新写入一个文件)。如果我们希望追加到文件末尾怎么办?我们可以传入'a'以追加(append)模式写入。

2.操作文件与目录

python的目录与文件操作常用的模块是os模块。 os模块的功能十分强大,如控制环境变量,切换目录,展示当前所有文件,删除并且新建文件与目录都是可以用os模块。

代码语言:javascript
复制
>>> import os
>>> os.environ
environ({'LC_PAPER': 'zh_CN.UTF-8','PATH': ...})

os的其它常用用法:

代码语言:javascript
复制
#查看当前文件夹中的所有文件
>>> os.listdir('./')
['.anaconda', '.astropy', '.cache', '.cisco', '.conda', '.condarc', '.dnx', '.git', '.gitconfig', '.ipython', '.jupyter', '.labelImgSettings.pkl', '.labelmerc', '.matplotlib', '3D Objects', 'ansel', 'apex', 'AppData', 'Application Data', 'Contacts', 'Cookies', 'Desktop', 'Documents', 'Downloads']
#查看当前目录的绝对路径
>>> os.path.abspath('.')
'C:\\Users\\DELL'
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
>>> os.path.join('./', 'demo')
'./demo'
# 然后创建一个目录:
>>> os.mkdir('./testdir')
# 删掉一个目录:
>>> os.rmdir('./testdir')
#拆分路径
>>> os.path.splitext('/path/to/file.txt')
('/path/to/file', '.txt')
#得到文件扩展名
>>> os.path.splitext('/path/to/file.txt')
('/path/to/file', '.txt')
# 对文件重命名:
>>> os.rename('test.txt', 'test.py')
# 删掉文件:
>>> os.remove('test.py')

尤其注意的是os不能进行复制文件,我们可以使用shutil提供的copyfile()函数来进行。

代码语言:javascript
复制
>>> import shutil
>>> shutil.copyfile('a.txt', 'b.txt')

在接下来的项目中,我们会使用glob模块来进行文件的匹配。如,我们需要得到某个文件夹下的所有png文件,我们可以:

代码语言:javascript
复制
files = glob.glob('*.png')

其中的*.png表示的是一个正则表达式,为匹配所有以png的图片,并返回一个list

3.序列化

任何一种语言,在程序运行过程中,所有的变量都是存储在内存之中,比如,定义一个list

代码语言:javascript
复制
>>> a = {'name':'james', 'age':18}

我们前面学到过list是可变类型,因此,我们可以在程序运行过程中修改里面的变量。但是,一旦程序结束,变量所占用的内存就会释放。我们把变量保存到磁盘中的过程称之为序列化,相对应从硬盘中加载变量到内存中的过程称之为反序列化。

同样,python中有相对应的模块:

  • pickle
代码语言:javascript
复制
>>> import pickle
>>> d = dict(name='james', age=34, score=99)
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x05\x00\x00\x00jamesq\x02X\x03\x00\x00\x00ageq\x03K"X\x05\x00\x00\x00scoreq\x04Kcu.'

pickle.dumps()方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件。或者用另一个方法pickle.dump()直接把对象序列化后写入文件之中。

代码语言:javascript
复制
>>> f = open('demo.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()

当我们从文件中加载内容至磁盘时,我们可以先把内容读到一个bytes,之后再用pickle.loads()方法反序列化出对象。

代码语言:javascript
复制
>>> f = open('demo.txt', 'rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age': 34, 'score': 99, 'name': 'james'}
  • Json
  1. 字典等格式数据

如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。其中,JSON和Python内置的数据类型对应如下:

姓名

年龄

{}

dict

[]

list

'string'

str

1.23

float或int

true/false

True/False

'numm'

None

Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。我们先看看如何把Python对象变成一个JSON:

代码语言:javascript
复制
>>> import json
>>> d = dict(name='james', age=34, score=99)
>>> json.dumps(d)
'{"age": 34, "score": 99, "name": "james"}'
>>> with open('./demo.json', 'w',encoding='utf-8') as json_file:
        json.dump(d,json_file,ensure_ascii=False)

dumps()方法返回一个str,内容就是标准的JSON。类似的,dump()方法可以直接把JSON写入一个文件中。

要把JSON反序列化为Python对象,用loads()或者对应的load()方法,前者把JSON的字符串反序列化,后者从文件中读取字符串并反序列化:

代码语言:javascript
复制
model={} #存放读取的数据
with open("./demo.json",'r',encoding='utf-8') as json_file:
    model=json.load(json_file)
  1. 实例对象等数据

一般我们常常会将dict等格式的数据存放成一个JSON的文件,不过我们经常也会将实例对象进行序列化,代码如下:

代码语言:javascript
复制
import json

class Student(object):
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score
s = Student('curry', 31, 88)
def student2dict(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }
代码语言:javascript
复制
>>> print(json.dumps(s, default=student2dict))
{"age": 31, "name": "curry", "score": 88}

其中,如果没有student2dict的方法时,无法进行序列化。dumps需要student2dict的方法来将Student的实例变成一个JSON对象。这样的话,Student实例就先被student2dict函数转变成一个dict格式,再被顺利序列化成JSON

那如果下次在遇到一个类无法进行序列化成一个JSON,是不是我们都需要写一个属性转成字典的函数呢?不是的,对于Python的class而言,具有__dict__属性,这样就可以存储实例变量。

代码语言:javascript
复制
>>> print(json.dumps(s, default=lambda obj: obj.__dict__)) #其中obj为实例化的对象

同样,我们需要将反序列化成一个对象的形式,loads方法首先会转换成一个dict对象,我们需要写一个dict转实例的方法,传到object_hook函数中去。

代码语言:javascript
复制
def dict2student(d):
    return Student(d['name'], d['age'], d['score'])

运行结果如下:

代码语言:javascript
复制
>>> json_str = '{"age": 31, "name": "curry", "score": 88}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x11cd3c190>
  1. XML文件解析 XML文件也是我们在实际中经常会使用的一种存储格式,比如labelimg保存的文件就是xml格式的。如下图为voc2007数据集中的一个示例,其中包含了很多的标签:

voc2007数据集实例

对于xml的解析,有三种方法,ExpatSAXDOM以及ElementTree。而这里主要介绍以ElementTree元素树的形式来进行xml的解析。

ElementTree生来就是为了处理XML,它在Python标准库中有两种实现:一种是纯Python实现的,如xml.etree.ElementTree,另一种是速度快一点的xml.etree.cElementTree。注意:尽量使用C语言实现的那种,因为它速度更快,而且消耗的内存更少。

因此,在代码中:

代码语言:javascript
复制
try:
    import xml.etree.cElementTree as ET #优先导入
except ImportError:
    import xml.etree.ElementTree as ET  

其中,常用的方法为:

  • 当要获取属性值的时候,用attrib方法。
  • 当要获取节点值的时候,用text方法。
  • 当要获取节点名时,用tag方法。

我们以解析VOC2007的数据集来举例子:

代码语言:javascript
复制
import numpy as np
import xml.etree.ElementTree as ET

CLASSES = ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car',
               'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike',
               'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor')
index_map = dict(zip(CLASSES, range(len(CLASSES))))
print(index_map)

输出:

代码语言:javascript
复制
{'aeroplane': 0, 'bicycle': 1, 'bird': 2, 'boat': 3, 'bottle': 4, 'bus': 5, 'car': 6, 'cat': 7, 'chair': 8, 'cow': 9, 'diningtable': 10, 'dog': 11, 'horse': 12, 'motorbike': 13, 'person': 14, 'pottedplant': 15, 'sheep': 16, 'sofa': 17, 'train': 18, 'tvmonitor': 19}

对xml的文件进行解析,得到对应的标注标签:

代码语言:javascript
复制
label = []
for obj in root.iter('object'):
    difficult = int(obj.find('difficult').text)
    cls_name = obj.find('name').text.strip().lower() #。text表示得到这个节点的值,strip()表示去掉空格,lower()为转小写
    if cls_name not in CLASSES:
        continue
    cls_id = index_map[cls_name]                     #得到对应的id

    xml_box = obj.find('bndbox')                     #查训bndbox这个节点的信息
    xmin = (float(xml_box.find('xmin').text) - 1)
    ymin = (float(xml_box.find('ymin').text) - 1)
    xmax = (float(xml_box.find('xmax').text) - 1)
    ymax = (float(xml_box.find('ymax').text) - 1)
    label.append([xmin, ymin, xmax, ymax, cls_id])

针对得到的标签,可以使用opencv或者pillow来进行标注可视化。

代码语言:javascript
复制
from PIL import Image, ImageDraw
img = Image.open(r'F:\working\dataset\VOCdevkit\VOC2007\JPEGImages\000002.jpg')
draw = ImageDraw.Draw(img)
for boxes in label:
    xmin = int(boxes[0])
    ymin = int(boxes[1])
    xmax = int(boxes[2])
    ymax = int(boxes[3])
    draw.rectangle([xmin, ymin, xmax, ymax], outline=(255, 0, 0))
    draw.text([xmin, ymin], CLASSES[boxes[4]], (255, 0, 0))
img.show()

最终,我们可视化下标注的位置以及类别信息。 特别提醒,在实际项目中,我们需要每一次check好自己的数据!不然,坑可大了!

最终,可视化的结果如图所示:

4. 关于我

欢迎加我微信,每天16个小时在线
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 灿视学长 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文件与存储
    • 1. 文件
      • 2.操作文件与目录
        • 3.序列化
          • 4. 关于我
          相关产品与服务
          文件存储
          文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档