任何语言都离不开对文件的操作,Python语言是如何来操作和管理文件的。掌握文件的基本概念、基本操作,了解文件属性、 linux 系统的文件管理机制及os模块对文件和目录的处理等相关内容。
- [√]慕课网Meshare_huang老师:Python文件处理
文件简介
Python的文件处理:
初学者:推荐使用虚拟机VMwareStation + Deepin
必备前置条件知识储备:
- Linux系统知识
- Linux下Python开发环境
- 掌握Python基础知识
Python文件概念:
文件: Python中文件是对象依赖我们的具体系统。
Linux文件:一切设备都可以看成文件
- 例如:磁盘文件,管道,网络Socket,外设等。
文件属性:用户,读,写,执行权限;
在Linux控制台输入命令:
#cd进一个空目录
cd Desktop/
#创建一个文件夹
mkdir PythonFile
cd PythonFile
touch hello.py
vim hello.py
mark
使用i进入insert模式进行插入代码。
插入完成后使用esc退出编辑模式,输入:wq进行保存。
mark
mark
通过ls -l命令可以看到文件的一些基本属性:
- 第一个横杠代表是一个文件,还是一个目录。
-代表是一个文件,d代表是一个目录
如下图1为一个目录。
mark
mark
-
第一组
rw-代表当前用户对于当前文件有读有写没有执行权限。(-代表没有权限,x为可执行) -
第二组
r--:表示当前用户组对该文件有读r没有写w的权限,没有执行权限x -
第三组
r--:表示其他用户对该文件有读r权限,但没有写w和执行x权限 -
第一个
mtianyan代表文件的拥有者是mtianyan -
第二个
mtianyan代表这个文件所属的用户组是mtianyan -
21代表文件的大小是21字节后面的时间是文件的创建时间。后面是文件名。
mark
可以看到我们最为当前用户现在没有该文件执行权限。
使用chmod为文件增加权限说明:
- 如果给所有人添加可执行权限:
chmod a+x 文件名; - 如果给文件所有者添加可执行权限:
chmod u+x 文件名; - 如果给所在组添加可执行权限:
chmod g+x 文件名; - 如果给所在组以外的人添加可执行权限:
chmod o+x 文件名;
mark
可以看到我们通过chmod +x hello.py为当前用户,用户组,其他用户都添加了执行权限
Linux下首行一定要加这句话:!/usr/bin/Python,!/usr/bin/Python是告诉操作系统执行这个脚本的时候,调用/usr/bin下的python解释器。
mark
打印出了Helloworld好像有点问题。以后对Linux熟了再回来看。
mark
python命令是可以正常执行的。
文件基础操作
主要讲解文件打开及文件不同的打开方式、使用read,readlines及迭代器访问文件、文件写入方式及写缓存处理、文件关闭及由不关闭导致的问题、文件指针及文件指针操作
文件打开方式
open()
open(name [,mode[buf]])
-
name: 文件路径 -
mode: 打开方式 -
buf: 缓冲buffering大小
mode方式有:只读,只写。读写。
read()
read([size])
读取文件(读取size个字节,默认读取全部)
- 如果文件长度大于size,我们就读size个长度。
- 如果文件长度小于size,我们就把文件读完。
- 如果没有设置size,默认全部读完。
readline()
readline([size])
读取一行
readlines()
readlines([size])
读取完文件(看到后面你就知道并不是),返回每一行所组成的列表。我们就可以使用列表访问的方式,使用文件。
弊端:一次性读完,占用很大内存。
write()
write(str)
将字符串写入文件
writelines()
writelines(sequence_of_strings) : 写多行到文件
sequence_of_strings是一个由字符型组成的列表。一次性写入多行。
输入ipython进入ipython环境
mark
type(f)可以看到打开的文件类型就是一个文件对象。
mark
拥有的方法可以使用f. + Tab自动补齐
mark
Error:File not open for writing
这里因为我们使用的是默认无参数打开,此时我们并不拥有写权限。
Python文件打开方式
| mode | 说明 | 说明 |
|---|---|---|
| 'r' | 只读方式打开 | 文件必须存在 |
| 'w' | 只写方式打开 | 文件不存在创建文件;存在则清空内容 |
| 'a' | 追加方式打开 | 文件不存在创建文件 |
| 'r+'/'W+' | 读写方式打开 | |
| 'a+' | 追加和读写方式打开 |
'rb','wb','ab','rb+','wb+','ab+':都是采用二进制方式打开。
练习'r' 'w' 'a' 'r+'
mark
-
不带参数的
open是用只读方式打开的,所以文件不存在会报错。
mark
- 请注意关闭文件
f.close()否则cat时文件内容会为空。
mark
- 当我们再次打开文件使用
w方式。已存在的文件会被清空才打开。
mark
- 我们以
a方式打开此时我们可以对于文件进行追加操作,原有内容并没有被清空
新增内容被添加到了最后。
mark
- 当我们以
r+方式打开,我们新增的内容会从文件开始替换原有内容的一部分。
mark
- 当我们以
w+方式打开,文件可以被读写。但是文件内容被清空。
mark
- 此时新文件可以被读写。
使用二进制方式打开的使用方法与上面介绍的基本一致。
意义在于: 比如读取一张图片的EXIF信息读取图片的长宽拍照日期
如果我们以普通方式打开是以文本方式打开,不能读取到正确的内容。
当我们以二进制打开我们就可以读取到正确的EXIF信息。
学习了打开文件,读取文件,写入文件。文件打开方式的不同。
文件读取方式
read()
read([size])
读取文件(读取size个字节,默认读取全部)
- 如果文件长度大于size,我们就读size个长度。
- 如果文件长度小于size,我们就把文件读完。
- 如果没有设置size,默认全部读完。
readline()
readline([size])
读取一行
示例操作
首先使用touch mtianyan.txt 和 vi mtianyan.txt
创建并写入内容:
blog.mtianyan.cn
blog.mtianyan.cn
blog.mtianyan.cn
mark
可以看出
readline(size):
if len(line) > size:
return size
if len(line) < size:
return len(line)
readlines()
readlines([size])
读取完文件(整数个buffer左右字节),返回每一行所组成的列表。我们就可以使用列表访问的方式,使用文件。
弊端:一次性读完,占用很大内存。
示例操作:
mark
虽然设置了size为1,但是还是把所有都读出来了。
mark
输入help(f.readlines)查看定义:重复的调用readline()然后返回一个读到的行所组成的列表。
读取文件的大小:
- 如果
size小于缓冲区的大小,则读取缓冲区大小的数据 - 如果
size大于缓冲区的大小,读取size大小的数据,但不是完全的等于size的大小,一般读取比size大的整行的数据.
这个设置是python解释器内部的一个变量。
import io
io.DEFAULT_BUFFER_SIZE
mark
设置参数小于8192每次能读取8192个字节。
- 设置参数
大于一个8192小于2*8192=16384。则读取两个buffer值. -
BUFFER都是一整个一整个读
Linux命令使用
使用vi打开文件。y是复制当前行,yy是复制光标所在的一整行.
如果复制了一行,则p是在光标所在行的下一行粘贴,10000p就是粘贴10000次
-
命令
yy,10000p复制10000行 .然后:set nu显示行数 -
gg:- 跳转到文件头
:1或gg - 跳转到
文件末尾行数+gg:$Shift+g - 跳转到指定行,例跳转到123行:
123gg或:123.
- 跳转到文件头
使用ctrl+z可将程序切换到后台。用fg可将后台程序切换到前台。
mark
可以看到此时的文件以及有十几万个字节了
mark
可以看出这时候不再是一次性把文件读取完了,而是读取了482行。
170051/100003行,每行17个字节。
mark
这时我们的size大于了缓冲区的大小8192。因此我们的size到了8192和16384
之间。
重点:系统数据是按着整个buffer,整个buffer来读的。此时系统会读取差不多两个buffer的数据。也就是963*17 =16371 。
待探究
iter: 使用迭代器读取文件
mark
迭代器并不是把我们的文件全部存入了内存中。而是我们每使用一次next方法就会自动读取下一行。所以能在不消耗大量内存的情况下进行读取整个文件。
文件写入与写缓存
write()
write(str)
将字符串写入文件
writelines()
writelines(sequence_of_strings) : 写多行到文件
sequence_of_strings参数是一个可迭代对象:
- 可以是字符串
- 可以是字符串所组成的元组。
- 可以是一个字符串所组成的迭代器。
- 也可是一个字符串组成的列表
实际操作
mark
In [37]: cat test.txt
test write
In [38]: f = open("mtianyan.txt",'w')
In [39]: f.writelines('123456')
In [40]: f.writelines((1,2,3))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-40-0a3726375675> in <module>()
----> 1 f.writelines((1,2,3))
TypeError: writelines() argument must be a sequence of strings
写入一个元组会报错 :提示必须是由字符串组成的序列。
In [41]: f.writelines(('1','2','3'))
In [42]: f.writelines(['1','2','3'])
In [43]: f.close()
In [44]: cat test.txt
test write
In [45]: cat mtianyan.txt
123456123123
可以看到我们都可以正常的写入由字符串组成的列表。
In [46]: f = open("mtianyan.txt",'w')
In [47]: f.write("11111")
In [48]: cat mtianyan.txt
In [49]:
文件内容为空,因为我们没有使用f.close()将文件关闭。
Linux系统有一种写缓存机制。如果我们不主动调用f.close()或 flush
他是不会写到磁盘文件的。
mark
写文件的过程中Python解释器进行系统调用。Linux系统内核将文件写入系统文件缓冲区。
当我们调用f.close()时,系统会更新文件到硬盘。
Python写磁盘的缓存机制
- 主动调用
closed()或flush方法。写缓存同步到磁盘 - 写入数据量大于或等于写缓存,写缓存同步到磁盘。(此时系统写缓存又清空,然后就又继续缓存直到
close())
实际操作
In [62]: f = open("mtianyan.txt",'w')
In [63]: f.write('test write')
In [64]: cat mtianyan.txt
In [65]: f.flush()
In [66]: cat mtianyan.txt
test write
进行flush()操作时内容更新。
mark
mark
说明我的Linux的缓冲区就是这么大147456
当我们执行close文件会变大。因为全部写进去了。
文件关闭
- 将写缓存同步到磁盘;
- linux系统中每个进程打开的文件个数是有限的;
- 如果打开文件数到了系统限制,在打开文件就会失败;
mtianyan@mtianyan-deep:~/Desktop/PythonFile$ ps
PID TTY TIME CMD
5587 pts/1 00:00:00 bash
5918 pts/1 00:00:02 ipython
看到ipython进程:
mtianyan@mtianyan-deep:~/Desktop/PythonFile$ cat /proc/5918/limits
Limit Soft Limit Hard Limit Units
Max open files 1024 1048576 files
-
file.fileno():文件描述符;每打开一个就会加1,直到1024失败
n [3]: for i in range(1027):
...: f = open("mtianyan.txt",'r')
...: print ("%d:%d" %(i,f.fileno()))
mark
并没有起到效果。fileno在10和12不断来回
实现代码:
In [11]: for i in range(1025):
...: list_f.append(open("mtianyan.txt",'w'))
...: print ("%d:%d" %(i,list_f[i].fileno()))
...:
运行结果:
1010:1022
---------------------------------------------------------------------------
IOError Traceback (most recent call last)
<ipython-input-10-dd5ee4024396> in <module>()
1 for i in range(1025):
----> 2 list_f.append(open("mtianyan.txt",'w'))
3 print ("%d:%d" %(i,list_f[i].fileno()))
4
IOError: [Errno 24] Too many open files: 'mtianyan.txt'
因此我们需要养成良好编码习惯记得关闭文件。
文件指针。
- 写入文件后,必须打开才能读取写入的内容
- 读取文件后,无法重新再次读取读取过的内容
比如:写完文件必须关闭才能读到我们写入的内容。
文件指针用于定位我们当前文件所执行到的位置:
- 使用
open()打开时文件指针就在起始位置。 -
read(3)文件指针就会往后挪3个位置。指向第四个位置。 -
write('mtianyan')就会从当前文件指针4开始写。文件指针就会移动8个字节。
我们之前的问题都是因为我们无法使文件指针归于起始位置的,
下面我们学习自由操作文件指针:
seek(offset[,whence]):移动文件指针
offset 偏移量,可以为负数
whence:偏移相对位置:
os.SEEK_SET:相对文件的起始位置0
os,SEEK_CUR:相对文件的当前位置1
os.SEEK_END:相对文件的结尾位置2
In [1]: f = open('mtianyan.txt','r+')
In [2]: import os
In [3]: f.tell()
Out[3]: 0
f.tell()(函数)返回当前文件的偏移返回的是一个整数,也许是一个长整数
In [4]: f.read(3)
Out[4]: 'tes'
In [5]: f.tell()
Out[5]: 3
In [6]: f.seek(0, os.SEEK_SET)
In [7]: f.tell()
Out[7]: 0
可以看出我们可以使用f.seek(0, os.SEEK_SET)来进行指针归零。
In [8]: f.seek(0,os.SEEK_END)
In [9]: f.tell()
Out[9]: 3139
In [10]: ls -l mtianyan.txt
-rw-r--r-- 1 mtianyan mtianyan 3139 1月 6 03:58 mtianyan.txt
In [11]: f.read(5)
Out[11]: ''
当然我们也可以把文件指针指到末尾去。文件指针已经在末尾了,向后继续读取会为空。
In [12]: f.seek(-5,os.SEEK_END)
In [13]: f.read(5)
Out[13]: '231\n\n'
使用f.seek(-5,os.SEEK_END)可以将文件指针倒着移回去。
In [14]: f.tell()
Out[14]: 3139
In [15]: f.seek(-5,os.SEEK_CUR)
In [16]: f.tell()
Out[16]: 3134
读取5个之后,指针又跑到了3139:使用f.seek(-5,os.SEEK_CUR)
又可以从当前位置往前移。
当文件偏移大于文件长度会出现的问题:
In [17]: f.seek(0,os.SEEK_END)
In [18]: f.tell()
Out[18]: 3139
In [19]: f.seek(-3140,os.SEEK_END)
---------------------------------------------------------------------------
IOError Traceback (most recent call last)
<ipython-input-19-331d0bc629ca> in <module>()
----> 1 f.seek(-3140,os.SEEK_END)
IOError: [Errno 22] Invalid argument
会报错:告诉我们这是一个无效的参数。类似于索引越界等。
文件属性及OS模块使用
讲解文件属性、标准文件、文件命令行参数、文件编码格式、使用os模块中方法对文件进行操作、使用os模块中的方法对目录进行操作
文件属性编码格式
Python文件属性
-
file.fileno():文件描述符;每打开一个就会加1,直到1024失败 -
file.mode:文件打开权限 -
file.encoding:文件编码格式 -
file.closed:文件是否关闭
In [20]: f.fileno()
Out[20]: 12
In [21]: f.closed
Out[21]: False
In [22]: f.mode
Out[22]: 'r+'
In [23]: f.encoding
没有返回值:为ASCII码文件。应该与系统相关。
Python标准文件
- 文件标准输入:
sys.stdin; - 文件标准输出:
sys.stdout; - 文件标准错误:
sys.stderr;
In [26]: import sys
In [27]: type(sys.stdin)
Out[27]: file
In [28]: sys.stdin.mode
Out[28]: 'r'
In [29]: sys.stdin.read()
系统的sys.stdin的read()是指从控制台读入数据。
In [32]: sys.stdin.fileno()
Out[32]: 0
系统的sys.stdin的fileno为0。也就是我们启动一个进程,他首先会打开标准输入:从控制台读数据。
a = raw_input() 其实内部是调用sys.stdin来读取数据的。
In [37]: sys.stdout.mode
Out[37]: 'w'
In [38]: sys.stdout.write("1000")
1000
sys.stdout只可以写,不可以读。写入相当于写入到控制台,会直接显示。
print实际内部调用的是stdout
In [39]: sys.stdout.fileno()
Out[39]: 1
sys.stdout是系统在启动程序时第二个打开的。
也就是系统会先为我们打开输入,再打开输出。
In [40]: sys.stderr.mode
Out[40]: 'w'
In [41]: sys.stderr.write("error")
error
In [42]: sys.stderr.fileno()
Out[42]: 2
sys.stderr是系统在启动程序时第二个打开的。
先开输入,再开输出。第三err输出。
Python文件命令行参数
sys模块提供了sys.argv,通过该属性可以得到命令行参数
很多程序都是这么做的:比如加上-v,根据不同的参数完成不同的功能。
sys.argv是一个字符串组成的列表
import sys
if __name__ == '__main__':
print len(sys.argv)
for arg in sys.argv:
print arg
运行结果:
1
D:\mtianBlog\hexoBlog-Github\source\_posts\test.py
直接运行,我们没有提供参数,打印出了当前文件的绝对路径文件名。
Python文件编码方式。
使用普通方式打开文件:写入u天涯明月笙。
In [3]: f.write(u'天涯明月笙')
---------------------------------------------------------------------------
UnicodeEncodeError Traceback (most recent call last)
<ipython-input-3-3cd15b559bd9> in <module>()
----> 1 f.write(u'天涯明月笙')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)
会报错:UnicodeEncodeError因为我们的文件是ascii格式的。不能将Unicode字符串写入。
实现代码:
In [1]: a = unicode.encode(u'天涯','utf-8')
In [2]: a
Out[2]: '\xe5\xa4\xa9\xe6\xb6\xaf'
In [3]: f.write(a)
运行结果:
In [7]: cat mtianyan.txt
天涯
我们可以用codecs模块提供方法创建制定编码格式文件.
import codecs
f = codecs.open('test.txt','w','utf-8')
f.encoding
open(fname,mode,encoding,errors,buffering):使用指定编码格式打开文件
linux文件系统
所有设备都可以看为文件:
- 包括磁盘(ext2,ext4)文件,NFS文件系统,各种外设
mark
虚拟文件系统为文件提供文件节点
每一个外设在内核中都有一个对应的驱动程序。通过系统调用对文件节点进行访问。把所有外设作为文件处理。
mark
在操作系统之上的Python解释器,Python程序跑在解释器中。
mark
使用open()会访问文件节点。设备节点对应虚拟文件系统的方法,这个文件系统与驱动绑定。
驱动会操作硬件设备。
os模块对文件和目录进行操作
使用os模块来打开文件:
os.open(filename, flag [,mode])打开文件
- os.O_CREAT:创建文件
- os.O_RDONLY:只读方式打开
- os.O_WRONLY:只写方式打开
- os.O_RDWR:读写方式打开
os.read(fd, buffersize):读取文件,返回值为读取的内容
os.write(fd, string):写入文件 ,返回值是写入数据的大小
os.lseek(fd, pos, how): 文件指针操作
os.close(fd):关闭文件
umask获取系统的默认权限。我的是0022
In [14]:fd = os.open('mtianyan.txt',os.O_CREAT | os.O_RDWR)
In [15]: n = os.write(fd,"mtianyan")
In [16]: n
Out[16]: 8
In [17]: os.lseek(fd,0,os.SEEK_SET)
Out[17]: 0
In [18]: os.read(fd,5)
Out[18]: 'mtian'
Out[19]: os.close()
使用os是为了跨平台。
os方法
| os方法 | 说明 |
|---|---|
| os.access(path, mode) | 判断该文件权限:F_OK存在,权限:R_OK ,W_OK, X_OK |
| os.listdir(path) | 返回当path路径下所有文件名组成的列表 |
| os.remove(path) | 删除文件 |
| os.rename(old, new) | 修改文件或者目录名 |
| os.mkdir(path[, mode]) | 创建目录 |
| os.makedirs(path[, mode]) | 创建多级目录 |
| os.removedirs(path) | 删除多级目录 |
| os.rmdir(path) | 删除目录(目录必须空目录) |
os.access('mtianyan,txt',os.R_OK)
os.listdir('./')
os.rename('test/','test1')
os.path方法
| os.path方法 | 说明 |
|---|---|
| os.path.exists(path) | 当前路径是否存在,也可以判断是否有该文件 |
| os.path.isdir(s) | 是否是一个目录 |
| os.path.isfile(path) | 是否是一个文件 |
| os.path.getsize(filename) | 返回文件大小,返回目录文件大小 |
| os.path.dirname(p) | 返回路径的目录 |
| os.path.basename(p) | 返回路径的文件名 |
os.path.exists('./mtianyan.txt')
# True
os.path.isdir('./'')
# True
os.path.isdir('mtianyan.txt')
# False
os.path.isfile('mtianyan.txt')
# True
文件练习
使用Python来管理ini文件:实现查询,添加,删除,保存。
- 掌握文件的基本操作
- 认识
ini文件 - 了解
ConfigPaser
ini配置文件格式:
节: [session]
参数(键=值) name=value
[port]
port1 = 800
port2 = 900
定位带port这个节,然后找到。
ConfigParser
mtianyan.txt内容:
[userinfo]
name = mtianyan
pwd = abc
[study]
python = 15
java_base = 20
两个概念:sections(大的分类)和option小的选项
import ConfigParser
cfg = ConfigParser.ConfigParser()
help(cfg.read)
cfg.read('mtianyan.txt')
# ['mtianyan.txt']
cfg.sections() #返回节点值:userinfo,study
# ['userinfo', 'study']
for se in cfg.sections():
print se
print cfg.items(se)
#遍历出整个文件
遍历结果如下
userinfo
[('name', 'johntian'), ('pwd', 'abc')]
study
[('python_base', '15'), ('python_junior', '20'), ('linux_base_', '15')]
cfg.set('userinfo', 'pwd', '1234567') #修改键值
cg.set('userinfo','email','1147727180.com')//增加键值
cfg.remove_option('user info','email') //删除键值
文件操作练习
import os
import ConfigParser
'''
1: dump ini
2: del section
3: del item
4: modify item
5: add section
6: save modify
'''
class student_info(object):
def __init__(self, recordfile):
self.logfile = recordfile
self.cfg = ConfigParser.ConfigParser()
def cfg_load(self):
self.cfg.read(self.logfile)
def cfg_dump(self):
se_list = self.cfg.sections()
print "**********************>"
for se in se_list:
print se
print self.cfg.items(se)
print "<*********************"
def delete_item(self, section, key):
self.cfg.remove_option(section, key)
def delete_section(self, sescion):
self.cfg.remove_option(section)
def add_section(self, section):
self.cfg.add_section(section)
def set_item(self, section, key, value):
self.cfg.set(section, key, value)
def save(self):
fp = open(self.logfile, 'w')
self.cfg.write(fp)
fp.close()
if __name__ == '__main__':
info = student_info('mtianyan.txt')
info.cfg_load()
info.cfg_dump()
info.add_section('userinfo')
info.set_item('userinfo', 'pwd', 'abc')
info.cfg_dump()
info.add_section('login')
info.set_item('login', '2018-0106', '20')
info.cfg_dump()
info.save()
运行结果:
**********************>
**********************>
userinfo
[('pwd', 'abc')]
<*********************
**********************>
userinfo
[('pwd', 'abc')]
<*********************
login
[('2018-0106', '20')]
<*********************
[Finished in 0.3s]










网友评论