# 文件

# 文件打开

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
1

# 参数file

这是文件的路径。
windows是"\"用来做路径分割,Mac和linux是以"/"用来做路径分割。
Windows和Mac对文件名和文件夹名的大小写不敏感,而linux对大小写敏感。

# 参数mode

指示以什么操作方式打开文件。它的操作符如下:

操作符 含义
'r' open for reading (default)
'w' open for writing, truncating the file first
'x' create a new file and open it for writing
'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 (deprecated)

常用的有:

  • r+ 读+写
  • b 二进制模式打开
  • a 文件后面追加

# 参数encoding

它规定以什么编码格式打开文件。

encoding is the name of the encoding used to decode or encode the file. This should only be used in text mode. The default encoding is platform dependent, but any encoding supported by Python can be passed. See the codecs module for the list of supported encodings.

从上面解释可以看出,默认编码是取决于平台的。那么我们应该怎么查看自己系统的默认编码呢?

import locale
print(locale.getpreferredencoding())
1
2

在我的windows上运行得到结果:cp936。这个cp936其实就是GBK,IBM在发明Code Page的时候将GBK放到第936页,因此叫cp936。

有时候我们在用python打开utf-8格式的文件的时候就弹出这个错:

UnicodeDecodeError: 'gbk' codec can't decode byte 0xac in position 67: illegal multibyte sequence

这个时候我们便可以理解为什么是用gbk进行解码,为什么会出错了。这个时候我们在打开文件的时候用encoding告诉python以utf-8格式打开即可。

# 参数buffering

该参数有以下几个值:

  • 负数:代表使用默认的缓冲区大小
  • 0:以不带缓冲区的方式打开文件
  • 1:以带缓冲区的方式打开文件,但是IO经过优化了

# 注意点

open 函数可以打开不存在的文件,但是文件所在目录如果不存在的话,会报错。我们打开文件前需要先检查目录是否存在,如果不存在则创建再打开。看实例:

import os

tFolder='test'

if not os.path.exists(tFolder):
    os.makedirs(tFolder)
    #如果要覆盖已有文件,请添加参数exist_ok=True
else:
    print("已经存在这个目录!")

f=open(tFolder+r'\hello.txt','w')
print("编码:%s" % f.encoding)
print("权限:%s" % f.mode)
print("是否关闭:%s" % f.closed)
print("文件名字:%s" % f.name)

f.write("hello world")
f.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 文件关闭

文件操作符.close()
1

有时候我们容易忘记使用close()关闭文件,建议使用这种操作方式打开文件:

with open(file,mode,...) as foperator:
    operation
1
2

with语句管理的资源必须是一个实现上下文管理协议的类,也就是该类需要实现__enter__()和__exit__()两个方法。

# 文本文件操作方法

文件操作符.readline()
文件操作符.readlines()

文件操作符.write()
文件操作符.writelines()
1
2
3
4
5

注意调用上述命令并不会立即把内容写入文件,它只是写入了缓存区。调用close()或者flush()将缓存区内容写入文件。

实例:

f=open(r'hello.txt','r+')
content=f.readlines()
num=0
f.write('\n')
for l in content:
    l = '%s : %s' % (num,l)
    num += 1
    f.write(l)
f.close()

f=open(r'hello.txt','r+')
content=f.readlines()
newcontent=['\n']
num=0
for l in content:
    l = '%s : %s' % (num,l)
    num += 1
    newcontent.append(l)
f.writelines(newcontent)
f.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

可以使用linecache读取某一行:

import linecache
import random
print(linecache.getline(random.__file__,3))
1
2
3

按字节或字符读取:

import os
f = open("temp.txt",'w+')
f.write("hello:"+os.linesep)
f.writelines(("this ","is "+os.linesep,"future"))
f.close()

f = open("temp.txt","r")
while True:
    # read时如果不传入参数,该方法默认会读取全部文件内容。
    char = f.read(1)
    if not char : break
    print(char,end=" ")
f.close()

# 也可以写成:
f = open("temp.txt","r")
try:
    while True:
        char = f.read(1)
        if not char : break
        print(char,end=" ")
finally:
    f.close()

# 还可以写成:
with open("temp.txt","r") as f:
    for line in f:
        print(line,end="")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

可以将多个文件合并到一块读:

import fileinput
for line in fileinput.input(files=('temp.txt','test.txt')):
    print(fileinput.filename(),fileinput.filelineno(),line,end="")
fileinput.close()
1
2
3
4

下面是更改文件指针位置的方法:

文件操作符.tell()
文件操作符.seek(offset,fromWhere)
1
2

tell是告诉当前文件指针所在位置
seek是改变位置,fromWhere等于0从文件头计算偏移量,等于1从当前文件指针位置计算偏移量,等于2从文件末尾计算偏移量。在文本文件中,只支持fromWhere=0,等于其他两种情况会报错。

实例:

f=open(r'hello.txt','r+')
print(f.tell())
content=f.readline()
print("第一行字符个数:%s" % len(content))
print("当前指针位置:%s" % f.tell())
print(len('\r\n'))
f.seek(44)
print(f.readline())
f.close()
1
2
3
4
5
6
7
8
9

假设hello.txt第一行是hi,那么会输出

0
第一行字符个数:3
当前指针位置:4
2

为什么会输出这个结果呢?因为windows下的换行符是\r\n,而Mac和linux下的换行符却是\n,在使用readline()读入第一行的时候会自动去掉\r。

# 二进制文件操作方法

f=open(r'hello.txt','rb+')
content=f.readlines()
num=0
f.write('\n'.encode())
for l in content:
    l = '%s : %s' % (num,l.decode('gbk'))
    print(l)
    num += 1
    f.write(l.encode('gbk'))
f.close()
1
2
3
4
5
6
7
8
9
10

我们注意到上边我们以二进制文件将内容读出,转换成文本,进行操作,再转换成二进制,这个过程,encode()和decode()如果没有参数指定默认是utf-8。

从上边发现我们要操作一个文件先要知道文件的编码,那么我们应该如何知道文件的编码呢?可以使用chardet:

import chardet
with open(r'hello.txt', 'rb') as fp:
    result = chardet.detect(fp.read())
    print(result)
1
2
3
4

# 目录

# pathlib

pathlib模块提供了一组关于面向对象的类:

  • PurePath:并不访问文件系统的真正路径,只是字符串上的代表,至于该字符串并不一定是真的实际路径。
  • Path:继承自PurePath,访问文件系统的真正路径,可进行判断文件是否存在等实际操作。

# PurePath

PurePath只代表路径字符串,本质上就是字符串。

from pathlib import *
pp1 = PurePath("c:/","windows/nt","win32")
print(type(pp1))
print("pp1:%s" % pp1)

#不传参数获,相当于传入.
print(PurePath())

#仅最后的绝对路径有效
pp2 = PurePosixPath("/root","/home","file")
print("pp2:%s" % pp2)
pp3 = PureWindowsPath("c:/windows","d:temp")
print("pp3:%s" % pp3)

print(PureWindowsPath("test")==PureWindowsPath("TEST"))
print(PureWindowsPath("test")==PurePosixPath("TEST"))

# 使用/将多个路径连接起来
pp4 = PurePath("workfolder")
print("合并pp1,pp4路径:%s" % pp1/pp4/"testfile")

print("返回pp1路径的各个部分:%s" % (pp1.parts,))
print("返回pp1路径的盘符:%s" % pp1.drive)
print("返回指定路径的根路径:%s" % pp2.root)
print("返回指定路径的父路径:%s" % pp2.parent)
print("返回指定路径的父路径0:%s" % pp2.parents[0])
print("返回指定路径的父路径1:%s" % pp2.parents[1])
print("返回路径字符串中的盘符和根路径:%s" % pp3.anchor)
print("返回路径字符串中的盘符和根路径:%s" % pp1.name)

pp5 = PurePath("temp/test.tar.gz")
print("文件名:%s" % pp5.stem)
print("默认后缀:%s" % pp5.suffix)
print("后缀0:%s" % pp5.suffixes[0])
print("后缀1:%s" % pp5.suffixes[1])

print("将pp1转换为posix风格:%s" % pp1.as_posix())
print("将绝对路径转换为uri:%s" % pp1.as_uri())
print("判断pp1是否是绝对路径:%s" % pp1.is_absolute())

print("判断pp1是否匹配路径nt/*:%s" % pp1.match("nt/*"))

print("判断pp1相对于c:/windows的目录: %s" % pp1.relative_to("c:/windows"))

print("替换pp5中的文件名:%s" % pp5.with_name("newName.tar.gz"))
print("替换pp5中的后缀:%s" % pp5.with_suffix(".zip"))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# Path

Path继承自PurePath,除了路径字符串的各种操作外,它还会真正访问底层文件系统,并进行各种操作。

from pathlib import *

path1 = Path('.')

for x in path1.iterdir():
    print(x)

print("当前目录下的所有python文件:")
for x in path1.glob('*.py'):
    print(x)

