python入门之:读写文件

读写文件

通过内置函数open()来打开一个文件,并返回文件对象,若无法被打开,则会抛出OSError。

文件内容

你好
张三
法外狂徒
林黛玉风雪山神庙

read

如果文件很小,read()一次性读取最方便

file = open("222.txt", mode="r", encoding="utf-8")
print(type(file)) #打印文件对象的类型
print(file.read()) #一次性读取文件所有的内容,这就注定它没办法读取大文件
file.close() #打开文件之后,一定要将文件关闭,否则会一直占用着内存

结果

<class '_io.TextIOWrapper'>
你好
张三
法外狂徒
林黛玉风雪山神庙

read之后剩下了什么?

with open("222.txt", mode="r", encoding="utf-8") as f:
    print("第一次read")
    print(f.read())
    print("第二次read")
    print(f.read())
第一次read
你好
张三
法外狂徒
林黛玉风雪山神庙

唐僧大战霸天虎
第二次read


Process finished with exit code 0

我们发现一个问题,第一次read完了之后,第二次啥也没读到,就可以想象成锅里有N个饼(从文件中获取内容到缓存中),一锅端到了盆里(从缓存中取出来打印出来),锅里就空了

with

好了,已经对读文件有一定认识了,但是我们要中断一下,上面我们读取文件的方式有弊端,就是我们必须在最后关闭文件,那下面两种情况,可能造成我们没有正常关闭文件,造成系统资源的浪费

1、调皮,就是忘了写了

2、打开了文件,但是在close之前程序报错了,执行不到close了

第一种还好,人眼校验,第二点就难受了呀,有一种解决办法就是通过try…finally的方式解决,但是还是很繁琐的,为了解决这个问题,引入了with,下面这种方式会自动帮我们调用close

with open("222.txt", mode="r", encoding="utf-8") as f:
    print(type(f))
    print(f.read())
<class '_io.TextIOWrapper'>
你好
张三
法外狂徒
林黛玉风雪山神庙

是不是和第一个例子是一样的,以后我们就这么写了,不要按照之前的方式写了。

read(size)

调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容

如果不能确定文件大小,反复调用read(size)比较保险

至于怎么使用,说实在的我在实际使用中也没用过这个,因为平时读取比较多的是配置文件,但是抄来了一份答案

def readlines(f, separator):
  '''
  读取大文件方法
  :param f:  文件句柄
  :param separator:  每一行的分隔符
  :return:
  '''
  buf = ''
  while True:
    while separator in buf:
      position = buf.index(separator) # 分隔符的位置
      yield buf[:position] # 切片, 从开始位置到分隔符位置
      buf = buf[position + len(separator):] # 再切片,将yield的数据切掉,保留剩下的数据

    chunk = f.read(4096) # 一次读取4096的数据到buf中
    if not chunk: # 如果没有读到数据
      yield buf # 返回buf中的数据
      break # 结束
    buf += chunk # 如果read有数据 ,将read到的数据加入到buf中


with open('text.txt',encoding='utf-8') as f:
  for line in readlines(f,'|||'):
    # 为什么readlines函数能够使用for循环遍历呢, 因为这个函数里面有yield关键字呀, 有它就是一个生成器函数 ......
    print(line)

readline

调用readline()可以每次读取一行内容

with open("222.txt", mode="r", encoding="utf-8") as f:
    print(type(f))
    print(f.readline())
<class '_io.TextIOWrapper'>
你好

我们发现,这个readline只能读取一行啊,那我们怎么读取多行呢。

在读取文件所有内容之前,我们来升级一下文件,中间加入一个空行

你好
张三
法外狂徒
林黛玉风雪山神庙

唐僧大战霸天虎
with open("222.txt", mode="r", encoding="utf-8") as f:
    done = 0
    while not done:  # 0为False,not False = True
        line = f.readline()
        if line != "":  # 如果读取的内容不为空
            print(line.strip())  # 打印这一行的内容,strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)
        else:
            done = 1  # 如果readline读取的内容为空了,就结束循环
你好
张三
法外狂徒
林黛玉风雪山神庙

唐僧大战霸天虎

这里可能很多人有疑问,是不是遇到一个空行,也会被认为是文件的结束呢?事实上,文件的空白行并不会返回一个空行。因为在每一行的末尾还有一个或者多个分隔符,因此“空白行”至少会有一个换行符或者系统使用的其他符号。所以,即使文件中真的包含一个“空白行”,读入的行也不是空的,这就意味着在真实遍历读取到文件结束之前,程序实际上是不会停止的。

readlines

调用readlines()一次读取所有内容并按行返回list

如果是配置文件,调用readlines()最方便

with open("222.txt", mode="r", encoding="utf-8") as f:
    for i in f.readlines():
        print(i.strip())
    print(type(f.readlines()))
你好
张三
法外狂徒
林黛玉风雪山神庙

唐僧大战霸天虎
<class 'list'>
with open("222.txt", mode="r", encoding="utf-8") as f:
    print(f.readlines())
['你好\n', '张三\n', '法外狂徒\n', '林黛玉风雪山神庙\n', '\n', '唐僧大战霸天虎']

我们发现,换行是\n,空白行也是\n,读取出来的空白行并不是""

for line in f.readlines():
    print(line.strip()) # 把末尾的'\n'删掉

rb和encoding

python3里面所有的字符都是utf-8的形式,如果在打开一个文件的时候我不知道这个编码怎么办?

那我们就不指定encoding的编码,那么Python3默认就是utf8但是这样也不行,同时我们要改变读的模式,才可以,r就是文本模式,可以直接读取字符串的,如果用户不知道文件的格式的话可以不指定编码格式,同时直接使用rb的模式,就是硬盘怎么存储的你就怎么存到内存,直接以二进制的形式,就可以了

错误示范

with open("one.jpg", mode="r") as f:
    print(f.readlines())

报错

Traceback (most recent call last):
  File "/Users/zc/PycharmProjects/pythonProject1/test/MyOne.py", line 2, in <module>
    print(f.readlines())
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/codecs.py", line 322, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

正确打开方式

with open("one.jpg", mode="rb") as f:
    print(f.readlines())

一个图片通过二进制文件打开也是很大的,就只展示出一部分算了

d7\xc4\xa5\x92\xbe]4H\x06\x9d8\xa6Aa\x8d\x15b%\xa3tD\x84\x8fUL\xa1F\xb7\x95\xc7\xf6G\xf4\xf6\xa0\x96T\x0b\xe7\xd5Y\xdbN:Vm\xac\xbd2\xe4`\xa6\x9eS\xea\x93J>\xb0\xaa\xd5\x04\x1d1)\xfa\xf8\xd3\x9b\xff\x00\xaf\xed|\x12+\xfe\x93\xf9\x0c}\xbd*\x82M/W\xe1\xe5\xf6\xf4\xae\x97r\xd6\xe2e\x9a\xb9*\x8a\x1aZ\x91\x03\x95vT}\x12i\x88\xd8\x8e}#\x8f\xf6\xfe\xdcm\xd2{5\x0c~ ~\xdf\xe5\xd2\xcf\x0c:\x96\x93$\xff\x00\x83\xfc\xfd\x0f;
。。。。。。。

该何如知道文件的编码格式呢?

chardet!

安装命令

pip3 install chardet

使用

import chardet

result = chardet.detect(open("222.txt", mode="rb").read())
print(result)
{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}

遇到不规范文件怎么办

遇到有些编码不规范的文件,你可能会遇到UnicodeDecodeError,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open()函数还接收一个errors参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略

反面教材,使用gbk编码读取utf-8编码文件

with open("222.txt", mode="r", encoding="gbk") as f:
    print(f.read())

报错

Traceback (most recent call last):
  File "/Users/zc/PycharmProjects/pythonProject1/test/MyOne.py", line 2, in <module>
    print(f.read())
UnicodeDecodeError: 'gbk' codec can't decode byte 0xa4 in position 18: illegal multibyte sequence

如果我们忽略报错呢

with open("222.txt", mode="r", encoding="gbk", errors='ignore') as f:
    print(f.read())

结果

浣犲ソ
寮犱笁
娉曞栫媯寰
鏋楅粵鐜夐庨洩灞辩炲簷

鍞愬儳澶ф垬闇稿ぉ铏

发现虽然编码不对,但至少不报错了,没有中断程序

write

with open("222.txt", mode="r") as f:
    print("文件被写之前")
    print(f.read())

with open("222.txt", mode="w") as f:
    f.write("为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢")

with open("222.txt", mode="r") as f:
    print("文件被写之后")
    print(f.read())

结果

文件被写之前
你好
张三
法外狂徒
林黛玉风雪山神庙

唐僧大战霸天虎
文件被写之后
为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢

我们发现,文件被覆盖了呀,其实

w不是修改,是创建了一个新的文件名字,如果和原来的旧文件有名字一样,原来的文件就是清空,如果是文件名字不一样就是新建,所以我们要是小心使用:w

追加模式

啊,覆盖了我的文件,我不想啊,我只想在文件最后添加内容,那我们就需要使用mode=“a”

with open("222.txt", mode="r") as f:
    print("文件被写之前")
    print(f.read())

with open("222.txt", mode="a") as f:
    f.write("这次是追加的内容了")

with open("222.txt", mode="r") as f:
    print("文件被写之后")
    print(f.read())
文件被写之前
为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢
文件被写之后
为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢这次是追加的内容了

我们发现直接加到了文件的最后,而且是同行展示,想要跨行展示,只需要将

```

