任何语言都离不开对文件的操作,Python语言是如何来操作和管理文件的。掌握文件的基本概念、基本操作,了解文件属性、 linux 系统的文件管理机制及os模块对文件和目录的处理等相关内容。
- [√]慕课网Meshare_huang老师:Python文件处理
Python的文件处理:
初学者:推荐使用虚拟机VMwareStation
+ Deepin
必备前置条件知识储备:
Python文件概念:
文件: Python中文件是对象依赖我们的具体系统。
Linux文件:一切设备都可以看成文件
- 例如:磁盘文件,管道,网络Socket,外设等。
文件属性:用户
,读
,写
,执行权限
;
在Linux控制台输入命令:
#cd进一个空目录
cd Desktop/
#创建一个文件夹
mkdir PythonFile
cd PythonFile
touch hello.py
vim hello.py
使用i
进入insert模式
进行插入代码。
插入完成后使用esc
退出编辑模式,输入:wq
进行保存。
通过ls -l
命令可以看到文件的一些基本属性:
-
代表是一个文件,d
代表是一个目录 rw-
代表当前用户对于当前文件有读
有写
没有执行权限
。(-
代表没有权限,x
为可执行)r--
:表示当前用户组对该文件有读r
没有写w
的权限,没有执行权限x
第三组r--
:表示其他用户对该文件有读r
权限,但没有写w
和执行x
权限
第一个mtianyan
代表文件的拥有者是mtianyan
mtianyan
代表这个文件所属的用户组是mtianyan
21
代表文件的大小是21字节
后面的时间是文件的创建时间。后面是文件名。可以看到我们最为当前用户现在没有该文件执行权限。
使用chmod
为文件增加权限说明:
chmod a+x 文件名
;chmod u+x 文件名
;chmod g+x 文件名
;chmod o+x 文件名
;可以看到我们通过chmod +x hello.py
为当前用户
,用户组
,其他用户
都添加了执行权限
Linux下首行一定要加这句话:!/usr/bin/Python
,!/usr/bin/Python
是告诉操作系统执行这个脚本的时候,调用/usr/bin
下的python解释器。
打印出了Helloworld
好像有点问题。以后对Linux熟了再回来看。
python
命令是可以正常执行的。
主要讲解文件打开及文件不同的打开方式、使用read
,readlines
及迭代器
访问文件、文件写入方式及写缓存
处理、文件关闭及由不关闭导致的问题、文件指针及文件指针操作
open(name [,mode[buf]])
name
: 文件路径mode
: 打开方式buf
: 缓冲buffering大小mode
方式有:只读
,只写
。读写
。
read([size])
读取文件(读取size个字节
,默认读取全部)
readline([size])
读取一行
readlines([size])
读取完文件(看到后面你就知道并不是),返回每一行所组成的列表。我们就可以使用列表访问的方式,使用文件。
弊端:一次性读完,占用很大内存。
write(str)
将字符串写入文件
writelines(sequence_of_strings) : 写多行到文件
sequence_of_strings
是一个由字符型组成的列表。一次性写入多行。
输入ipython
进入ipython环境
type(f)
可以看到打开的文件类型就是一个文件对象。
拥有的方法可以使用f.
+ Tab
自动补齐
Error:File not open for writing
这里因为我们使用的是默认无参数打开,此时我们并不拥有写权限。
Python文件打开方式
mode | 说明 | 说明 |
---|---|---|
‘r’ | 只读方式打开 | 文件必须存在 |
‘w’ | 只写方式打开 | 文件不存在创建文件;存在则清空内容 |
‘a’ | 追加方式打开 | 文件不存在创建文件 |
‘r+’/’W+’ | 读写方式打开 | |
‘a+’ | 追加和读写方式打开 |
'rb'
,'wb'
,'ab'
,'rb+'
,'wb+'
,'ab+'
:都是采用二进制方式打开。
'r'
'w'
'a'
'r+'
open
是用只读方式打开的,所以文件不存在会报错。f.close()
否则cat
时文件内容会为空。w
方式。已存在的文件会被清空才打开。a
方式打开此时我们可以对于文件进行追加操作,原有内容并没有被清空 r+
方式打开,我们新增的内容会从文件开始替换原有内容的一部分。w+
方式打开,文件可以被读写。但是文件内容被清空。使用二进制方式打开的使用方法与上面介绍的基本一致。
意义在于: 比如读取一张图片的EXIF信息读取图片的长宽拍照日期
如果我们以普通方式打开是以文本方式打开,不能读取到正确的内容。
当我们以二进制打开我们就可以读取到正确的EXIF信息
。
学习了打开文件,读取文件,写入文件。文件打开方式的不同。
read([size])
读取文件(读取size个字节
,默认读取全部)
readline([size])
读取一行
首先使用touch mtianyan.txt
和 vi mtianyan.txt
创建并写入内容:
blog.mtianyan.cn
blog.mtianyan.cn
blog.mtianyan.cn
可以看出
readline(size):
if len(line) > size:
return size
if len(line) < size:
return len(line)
readlines([size])
读取完文件(整数个buffer左右字节),返回每一行所组成的列表。我们就可以使用列表访问的方式,使用文件。
弊端:一次性读完,占用很大内存。
虽然设置了size
为1
,但是还是把所有都读出来了。
输入help(f.readlines)
查看定义:重复的调用readline()
然后返回一个读到的行所组成的列表。
读取文件的大小:
- 如果size
小于缓冲区
的大小,则读取缓冲区大小
的数据
- 如果size
大于缓冲区
的大小,读取size大小
的数据,但不是完全的等于size的大小,一般读取比size大的整行的数据.
这个设置是python解释器内部的一个变量。
import io
io.DEFAULT_BUFFER_SIZE
设置参数小于8192
每次能读取8192
个字节。
大于
一个8192
小于2*8192=16384
。则读取两个buffer
值.BUFFER
都是一整个一整个读使用vi
打开文件。y
是复制当前行,yy
是复制光标所在的一整行.
如果复制了一行,则p
是在光标所在行的下一行粘贴,10000p
就是粘贴10000次
命令yy,10000p
复制10000
行 .然后:set nu
显示行数
gg
:
:1
或 gg
文件末尾行数+gg
:$
Shift+g
123gg
或:123
.使用ctrl+z
可将程序切换到后台。用fg
可将后台程序切换到前台。
可以看到此时的文件以及有十几万个字节了
可以看出这时候不再是一次性把文件读取完了,而是读取了482
行。
170051/100003
行,每行17
个字节。
这时我们的size
大于了缓冲区的大小8192
。因此我们的size
到了8192
和16384
之间。
重点:系统数据是按着整个buffer,整个buffer
来读的。此时系统会读取差不多两个buffer
的数据。也就是963*17 =16371
。
待探究
迭代器并不是把我们的文件全部存入了内存中。而是我们每使用一次next
方法就会自动读取下一行。所以能在不消耗大量内存的情况下进行读取整个文件。
write(str)
将字符串写入文件
writelines(sequence_of_strings) : 写多行到文件
sequence_of_strings
参数是一个可迭代对象
:
- 可以是字符串
- 可以是字符串所组成的元组。
- 可以是一个字符串所组成的迭代器。
- 也可是一个字符串组成的列表
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)
40-0a3726375675> in ()
----> 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
他是不会写到磁盘文件的。
写文件的过程中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()
操作时内容更新。
说明我的Linux的缓冲区就是这么大147456
当我们执行close
文件会变大。因为全部写进去了。
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()))
并没有起到效果。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)
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)
19-331d0bc629ca> in ()
----> 1 f.seek(-3140,os.SEEK_END)
IOError: [Errno 22] Invalid argument
会报错:告诉我们这是一个无效的参数。类似于索引越界等。
讲解文件属性、标准文件、文件命令行参数、文件编码格式、使用os模块中方法对文件进行操作、使用os模块中的方法对目录进行操作
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
码文件。应该与系统相关。
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输出。
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
直接运行,我们没有提供参数,打印出了当前文件的绝对路径文件名。
使用普通方式打开文件:写入u天涯明月笙
。
In [3]: f.write(u'天涯明月笙')
---------------------------------------------------------------------------
UnicodeEncodeError Traceback (most recent call last)
3-3cd15b559bd9> in ()
----> 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):
使用指定编码格式打开文件
所有设备都可以看为文件:
- 包括磁盘(ext2,ext4)文件,NFS文件系统,各种外设
虚拟文件系统
为文件提供文件节点
每一个外设在内核中都有一个对应的驱动程序。通过系统调用对文件节点进行访问。把所有外设作为文件处理。
在操作系统之上的Python解释器,Python程序跑在解释器中。
使用open()
会访问文件节点
。设备节点对应虚拟文件系统的方法,这个文件系统与驱动绑定。
驱动会操作硬件设备。
使用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.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.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
节: [session]
参数(键=值) name=value
[port]
port1 = 800
port2 = 900
定位带port
这个节,然后找到。
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]