# 读写文本文件
targetFile = Path('temp.txt')
result = targetFile.write_text("自信的人生不需要解释",encoding='GBK')
print(targetFile.read_text(encoding='GBK'))

# 读写二进制文件
targetBytesFile = Path('BytesFile.txt')
sourceStr="你好,世界"
targetBytesFile.write_bytes(bytes(sourceStr,encoding="utf8"))
print(str(targetBytesFile.read_bytes(),encoding="utf8"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

上面的glob只能进行简单模式的匹配,fnmatch模块可以支持更复杂的匹配机制,支持unix shell风格的文件名。

from pathlib import *
import fnmatch

for file in Path(".").iterdir():
    if fnmatch.fnmatch(file,"*mp.py"):
        print(file)
    # 区分大小写

if fnmatch.fnmatchcase("temp.py","*Mp.py"):
        print("yes")
    
print(fnmatch.filter(["a.py","b.py","c.py"],"[ac].py"))

# 将unix风格的模式转换为正则表达式模式
print(fnmatch.translate("[ac].py"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# os模块

os模块中有一些对目录,文件,以及权限相关的操作。

# 目录相关

import os

print("获取文件的绝对路径:%s" % os.path.abspath("temp.txt"))
print("获取当前目录的绝对路径:%s" % os.path.abspath("."))

# 获取路径前缀是按字符串来
print("获取两个目录的共同路径:%s" % os.path.commonprefix(["C:\windows\win32","C:/windows/gamepad"]))
# 在获取共同路径前先进行处理
print("获取两个目录的共同路径:%s" % os.path.commonpath(["C:\windows\win32","C:/windows/gamepad"]))

print("获取路径中的目录:%s" % os.path.dirname("c:/windows/system32/notepad.exe"))
print("指定路径是否存在:%s" % os.path.exists("c:/windows/system32/notepad.exe"))

import time

print("最近一次访问时间:%s" % time.ctime(os.path.getatime('temp.py')))
print("最后一次修改时间:%s" % time.ctime(os.path.getmtime('temp.py')))
print("创建时间:%s" % time.ctime(os.path.getctime('temp.py')))

print("获取文件大小:%s" % os.path.getsize("temp.py"))
print("是否为文件:%s" % os.path.isfile("temp.py"))
print("是否为目录:%s" % os.path.isdir("."))
print("是否为同一个文件:%s" % os.path.samefile("temp.py","./temp.py"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

此外该模块还提供一些直接操作目录的函数:

import os

print("当前工作目录:%s" % os.getcwd())
print("列出当前目录内容:")
for one in os.listdir():
    print(one)

os.chdir("../")
print("跳到上级目录:%s" % os.getcwd())

print("创建目录:")
targetPath="MyTest"
#os.rmdir(targetPath)
import shutil
if os.path.exists("MyTest1"):
    os.removedirs("MyTest1/test2/t2")

if os.path.exists(targetPath):
    shutil.rmtree(targetPath)

os.mkdir(targetPath,0o755)

os.makedirs(targetPath+"/test/t1",0o755)
os.rename(targetPath,"MyTest1")
os.renames("MyTest1/test/t1","MyTest1/test2/t2")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 权限相关

来看下如何查看权限以及更改权限:

import os,sys,stat

# 检查当前权限
print(os.access("temp.py",os.F_OK | os.R_OK | os.W_OK | os.X_OK))

# 更改文件
os.chmod("temp.txt",stat.S_IRUSR)
1
2
3
4
5
6
7

# 文件相关

简单了解下和文件操作有关的函数即可:

import os

f = os.open("temp.txt",os.O_RDWR | os.O_CREAT)

contentLen = os.write(f,"世界你好".encode("utf-8"))

# 将文件指针重置在文件头
os.lseek(f,0,os.SEEK_SET)

rawData = os.read(f,contentLen)
print(rawData)
print(rawData.decode("utf-8"))
os.close(f)

# 创建快捷方式
os.symlink("temp.txt","temp1")
# 创建硬链接,windows下是复制文件
os.link("temp.txt","temp2")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# tempfile模块

该模块专门用于创建临时文件和目录。

import tempfile
import os

# 将临时目录制定为当前目录
tempfile.tempdir="."
with tempfile.TemporaryFile() as fp:
    print(fp.name)
    fp.write(b"hello world!")
    fp.flush()
    #os.system("pause")
    input("暂停")

with tempfile.TemporaryDirectory() as tmpDir:
    #os.system("pause")
    print(tempfile.gettempdir())
    input("暂停")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16