f.write(“这次是追加的内容了”)
```

修改成

f.write("\n这次是追加的内容了")

writelines

a = ["\n", "重生之我是刘姥姥\n", "刘姥姥之宝玉爱上我"]
with open("222.txt", mode="r") as f:
    print("文件被写之前")
    print(f.read())
with open("222.txt", mode="a") as f:
    f.writelines(a)
with open("222.txt", mode="r") as f:
    print("文件被写之后")
    print(f.read())
文件被写之前
为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢
文件被写之后
为什么陨石总是掉在陨石坑里,这么准,这个坑是谁挖的呢
重生之我是刘姥姥
刘姥姥之宝玉爱上我

文件路径

文件路径分为相对路径和绝对路径,相对路径是指一个文件或文件夹所在路径与其它文件或文件夹的路径关系,而绝对路径是指从盘符(即磁盘区)到当前位置的路径。

判断文件或文件夹的路径是否为绝对路径

import os
print(os.path.isabs("222.txt"))
print(os.path.isabs("/Users/zc/PycharmProjects/pythonProject1/test/222.txt"))
False
True

获取文件绝对路径

import os
print(os.path.abspath("222.txt"))
/Users/zc/PycharmProjects/pythonProject1/test/222.txt

获取当前路径

import os
path1 = os.getcwd()
path2 = os.path.dirname(__file__)  #更常用
print(path1)
print(path2)
/Users/zc/PycharmProjects/pythonProject1/test
/Users/zc/PycharmProjects/pythonProject1/test

判断路径是否存在

import os
print(os.path.exists("/Users/zc/PycharmProjects/pythonProject1/test"))
print(os.path.exists("/Users/zc/PycharmProjects/pythonProject1/tes2"))
True
False

返回所在路径

如果入参是路径,那就是返回上一层

import os
path1 = os.getcwd()
print(path1)
path2 = os.path.dirname(path1)
print(path2)
/Users/zc/PycharmProjects/pythonProject1/test
/Users/zc/PycharmProjects/pythonProject1

如果入参是文件,那就返回文件的路径

import os
path1 = "/Users/zc/PycharmProjects/pythonProject1/test/MyOne.py"
path2 = os.path.dirname(path1)
print(path2)
/Users/zc/PycharmProjects/pythonProject1/test

拼接路径

import os

path1 = os.path.dirname(__file__)
path2 = os.path.join(path1, "112.txt")
path3 = os.path.join(path1, "222.txt")
print(path2)
print(path3)
/Users/zc/PycharmProjects/pythonProject1/test/112.txt
/Users/zc/PycharmProjects/pythonProject1/test/222.txt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值