Python基础之文件操作
一:文件打开操作
def open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True): # known special case of open
打开一个文件,返回一个文件对象(流对象)和文件描述符。打开文件失败,则返回异常
f = open('test.txt') # file对象
# windows <_io.TextIOWrapper name='test' mode='r' encoding='cp936'
# linux <_io.TextIOWrapper name='test' mode='r' encoding='UTF-8'
print(f.read()) # 读取文件(不加参数会将文件中所有内容读取到内存中)
f.close() # 关闭文件
文件操作中,最常用的操作就是读和写。文件访问的模式有两种:文本模式和二进制模式。不同模式下,操作函数也不尽相同,表现的结果也不一样。
open的参数:
file:打开或者要创建的文件名。如不指定路径,默认就是当前路径。
mode模式:
描述字符 | 意义 |
---|---|
r | 缺省的,表示只读打开。使用write方法抛异常;文件不存在抛异常 |
w | 只写打开。如果读取则抛异常;文件不存在则创建文件;文件存在则清空文件内容再写入新内容 |
x | 创建并写入一个新文件。文件不存在则创建文件,并只写方式打开;文件存在则抛异常 |
a | 写入打开。如果文件存在,则追加;文件不存在则创建后只写方式打开追加内容 |
b | 二进制模式。字节流,将文件按照字节理解,与字符编码无关。二进制模式操作时,字节操作使用bytes类型 |
t | 缺省的,文本模式。字符流,将文件的字节按照某种字符编码理解,按照字符操作。open的默认mode就是rt |
+ | 读写打开一个文件。给原来只读,只写方式打开提供缺失的读或者写能力 |
文件指针(默认指定当前字节位置):
mode=r:指针起始在0
mode=a:指针起始在EOF
tell():显示指针当前位置
seek(offset[,whence]):移动文件指针位置。offset偏移多少字节,whence从哪里开始。
文本模式下:
whence 0缺省值,表示从头开始,offset只能正整数
whence 1表示从当前位置,offset只接受0
whence 2表示从EOF开始,offset只接受0
二:上下文管理
lst = []
for _ in range(2000):
lst.append(open('test.txt'))
print(len(lst))
# 如果linux系统没有做过优化,会抛下面的异常
# OSError: [Error 24] Too many open files: test.txt
ulimit -a查看系统所有的限制。其中open files就是打开文件数的限制,默认为1024。由于上面的代码没有将打开的文件关闭,避免系统资源浪费,将打开的文件全部关闭
for x in lst:
x.close()
如何处理异常?当出现异常的时间,拦截异常。但是,因为很多代码都可能出现OSError异常,还不好判断异常就是因为资源限制产生的。
f = open('test.txt')
try:
f.write('abc')
finally: # 使用finally可以保证打开的文件可以被关闭
f.close()
上下文管理:一种特殊的语法,交给解释器去释放文件对象。
with open('test.txt') as f:
result = f.read() # 默认以只读模式打开文件
print(result)
# raise ValueError()
print(f.closed) # True 测试f是否已关闭,True为关闭
上下文管理:
- 使用with…as关键字
- 上下文管理的语句块并不会开启新的作用域
- with语句块执行完的时候,会自动关闭文件对象
实例1:指定一个源文件,实现copy到目标目录。如把当前目录下的/tmp/test.txt拷贝为test.txt.copy
origin_file = 'test.txt'
dest_file = 'test.txt.copy'
with open(origin_file) as f1:
with open(dest_file, 'w') as f2:
content = f1.read()
f2.write(content)
# 注:不会拷贝文件的元信息,只会拷贝文件内容
实例2:有一个文件,对其进行单词统计,不区分大小写,并显示单词重复最多的10个单词
def word_count(file = 'test.txt'):
chars = '''~!@#$%^&*()_+{}[]|\\/=;:.'<>?'''
with open(file, encoding='utf-8') as f:
count = {}
for line in f:
words = line.split()
# print(words)
for k, v in zip(words, (1,)*len(words)):
k = k.strip(chars)
k = k.lower()
count[k] = count.get(k,0) + 1
lst = sorted(count.items(), key = lambda x: x[1], reverse=True)
# for i in range(10):
# print(str(lst[1]).strip("'()").replace("'", ""))
return lst
wc = word_count()
print(wc)
print(len(wc))
三:StringIO与BytesIO
3.1 StringIO
io模块中的类:
from io import StringIO
内存中,开辟一个文本模式的buffer,可以像文件对象一样操作它。当close方法被调用的时候,这个buffer会被释放
from io import StringIO
# 内存中构建
s = StringIO() # 像文件对象一样操作
print(s.readable(), s.writable(), s.seekable()) # 可读,可写,可seek
s.write("Life is short,\nyou need Python") # 这里要用双引号,单引号换行符不起作用
s.seek(0) # 移动指针到开头
print(s.readline())
print(s.getvalue()) # 无视指针,输出全部内容
s.close()
好处:一般来说,磁盘的操作比内存的操作要慢得多,内存足够的情况下,一般的优化思路是少落地,减少磁盘IO的过程,可以大大提高程序的运行效率。
3.2 BytesIO
io模块中的类:
from io import BytesIO
内存中,开辟一个二进制模式的buffer,可以像文件对象一样操作它。当close方法被调用时,这个buffer会被释放。
from io import BytesIO
s = BytesIO() # 像文件对象一样操作
print(s.readable(), s.writable(), s.seekable()) # 可读,可写,可seek
s.write(b'Life is short,\nyou need Python')
s.seek(0) # 移动指针到开头
print(s.readline().decode())
print(s.getvalue().decode()) # 无视指针,输出全部内容
s.close()
3.3 file-like对象
类文件对象,可以像文件对象一样操作。socket对象、输入输出对象(stdin, stdout)都是类文件对象
from sys import stdout
f = stdout
print(type(f))
f.write('Life is short, you need python')
# <class '_io.TextIOWrapper'>
# Life is short, you need python
四:文件修改操作
文件的修改操作要为以下五个步骤:
- 以读的模式打开原文件
- 以写的模式打开一个新文件
- 将原文件的内容读取到内存按照要求进行修改,形成新内容写入新文件
- 将原文件删除
- 将新文件生命成原文件
import os
with open('test.txt', encoding='utf-8') as f1:
with open('test_new.txt', 'w', encoding='utf-8') as f2:
for line in f1:
new_content = line.replace('you', 'your')
f2.write(new_content)
os.remove('test.txt')
os.renames('test_new.txt', 'test.txt')
五:shutil模块
上面所说的文件拷贝使用打开两个文件对象,源文件读取内容,写入目标文件中来完成拷贝过程。但是这样丢失了stat数据信息(权限等)。python提供了一个方便的库:shutil(高级文件操作)
5.1 copy
copyfileobj(fsrc,fdst[,length])
文件对象的复制,fsrc和fdst是open打开的文件对象,复制内容。fdst要求可写。length指定了buffer的大小。
copyfile(src, dst, *, follow_symlinks=True) # 不包含元数据,本质上调用的就是copyfileobj
copymode(src, dst, *, follow_symlinks=True) # 仅仅复制权限
copystat(src, dst, *, follow_symlinks=True) # 复制元数据,包含权限
copy(src, dst, *, follow_symlinks=True) # 拷贝文件内容,权限,部分元数据,不包括创建时间和修改时间,本质上调用copyfile与copymode
copy2(src, dst, *, follow_symlinks=True) # 比copy多个复制全部元数据,需要平台支持。本质上调用copyfile, copystate
copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,ignore_dangling_symlinks=False)
# 递归复制目录。默认使用copy2,也就是带更多的元数据复制
rmtree(path, ignore_errors=False, onerror=None) # 递归删除。慎用。它不是原子操作,有可能删除错误就会中断,已经删除的就删除了。
move(src, dst, copy_function=copy2) # 递归移动文件,目录到目标,返回目标。
shutil还有打包功能。生成tar并压缩。支持zip, gz, bz, xz。
六:json文件处理
json本质上就是一个字符串
6.1 字典和列表转json
import json
person = [
{
"username": "Bruce",
'age': 30
},
{
"username": '天龙八部',
"age": 32
},
]
json_str = json.dumps(person, ensure_ascii=False)
print(json_str, type(json_str))
# 没有ensure_ascii=False参数的输出
# [{"username": "Bruce", "age": 30}, {"username": "\u5929\u9f99\u516b\u90e8", "age": 32}] <class 'str'>
# 有ensure_ascii=False参数的输出
# [{"username": "Bruce", "age": 30}, {"username": "天龙八部", "age": 32}] <class 'str'>
因为json在dump时,只能存放ascii的字符,因此会将中文进行转义,此时可以使用ensure_ascii=False关闭这个特性。
在Python中只有基本数据类型才能转换成JSONS格式的字符串。即:int, floot, str, list, dict, tuple。
6.2 json数据dump到文件
json模块中除了dumps函数,还有一个dump函数,这个函数可以传入一个文件指针,直接将字符串dump到文件中。
import json
person = [
{
"username": "Bruce",
'age': 30
},
{
"username": '天龙八部',
"age": 32
},
]
with open('a.json', 'w', encoding = 'utf-8') as f:
json.dump(person, f, ensure_ascii=False)
6.3 将json字符串load成python对象
json_str = '[{"username": "Bruce", "age": 30}, {"username": "天龙八部", "age": 32}]'
ret = json.loads(json_str, encoding='utf-8')
print(ret, type(ret))
直接从文件中load:
with open('a.json', 'r', encoding='utf-8') as f:
person = json.load(f)
print(person, type(person))
七:csv数据
写入csv数据:
import csv
header = ['id', 'title', 'lecture', 'price']
rows = [(1,'Python','Andy',200),(2,'Java','Jackie',100),(3,'PHP','Mike','288')]
with open('data1.csv', 'w', encoding = 'utf8') as f:
writer = csv.writer(f)
writer.writerow(header)
writer.writerows(rows)
读CSV数据:
import csv
with open('data1.csv') as f:
reader = csv.reader(f)
for row in reader:
print(row)
# 每一行数据都是一个列表
import csv
with open('data1.csv') as f:
reader = csv.DictReader(f)
for row in reader:
print(row) # OrderedDict([('id', '1'), ('name', 'Tom'), ('score', '80')])
for k,v in row.items():
print(k,v)
八:excel文件处理
官方文档:
https://openpyxl.readthedocs.io/en/stable/
8.1 安装
pip install openpyxl
pip install Pillow
# 想要在文件中插入图片文件,需要安装pillow
8.2 简单使用
from openpyxl import Workbook
import datetime
import time
wb = Workbook() #创建文件对象
# grab the active worksheet
ws = wb.active #获取第一个sheet
# Data can be assigned directly to cells
ws['A1'] = 42 #写入数字
ws['B1'] = "你好"+"automation test" #写入中文(unicode中文也可)
# Rows can also be appended
ws.append([1, 2, 3]) #写入多个单元格
ws['A3'] = datetime.datetime.now() #写入一个当前时间
ws['A4'] =time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()) #写入一个自定义的时间格式
wb.save("e:\\sample.xlsx")
写入表格后的数据: