文件操作


# 读操作
#
打开文件模式:r,w,a f=open('陈粒',encoding='utf-8') #一定要注明编码用什么。 data=f.read() print(data) f.close() #回收。不写不报错,但会一直占用内存不释放 f=open('xxx') #保存文件xxx的时候选择GBK。当前windows用的就是GBK,所以默认GBK编解码 data=f.read() print(data) #文件打开方式:读r 写w 加a f=open('陈粒','r',encoding='utf-8') # 只读 data=f.read() #读取文件全部 # print(data) #打印出读出来的全部文件内容 print(f.readable()) #可读与否:返回True/False # readline()一次读一行.如果在readable后再readline将输出空白。 print('第1行',f.readline(),end='') #一次读一行。如果某一行没有内容了,就输出空白 print('第2行',f.readline()) #每行之间会有一个空行 print('第3行',f.readline()) print('第4行',f.readline()) print('第5行',f.readline()) print('第6行',f.readline()) print('第7行',f.readline()) # readlines()将所有文件内容都读取出来放到一个列表当中。 data=f.readlines() # ['1111 ','22222 ','dsds ','hsdichuiuh'] print(data) f.close()
# 写操作:w模式打开,会全部清空文件
f=open('陈粒1','w',encoding='utf8')        #如果文件不存在,会自动创建一个新的文件
# f.read()                                #写模式,只能写不能读。
f.write('11111111
')                    #要自己加换行符
f.write('222222222
')
f.write('333
4444
555
')
# f.writable()
f.writelines(['555
','6666
'])
# f.writelines(['555
','6666
',1])         # 文件内容只能是字符串,只能写字符串
f.close()
# 文件处理追加操作
f=open('陈粒1','a',encoding='utf-8')
f.write('写到文件最后')

一 文件操作

一 介绍

计算机系统分为:计算机硬件,操作系统,应用程序三部分。

我们用python或其他语言编写的应用程序若想要把数据永久保存下来,必须要保存于硬盘中,这就涉及到应用程序要操作硬件,众所周知,应用程序是无法直接操作硬件的,这就用到了操作系统。操作系统把复杂的硬件操作封装成简单的接口给用户/应用程序使用,其中文件就是操作系统提供给应用程序来操作硬盘虚拟概念,用户或应用程序通过操作文件,可以将自己的数据永久保存下来。

有了文件的概念,我们无需再去考虑操作硬盘的细节,只需要关注操作文件的流程:

#1. 打开文件,得到文件句柄并赋值给一个变量
#2. 通过句柄对文件进行操作
#3. 关闭文件

二 在python中

复制代码
#1. 打开文件,得到文件句柄并赋值给一个变量
f=open('a.txt','r',encoding='utf-8') #默认打开模式就为r

#2. 通过句柄对文件进行操作
data=f.read()

#3. 关闭文件
f.close()
复制代码

三 f=open('a.txt','r')的过程分析

#1、由应用程序向操作系统发起系统调用open(...)

#2、操作系统打开该文件,并返回一个文件句柄给应用程序

#3、应用程序将文件句柄赋值给变量f

四 强调!!!

#强调第一点:
打开一个文件包含两部分资源:操作系统级打开的文件+应用程序的变量。在操作完毕一个文件时,必须把与该文件的这两部分资源一个不落地回收,回收方法为:
1、f.close() #回收操作系统级打开的文件
2、del f #回收应用程序级的变量

其中del f一定要发生在f.close()之后,否则就会导致操作系统打开的文件还没有关闭,白白占用资源,
而python自动的垃圾回收机制决定了我们无需考虑del f,这就要求我们,在操作完毕文件后,一定要记住f.close()

虽然我这么说,但是很多同学还是会很不要脸地忘记f.close(),对于这些不长脑子的同学,我们推荐傻瓜式操作方式:使用with关键字来帮我们管理上下文
with open('a.txt','w') as f:
pass

with open('a.txt','r') as read_f,open('b.txt','w') as write_f:
data=read_f.read()
write_f.write(data)

强调第一点:资源回收

#强调第二点:
f=open(...)是由操作系统打开文件,那么如果我们没有为open指定编码,那么打开文件的默认编码很明显是操作系统说了算了,操作系统会用自己的默认编码去打开文件,在windows下是gbk,在linux下是utf-8。
这就用到了上节课讲的字符编码的知识:若要保证不乱码,文件以什么方式存的,就要以什么方式打开。

f=open('a.txt','r',encoding='utf-8')

五 python2中的file与open

 
#首先在python3中操作文件只有一种选择,那就是open()
#而在python2中则有两种方式:file()与open()
两者都能够打开文件,对文件进行操作,也具有相似的用法和参数,但是,这两种文件打开方式有本质的区别.
file为文件类,用file()来打开文件,相当于这是在构造文件类。
而用open()打开文件,是用python的内建函数来操作。
我们一般使用open()打开文件进行操作,而用file当做一个类型,比如type(f) is file

二 打开文件的模式

文件句柄 = open('文件路径', '模式')

模式可以是以下方式以及他们之间的组合:

Character Meaning
‘r' open for reading (default)
‘w' open for writing, truncating the file first
‘a' open for writing, appending to the end of the file if it exists
‘b' binary mode
‘t' text mode (default)
‘+' open a disk file for updating (reading and writing)
‘U' universal newline mode (for backwards compatibility; should not be used in new code)
复制代码
#1. 打开文件的模式有(默认为文本模式):
r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】
w,只写模式【不可读;不存在则创建;存在则清空内容】
a, 之追加写模式【不可读;不存在则创建;存在则只追加内容】

#2. 对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式)
rb 
wb
ab
注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码

#3. 了解部分
"+" 表示可以同时读写某个文件
r+, 读写【可读,可写】
w+,写读【可读,可写】
a+, 写读【可读,可写】


x, 只写模式【不可读;不存在则创建,存在则报错】
x+ ,写读【可读,可写】
xb
复制代码
# 文件处理b模式
# 应用程序和函数一样,都是执行某项功能
# b就是以二进制形式执行

# f=open('test11.py','rb',encoding='utf-8')     #b的方式不能指定编码
f=open('test11.py','rb')                         #b的方式不能指定编码
data=f.read()                                    #读取文件中所有数据。
#'字符串'---------encode---------》bytes
#bytes---------decode---------》'字符串'
print(data)                            #  b'hello1
22222
3333
4444xe4xbdxa0.....
print(data.decode('utf-8'))            #  为了看到内容,加上解码: 
#hello1
#22222
#3333
#你好
f.close()

# 字符串转bytes,叫编码
f=open('test22.py','wb')                         #b的方式不能指定编码
f.write(bytes('1111
',encoding='utf-8'))        #不能直接写字符串类型,需要转换成编码,才能得到bytes类型
f.write('杨件'.encode('utf-8'))                    

f=open('test22.py','ab')             #b的方式不能指定编码,从文件的最后一个位置开始写,自己不会换了行再写
f.write('杨件'.encode('utf-8'))

open('a;ltxt','wt')            #打开方式默认就是文本模式打开,就是t
# 但是不只有文本,还有视频、图片等,因此有b(二进制)模式
# linux和windows底层都是二进制形式处理,用二进制可以相通
# b对linux没有强调必要,因为linux就是二进制处理。

# 了解U模式
# 回车与换行的来龙去脉
http://www.cnblogs.com/linhaifeng/articles/8477592.html

# U模式
'U' mode is deprecated and will raise an exception in future versions
of Python.  It has no effect in Python 3.  Use newline to control
universal newlines mode.

# 总结:
在python3中使用默认的newline=None即可,换行符无论何种平台统一用
即可

三 操作文件的方法

#掌握
f.read()     #读取所有内容,光标移动到文件末尾
f.readline() #读取一行内容,光标移动到第二行首部
f.readlines() #读取每一行内容,存放于列表中

f.write('1111
222
') #针对文本模式的写,需要自己写换行符
f.write('1111
222
'.encode('utf-8')) #针对b模式的写,需要自己写换行符
f.writelines(['333
','444
']) #文件模式
f.writelines([bytes('333
',encoding='utf-8'),'444
'.encode('utf-8')]) #b模式

#了解
f.readable() #文件是否可读
f.writable() #文件是否可读
f.closed    #文件是否关闭
f.encoding   #如果文件打开模式为b,则没有该属性与原文件的编码无关。如果不知道原文件的编码,肯定就是乱码。
f.flush()    #立刻将文件内容从内存刷到硬盘
f.name
练习,利用b模式,编写一个cp工具,要求如下:

  1. 既可以拷贝文本又可以拷贝视频,图片等文件

  2. 用户一旦参数错误,打印命令的正确使用方法,如usage: cp source_file target_file

  提示:可以用import sys,然后用sys.argv获取脚本后面跟的参数

 

import sys
if len(sys.argv) != 3:
print('usage: cp source_file target_file')
sys.exit()

source_file,target_file=sys.argv[1],sys.argv[2]
with open(source_file,'rb') as read_f,open(target_file,'wb') as write_f:
for line in read_f:
write_f.write(line)

四 文件内光标移动

一: read(3):

  1. 文件打开方式为文本模式时,代表读取3个字符

  2. 文件打开方式为b模式时,代表读取3个字节

二: 其余的文件内光标移动都是以字节为单位如seek,tell,truncate

注意:

  1. seek有三种移动方式0,1,2,其中1和2必须在b模式下进行,但无论哪种模式,都是以bytes为单位移动的

  2. truncate是截断文件,所以文件的打开方式必须可写,但是不能用w或w+等方式打开,因为那样直接清空文件了,所以truncate要在r+或a或a+等模式下测试效果

 

import time
with open('test.txt','rb') as f:
f.seek(0,2)
while True:
line=f.readline()
if line:
print(line.decode('utf-8'))
else:
time.sleep(0.2)

练习:基于seek实现tail -f功能

五 文件的修改

文件的数据是存放于硬盘上的,因而只存在覆盖、不存在修改这么一说,我们平时看到的修改文件,都是模拟出来的效果,具体的说有两种实现方式:

方式一:将硬盘存放的该文件的内容全部加载到内存,在内存中是可以修改的,修改完毕后,再由内存覆盖到硬盘(word,vim,nodpad++等编辑器)

复制代码
import os

with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
    data=read_f.read() #全部读入内存,如果文件很大,会很卡
    data=data.replace('alex','SB') #在内存中完成修改

    write_f.write(data) #一次性写入新文件

os.remove('a.txt')
os.rename('.a.txt.swap','a.txt') 
复制代码

方式二:将硬盘存放的该文件的内容一行一行地读入内存,修改完毕就写入新文件,最后用新文件覆盖源文件

复制代码
import os

with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
    for line in read_f:
        line=line.replace('alex','SB')
        write_f.write(line)

os.remove('a.txt')
os.rename('.a.txt.swap','a.txt') 
复制代码

练习题:

复制代码
1. 文件a.txt内容:每一行内容分别为商品名字,价钱,个数,求出本次购物花费的总钱数
apple 10 3
tesla 100000 1
mac 3000 2
lenovo 30000 3
chicken 10 3

2. 修改文件内容,把文件中的alex都替换成SB
复制代码
##例1:如果原文件是utf-8格式编码的
f=open('a.txt','r+',encoding='gb2312')        #给的编码encoding错了,报错:无法解码decode
# data=f.read()            # 可能读出乱码,也可能报错decode错误
# print(data)
f.write('你好')            # 写入时按照encoding='gb2312'来编码写入文件。执行不报错。
# 但是到a.txt文件中取看:编码错误,刚写入的变成乱码
# 因此,如果不知道原文件编码,或者原文件编码与你解码方式不匹配,不论是读还是写都会出问题

##############################################可以忘记,不大准
# latin-1尽可能多的保存原文件代码
f=open('b.txt','r+',encoding='latin-1')        #latin-1可以兼容大部分的编码。当不知道原文件编码方式时,可以试一下
data=f.read()
print(data)
f.write('aaaaaaaaaaa')                        #英文可以正常输入原文件
################################################3

# 要是不加上newline=''的话,python会帮你打印成/r/n,即当前windows系统中的换行符
f=open('b.txt','r',encoding='utf-8',newline='')  #读取文件中真正的换行符号
f=open('b.txt','r+',encoding='utf-8',newline='') #读取文件中真正的换行符号

print(f.closed)
print(f.encoding)        #取文件打开模式的编码,与原文件的编码无关。如果不知道原文件的编码,肯定就是乱码。
f.flush()                #立刻将文件内容从内存刷到硬盘
print(f.readlines())

# tell以字节为单位,显示光标位置。文件起始位置为0
print(f.tell())
f.readline()
print(f.tell())

# seek控制光标的移动,seek以字节处理
f.seek(1)                 # 移动一个字节。原文件中第一行为为   你好    则移动到汉字  你(占3个字节) 的第一个字节处
print(f.tell())             # 看看光标位置
print(f.readlines())     # 报错:无效开始字节。即从 你  的(占3个字节) 的后二个字节处开始无法读到正确的

f.seek(3)                 # 从开头开始算,将光标移动到第三个字节
print(f.tell())             # 看看光标位置
print(f.read())             # 好         #从光标当前位置开始读字符

data=f.read(1)             #你   从光标当前位置开始直接读出一个字符,传的数字参数表示往后读几个字符
print(data)

# 文件截断:这是一种写操作,从开头到第十个位置保留,剩下删去。
# 打开方式不能是w+,否则打开完全就没有东西了,空文件何谈截取
f.truncate(10)
f.flush() #讲文件内容从内存刷到硬盘
f.closed #文件如果关闭则返回True
f.encoding     #查看使用open打开文件的编码
f.tell()     #查看文件处理当前的光标位置
f.seek(3) #从开头开始算,将光标移动到第三个字节
f.truncate(10) #从开头开始算,将文件只保留从0-10个字节的内容,文件必须以写方式打开,但是w和w+除外


f=open('d.txt','r',newline='')            #加上newline='',读取文件中真正的换行符
data=f.readline().encode('utf-8')
print(data)
print(f.tell())

# utf-8编码的seek.txt文件。

# hello
# 你好
# 123
# 123

# hello
你好
123
123


# 0模式:seek按照绝对位置来移动光标:默认模式,等价于第二个参数为0
f=open('seek.txt','r',encoding='utf-8')        #默认seek模式下,不用写明是rb,只写r就可以,seek会默认是rb
print(f.tell())            #0                #刚打开文件,光标在0的位置
f.seek(10)                #光标移动到10的位置
print(f.tell())            #10
f.seek(3)                #光标移动到3
print(f.tell())            #3

# 1模式:seek按照相对位置移动光标。
f=open('seek.txt','rb')        # 1模式需要注明是rb模式,否则报错。同时二进制b模式下,不能指定编码方式。
print(f.tell())
f.seek(10,1)            #10
print(f.tell())
f.seek(3,1)                #13    从当前位置再移动3个
print(f.tell())

# 2模式是从文件末尾倒着读,参数列表第一项应该为负的。
# 但是取出来的数据是正序
f=open('seek.txt','rb')
print(f.tell())
f.seek(-5,2)        #2模式移动光标,光标移动到倒数第五个字节处,即第二行3后面
print(f.read())        #从当前位置开始读完文件所有内容。将读出正序的  #  b'
123'   #按照文件在的windows系统中的回车符来表示

print(f.tell())
f.seek(3,1)
print(f.tell())

# 用途:日志文件非常大,每写一条就要追加上时间。一般查看时都是看最新的。开始部分都太久远了
# 2016/12/25 alex  干了件事情
# 2016/12/26 alex  干了件事情
# 2016/12/27 alex  干了件事情
# 2016/12/28 alex  干了件事情
# 2016/12/29 alex  干了件事情
# 2016/12/30 sb  干了件sb事情

# 方法一:如果不用seek方法,只能用readlines一行行读出全部放入一个列表中,占大量内存。
# 而且我只需要后面一部分内容而已
f=open('日志文件','rb')
data=f.readlines()
print(data[-1].decode('utf-8'))

# 方法二:依旧取出全部文件的直接放入内存中
f=open('日志文件','rb')
for i in f.readlines():
    print(i)
# 循环文件的推荐方式
# for i in f:
    # print(i)
    
# 面试题考过:读取日志文件的最后一行
    # 方法三:直接写f,而不是f.readlines()。这样要一行取一行,不会一次性全部取出来放在内存中。
for i in f:
    offs=-10                #偏移量,预估原文件中一行有多少字节
    while True:
        f.seek(offs,2)        # 用2模式倒着读
        data=f.readlines()    #从当前光标开始,把后面的内容全读出来组成一个列表
        if len(data) > 1:    #读了至少两行,列表长度是大于一行
            print('文件的最后一行是%s' %(data[-1].decode('utf-8')))        #读出列表中最后一行
            break            #已经找到最后一行了,必须加上break退出
        offs*=2            # 没有把最后一行读完整,扩大偏移量
原文地址:https://www.cnblogs.com/Josie-chen/p/8687826.html