文件操作

一 什么是文件

文件是操作系统提供给用户/应用程序操作硬盘的一种虚拟的概念/接口

用户/应用程序

操作系统(文件)

计算机硬件(硬盘)

二 为何要用文件

用户/应用程序可以通过文件将数据永久保存在硬盘中,即操作 文件就是操作硬盘

用户/应用程序直接操作的是文件,对文件进行的所有的操作,都是在向操作系统发送系统调用,然后再由操作将其转换成具体的硬盘操作

三 如何用文件:open()

控制文件读写内容的模式:t和b

​ 强调:t和b不能单独使用,必须跟r/w/a连用

1、t文本(默认的模式)

​ 1、读写都以str(unicode)为单位的
​ 2、文本文件
​ 3、必须指定encoding='utf-8'

2、b二进制/bytes

b:binary 模式

​ 1、读写都是以 bytes 为单位

​ 2、可以针对所有模式

​ 3、b 模式下,一定不能指定字符编码,即一定不能指定 encoding参数

总结:

​ 1、在操作纯文本方面 t 模式帮我们省去了编码与解码的环节

​ 2、针对文本文件(如图片、视频、音频等)只能使用 b 模式

​ 通用模式为 b 模式

bytes 类型转换

x = 10 #int(10)
'你'.encode('gbk') # bytes( )

控制文件读写操作的模式:

​ r只读模式
​ w只写模式
​ a只追加写模式
​ +:r+、w+、a+

ps:

​ x 模式(控制文件操作的模式)-->了解

​ x:只写模式(不可读,不存在则创建,存在则报错)

​ x+ :可读可写

四 文件基本操作

1、打开文件

​ windows 路径分隔符问题解决方法:

​ 绝对路径:

​ 从根目录下一层一层找

open(r 'c:/a/nb/c.text ')  #假如写右斜杠也行,open()会帮你转换成,用 r 表示告诉你这只是普通的字符,没有转义

​ 相对路径:

​ 必须以当前文件为基准

open('c.txt') #打开文件, 后面才能操作文件内容
f = open('c.txt' mode='rt')   #f的值是一种变量,占用的应用程序的内存空间,open操作占用操作系统的内存(因为它想操作系统发送的打开文件的请求)
print(f) 

2、操作文件:读/写文件

​ 应用程序对文件的读写请求都是在向操作系统发送系统调用,然后由操作系统控制硬盘把输入读入内存、或者写入硬盘。

res = f.read()#向操作系统发送请求,把文件读入内存
print(res)

3、关闭文件

f.close( ) #  关闭文件, 回收操作系统资源
f.read( ) #变量 f 是仍然存在的,但是不能再读了
#del f #回收应用程序资源  在 python 中回收应用程序资源不需要考虑,python 会帮你回收

五 with 上下文管理

由于大部分程序员还是会不由自主地忘记f.close(),考虑到这一点,python提供了with关键字来帮我们管理上下文

f:文件句柄

with open('a.txt' mode='rt')as f:  # ===> f= open('a.txt' mode='rt')
  res = f.read()
  print(res)
with open('a.txt' mode='rt')as f1, 
	open('b.txt' mode='rt')as f2:
  res1 = f1.read()
  res2 = f2.read()
  print(res1)
  print(res2)

2、指定字符编码

强调:t 和 b 不能单独使用, 必须跟 rwa 连用

t 文本(默认的模式):

1、读写都以 str(unicode)为单位的

2、文本文件

3、必须指定 encoding = 'utf-8'

没有指定 encoding 参数操作系统会使用自己默认的编码

linux 系统默认 utf-8

windows 系统默认 gbk

with open('c.txt', mode='rt', encoding= 'utf-8')as f:
		res= f.read() #t模式会将 f.read()读出的结果解码成 unicode
		print(res, type(res))

内存:utf-8格式的二进制----------解码--------------->unicode

硬盘:c.txt内容:utf-8格式的二进制

3、文件操作模式详解

以t模式为基础进行内存操作

1、r(默认的操作模式):只读模式,当文件不存在时报错,当文件存在时文件指针跳到开始位置

with open('c.txt',mode='rt',encoding='utf-8') as f:
    print('第一次读'.center(50,'*'))
    res=f.read() # 把所有内容从硬盘读入内存
    print(res) #哈哈哈哈
    print('第二次读'.center(50,'*'))
    res1=f.read()
    print(res1)#换行符

案例:

inp_username=input('your name>>: ').strip()
inp_password=input('your password>>: ').strip()

with open('user.txt',mode='rt',encoding='utf-8') as f:
    for line in f:
        # print(line,end='') # egon:123

        username,password=line.strip().split(':')
        if inp_username == username and inp_password == password:
            print('login successfull')
            break
    else:
        print('账号或密码错误')

应用程序======》文件

应用程序=》数据库管理软件=》文件

2、w:只写模式,当文件不存在时会创建空文件,当文件存在会清空文件,指针位于开始位置

注:如果重新以w模式打开文件,则会清空文件内容

with open('d.txt',mode='wt',encoding='utf-8') as f:
    f.read() # 报错,不可读
    f.write('擦勒
')

在以w模式打开文件没有关闭的情况下,连续写入,新的内容总是跟在旧的之后

with open('d.txt',mode='wt',encoding='utf-8') as f:
    f.write('擦勒1
')
    f.write('擦勒2
')
    f.write('擦勒3
')

w 模式一般用来创建全新的文件

文本文件的 copy 工具

src_file=input('源文件路径>>: ').strip()
dst_file=input('源文件路径>>: ').strip()
with open(r'{}'.format(src_file),mode='rt',encoding='utf-8') as f1,
    open(r'{}'.format(dst_file),mode='wt',encoding='utf-8') as f2:
    res=f1.read()
    f2.write(res)

3、a:只追加写,在文件不存在时会创建空文档,在文件存在时文件指针会直接调到末尾

with open('e.txt',mode='at',encoding='utf-8') as f:
    # f.read() # 报错,不能读
    f.write('擦嘞1
')
    f.write('擦嘞2
')
    f.write('擦嘞3
')

w 模式 与a 模式 的异同:

1、相同点:在打开的文件不关闭的情况下,连续的写入,新写的内容总会跟在前写的内容之后;

2、不同点:以 a 模式重新打开文件,不会清空原文件内容,会将文件指针直接移动到文件;

案例:a模式用来在原有的文件内存的基础之上写入新的内容,比如记录日志、注册

注册功能

name=input('your name>>: ')
pwd=input('your name>>: ')
with open('db.txt',mode='at',encoding='utf-8') as f:
    f.write('{}:{}
'.format(name,pwd))

了解:+ 不能单独使用,必须配合 r、w、a,而且它是继承自己(r/w/a)的特性的。其实就是在(r/w/a)的基础特性上增加了一个可读可写的功能。

with open('a.txt',mode='r+t', encoding='utf-8')as f:
		print(f.read()) #读出硬盘的二进制 —>t模式控制将二进制转换成unicode->字符。
		f.write('中国')
with open('a.txt', mode='w+', encoding='utf-8')as f:
    print(f.read()) # 读不到 ,w 模式下文件以前的数据会被清除
    f.write('中国
')
    f.write('222
')
    f.write('333
')
    print(f.read())#读不到, 指针处于末尾位置

with open('a.txt', mode='a+', encoding='utf-8')as f:
    print(f.read()) #读不到,因为 a模式下指针会指向末尾位置
    f.write('中国
')
    print(f.read()) #读不到,写入后指针还是处于末尾位置

Ps:

open(打开文件(文件很大))是不涉及读和写操作的,只是将文件指针控制到开头或者末尾,并不会阻塞,当data =f.read()(赋值:把硬盘的内容读入内存)操作时,才会会阻塞。

错误演示:t 模式只能读文本文件

with open('qq.mp4', mode='rt', encoding='utf-8')as f:
		f.read()#硬盘的二进制读入内存->t 模式会将读入内存的内容进行 decode解码操作

以b模式为基础进行内存操作

b 模式下:硬盘文件内容读入内存是不做任何转换的

一定不能指定 encoding 参数

读写都是以 bytes

with open(r'a.txt', mode='rb')as f: 
  	res = f.read() #硬盘的二进制(utf-8格式)直接读入内存->b模式下不做转换
  	print(res,type(res)) #b'xe4xbdxa0xe5xa5xbd' class<bytes>  -->当成二进制
    print(res.decode('utf-8'))#你好

b 模式下:不能写入字符串格式数据,所以需要 encode成硬盘的编码格式(utf-8)

with open('a.txt', mode='wb')as f:
	f.write('你好')#报错
  f.write('你好'.encode('utf-8'))
with open(r'b.txt', mode='wb')as f:
	f.write('你好 hello'.encode('gbk')) #��� hello  #你好显示乱码是因为pycharm打开默认是 utf-8解码,所以使用gbk编码导致中文显示会乱码
  

文件拷贝工具

src_file=input('源文件路径>>: ').strip()
dst_file=input('源文件路径>>: ').strip()
with open(r'{}'.format(src_file),mode='rb') as f1,
    open(r'{}'.format(dst_file),mode='wb') as f2:
    # res=f1.read() # 内存占用过大
    # f2.write(res)

    for line in f1:
        f2.write(line)

4、循环读取文件

方式一 : while + f.read(数据长度参数)

​ 数据长度参数:自己控制每次读取的数据量

​ 数据长度为字节长度

with open(r'test.jbg', mode='rb')as f:
		while True:
      	res = f.read(1024)#1024个字节
        if len(res)== 0:
        		break
        print(len(res)) #1024

方式二: for : 以行数为单位读, 当一行内容过长时会导致一次性读入内存的数据量过大

for 循环区分行是以换行符区分的

with open(r'g.txt', mode='rb')as f:
  for line in f:
    print(line, len(line))

注:当文件内容一行过长时,可以用 while,它可以控制每次读取的量

f.readline:一次读一行

相当于 for 循环,不指定默认每次读一行

f.readline(n):

t模式下:

​ 1)当 n 不超过一行字符个数时,读n 个字符

'''
你好 hello
haha
1111
'''
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    res = f.readline(2)
    print(res)  ##你好 t模式下read(n): n代表的是字符个数
    

​ 2)当 n超过一行字符个数时,读一行字符

'''
你好 hello
haha
1111
'''
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    res = f.readline(100)
    print(res)  #你好 hello
    						#空行(换行符)    							

b 模式下:

​ n 代表的是字节数

f.readlines:一次读所有行

f.readlines( ):默认读所有行,存放于列表中

'''
你好 hello
haha
1111
'''
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    res = f.readlines()
    print(res) #['你好 hello
', 'haha
', '1111
']

f.readlines( n): t 模式下的n 代表读的字符数

1)如果字符数没有超过一行的字符数,那么就读一行

2)如果字符数超过或等于一行的字符数,指针会读到换行符移至第二行,所以也会读取下一行内容,依次往下类推

'''
你好 hello
haha
1111
'''
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    res = f.readlines(1)
    print(res) #['你好 hello
']
    res = f.readlines(9) #读到了
    print(res) #['你好 hello
', 'haha
']
		res = f.readlines(13)
    print(res) #['你好 hello
', 'haha
']
    ['你好 hello
', 'haha
', '1111
']

f.read( n): b 模式下的n 代表读的字节数

with open('1.mp4',mode='rb') as f:
    while True:
        data=f.read(1024) # 同一时刻只读入1024个Bytes到内存中
        if len(data) == 0:
            break
        print(data)


强调:

f.read( )与 f.readlines( )都是将内容一次性读入内存,如果内容过大会导致内存溢出。

5、循环写入操作

f.writelines( 列表)

t 模式下写入时, 列表内部的元素必须为字符串类型,相当于一个 for 循环写入

with open('h.txt',mode='wt',encoding='utf-8') as f:
    # f.write('1111
222
3333
')

    # l=['11111
','2222','3333',4444] #列表中的有元素为不是字符串类型,所有写入是会报错
    l=['11111
','2222','3333']
    # for line in l:
    #     f.write(line)
    f.writelines(l)

b模式下写入时,必须要将字符串类型 进行encode成 ’ 硬盘类型编码格式’,否则 f.write( )报错

with open('h.txt', mode='wb')as f:
		l = [
		'111aa
'.encode('utf-8'), 
    '222bb
'.encode('utf-8'),
		]
    f.writelines(l)

补充:

#如果字符串是是纯数字,英文字符组成,可以直接加前缀b 得到 bytes 类型
l = [
  b'111aa
',
  b'222bb
'
]

#'上'.encode('utf-8')等同于 bytes('上', encoding='utf-8')
l = [
  bytes('上啊
',encoding='utf-8'),
  bytes('冲啊
',encoding='utf-8')
]

f.flush

立即将文件内容从内存存入硬盘

一般会用于文件测试

with open('h.txt', mode='wt',encoding='utf-8') as f:
    f.write('哈')
    # f.flush()

了解:

f.readable( ) :文件是否可读

f.writeable( ):文件是否可写

f.closed : 文件是否关闭

f.encoding :如果文件打开模式为 b,则没有该属性

f.flush( ): 立刻将文件内容从内存刷到硬盘

f.name: 文件名

with open('aaa.txt', mode='wt', encoding='utf-8') as f:
    print(f.readable()) #False  
    print(f.writable()) #True
    print(f.name) #aaa.txt
    print(f.encoding) #utf-8

六 控制文件指针操作

指针移动的单位都是以 bytes(字节)为单位,只有一种情况特殊:t模式下的 read( n) ,n 代表的是字符个数

'''
你好 hello
haha
1111
'''
with open('aaa.txt', mode='rt', encoding='utf-8') as f:
    res = f.read(4)
    print(res) #你好 h

1、f.seek:移动指针

f.seek( n,模式): n 指的是移动的字节个数

注:不管是否是在 t 或者 b 模式下,f.seek都是以字节个数移动

模式

模式 0:参照物是文件开头位置

 f.seek(9,0)
 f.seek(3,0) # 3

模式 1:参照物是当前指针所在位置

f.seek(9,1)
f.seek(3,1) # 12

模式2:参照物是文件末尾位置,应该倒着移动

f.seek(-9,2)#3
f.seek(-3,2)#9

强调

t模式下只能使用模式 0,而b 模式下可以使用模式 0,1,2

'''
你好 hello
haha
1111
'''

with open('aaa.txt', mode='rb') as f:
    f.seek(2, 0) #2
    f.seek(3, 0) #3
    print(f.tell()) #3
    res =f.read()
    print(res) #b'xe5xa5xbd hello
haha
1111'
    print(res.decode('utf-8')) 
    '''
    好 hello
    haha
    1111
    '''

Ps:硬盘压根没有修改操作,都是新数据完全覆盖旧数据;都是修改内存的数据保存覆盖硬盘原数据。

七 文件修改

文件修改的两种方式

方式一:文本编辑采用的就是这种方式

实现思路:将文件内容发一次性全部读入内存,然后在内存中修改完毕后再覆盖写回原文件

​ 优点:在文件修改过程中同一份数据只有一份(硬盘)

​ 缺点:会过多地占用内存

with open('c.txt',mode='rt',encoding='utf-8') as f:
    res=f.read()
    data=res.replace('alex','dsb')
    print(data)

with open('c.txt',mode='wt',encoding='utf-8') as f1:
    f1.write(data)

方式二:

​ 实现思路:以读的方式打开原文件,以写的方式打开一个临时文件,一行行读取原文件内容,修改完后写入临时文件...,删掉原文件,将临时文件重命名原文件名

​ 优点:不会占用过多的内存

​ 缺点: 在文件修改过程中同一份数据存了两份(硬盘)

with open('c.txt', mode='rt', encoding='utf-8') as f, 
        open('.c.txt.swap', mode='wt', encoding='utf-8') as f1:
    for line in f:
        f1.write(line.replace('alex', 'dsb'))

os.remove('c.txt')
os.rename('.c.txt.swap', 'c.txt')