器→工具, 术→技巧, 研发, 编程语言

Python文件的读写操作

钱魏Way · · 467 次浏览

使用Python编程时,经常会遇到读写文件的操作。对于读写文件的各种模式(如阅读、写入、追加等)有时真的会迷惑人,以及搞不清open、read、readline、readlines、write、writelines等方法的使用也会把你绕的云里雾里。

期望这篇文章能够帮你更好的了解应该如何读写文件,并在最恰当的地方用最合适的方法。

什么是文件?

在我们开始研究如何使用Python中的文件之前,了解文件究竟是什么以及现代操作系统如何处理它们的某些方面是非常重要的。

从本质上讲,文件是用于存储数据的连续字节集。这些数据以特定格式组织,可以是任何像文本文件一样简单的数据,也可以像程序可执行文件一样复杂。最后,这些字节文件被翻译成二进制文件1,0以便计算机更容易处理。

大多数现代文件系统上的文件由三个主要部分组成:

  • 标题(Header):有关文件内容的元数据(文件名,大小,类型等)
  • 数据(Data): 由创建者或编辑者编写的文件内容
  • 文件结束(EOF):表示文件结尾的特殊字符

数据表示的内容取决于所使用的格式规范,通常由扩展名表示。例如,扩展名为.gif的文件最可能符合图形交换格式规范。有数百个(如果不是数千个)文件扩展名

文件路径

在操作系统上访问文件时,需要文件路径。文件路径是表示文件位置的字符串。它分为三个主要部分:

  • 文件夹路径:文件系统上的文件夹位置,后续文件夹由正斜杠/(Unix)或反斜杠\(Windows)分隔
  • 文件名:文件的实际名称
  • 扩展名:文件路径的末尾预先设置了句号(.),用于表示文件类型

Python文件路径相关操作:

  • 使用getcwd()可获得当前工作目录,即当前Python脚本工作的目录路径,类似Linux中pwd命令
    • 如果想打印Windows中包含中文的文件名或路径,需要使用“GBK”进行decode
  • 合并路径使用path.join()
    • Windows中的反斜杠与Linux中的正斜杠使用sep(反斜杠需要使用\\转义)
  • 相对路径
    • (.)点表示当前文件夹
    • (.. )点点表示父文件夹
  • 相对路径、绝对路径转换
    • path.abspath(path):返回绝对路径
    • path.isabs(path):判断是否是绝对路径
    • path.relpath(path,start):返回相对路径
  • 路径分割:
  • path.dirname(path):返回文件所在目录,os.path.basename(path):返回文件名
  • path.split(path) == (os.path.dirname(path), os.path.basename(path)).silit(os.sep)是将路径按分隔符分割
  • path.basename():获取文件名
  • path.splitext():分离扩展名
  • 查看文件大小:path.getsize(filename),只能统计文件,不同统计文件夹,如需统计文件夹需要自行遍历。
  • 获取文件属性:stat(file)
  • 检查路径有效性
    • path.exists:路径是否存在
    • path.isdir:是否为目录
    • path.isfile:是否是文件
  • 返回指定目录下的所有文件和目录名:listdir()

其他方法:

  • 删除文件:os.remove()
  • 删除目录:removedirs(r”c:\python “)
  • 重命名:rename(old, new) #文件或目录都是使用这条命令
  • 创建多级目录:makedirs(r”c:\python\test”),其会创建中间文件夹,类似Linux命令 mkdir -p
  • 创建单个目录:mkdir(“test”)
  • 修改文件权限与时间戳:chmod(file)

目录操作:

  • 复制文件:
    • copyfile(“oldfile”, “newfile”) #oldfile和newfile都只能是文件
    • copy(“oldfile”, “newfile”) #oldfile只能是文件夹,newfile可以是文件,也可以是目标目录
  • 复制文件夹:copytree(“olddir”, “newdir”) #olddir和newdir都只能是目录,且newdir必须不存在
  • 移动文件(目录):move(“oldpos”,”newpos”)
  • 删除目录
    • rmdir(“dir”) #只能删除空目录
    • rmtree(“dir”) #空目录、有内容的目录都可以删

行结尾

处理文件数据时经常遇到的一个问题是新行或行结尾的表示。行结尾起源于莫尔斯电码时代,使用一个特定的符号被用来表示传输的结束或一行的结尾。

后来,国际标准化组织(ISO)和美国标准协会(ASA)对电传打字机进行了标准化。ASA标准规定行尾应使用回车(序列CR或\r)和换行(LF或\n)字符(CR+LF或\r\n)。然而,ISO标准允许CR+LF字符或仅LF字符。

Windows使用CR+LF字符表示新行,而Unix和较新的Mac版本仅使用LF字符。当你处理来源于不同操作系统上的文件时,这可能会导致一些复杂情况。这是一个简单的例子。假设我们检查在Windows系统上创建的文件dog_breeds.txt:

Pug\r\n
Jack Russel Terrier\r\n
English Springer Spaniel\r\n
German Shepherd\r\n
Staffordshire Bull Terrier\r\n
Cavalier King Charles Spaniel\r\n
Golden Retriever\r\n
West Highland White Terrier\r\n
Boxer\r\n
Border Terrier\r\n

同样的输出将在Unix设备上以不同方式解释:

Pug\r
\n
Jack Russel Terrier\r
\n
English Springer Spaniel\r
\n
German Shepherd\r
\n
Staffordshire Bull Terrier\r
\n
Cavalier King Charles Spaniel\r
\n
Golden Retriever\r
\n
West Highland White Terrier\r
\n
Boxer\r
\n
Border Terrier\r
\n

解决方案:

with open('test.txt', 'r') as f:
    for line in f.readlines():
    line = line.strip('\n')

Python遍历文件夹方法

使用walk()

输出总是先文件夹后文件名

# -*- coding: utf-8 -*- 
import os 
def echo_path(root_dir): 
    list_dirs = os.walk(root_dir) 
    for root, dirs, files in list_dirs: 
        for d in dirs: 
            print os.path.join(root, d)
        for f in files: 
            print os.path.join(root, f)

使用listdir

按照目录树结构以及按照首字母排序进行输出的。

# -*- coding: utf-8 -*- 
import os 

def echo_path(root_dir): 
    for dir_list in os.listdir(root_dir): 
        path = os.path.join(root_dir, dir_list) 
        print(path)
        if os.path.isdir(path): 
            echo_path(path)

glob模块

glob模块是最简单的模块之一,内容非常少。用它可以查找符合特定规则的文件路径名。

glob.glob

返回所有匹配的文件路径列表。它只有一个参数pathname,定义了文件路径匹配规则,这里可以是绝对路径,也可以是相对路径。示例:

import glob  

#获取指定目录下的所有图片  
print(glob.glob(r"E:\Picture\*\*.jpg") )

#获取上级目录的所有.py文件  
print(glob.glob(r'../*.py') #相对路径)

glob.iglob

获取一个可编历对象,使用它可以逐个获取匹配的文件路径名。与glob.glob()的区别是:glob.glob同时获取所有的匹配路径,而 glob.iglob一次只获取一个匹配路径,即生成器。

import glob

file = glob.iglob(r'../*.py')
print(file) #<generator object iglob at 0x00B9FF80> 

for py in f:
    print(py)

Python读写文件内容

使用with语句打开文件

在Python中读写文件需要3个步骤:

  • 调用open函数,返回一个File对象
  • 调用File对象的read()或write()方法
  • 调用File对象的close() 方法,关闭该文件

文件常用打开模式:

  • ‘r’:只读(缺省。如果文件不存在,则抛出错误)
  • ‘w’:只写(如果文件不存在,则自动创建文件)
  • ‘a’:附加到文件末尾
  • ‘r+’:读写

如果需要以二进制方式打开文件,需要在mode后面加上字符”b”,比如”rb””wb”等

如果不用with语句,代码如下:

file = open("/tmp/foo.txt")
data = file.read()
file.close()

这里有两个问题:

  • 可能忘记关闭文件句柄;
  • 文件读取数据发生异常,没有进行任何处理。

加强版代码:

try:
    f = open('xxx')
except:
    print 'fail to open'
    exit(-1)
try:
    do something
except:
    do something
finally:
     f.close()

虽然这段代码运行良好,但是太冗长了。with除了有更优雅的语法,还可以很好的处理上下文环境产生的异常。

with版本的代码:

with open("/tmp/foo.txt") as file:
    data = file.read()

with的工作流程:

  • 紧跟with后面的语句被求值后,返回对象的 __enter__() 方法被调用,这个方法的返回值将被赋值给as后面的变量。
  • 当with后面的代码块全部被执行完之后,将调用前面返回对象的 __exit__()方法。

有时你可能想要读取文件并同时写入另一个文件。如果你使用在学习如何写入文件时显示的示例,它实际上可以合并到以下内容中:

d_path = 'dog_breeds.txt'
d_r_path = 'dog_breeds_reversed.txt'
with open(d_path, 'r') as reader, open(d_r_path, 'w') as writer:
    dog_breeds = reader.readlines()
    writer.writelines(reversed(dog_breeds))

文本读取:read()、readline()、readlines()的区别

read()

read()是最简单的一种方法,一次性读取文件的所有内容放在一个大字符串中,即存在内存中。

f = open('test.txt') 
try:
    file_context = file_object.read() 
    file_context = open(file).read().splitlines()   // file_context是一个list,每行文本内容是list中的一个元素
finally:
    file_object.close()

read()的优点:

  • 方便、简单
  • 一次性独读出文件放在一个大字符串中,速度最快

read()的缺点:

  • 文件过大的时候,占用内存会过大

readline()

readline()是逐行读取文本,结果是一个list

with open(file) as f:
    line = f.readline()
    while line:
        print(line)
        line = f.readline()

readline()的优点:

  • 占用内存小,逐行读取

readline()的缺点:

  • 由于是逐行读取,速度比较慢

readlines()

readlines()一次性读取文本的所有内容,结果是一个list

with open(file) as f:
  for line in f.readlines():
     print(line)

这种方法读取的文本内容,每行文本末尾都会带一个’\n’换行符 (可以使用L.rstrip(‘\n’)去掉换行符)

readlines()的优点:

  • 一次性读取文本内容,速度比较快

readlines()的缺点:

  • 随着文本的增大,占用内存会越来越多

文本写入:write()与writelines()

  • write()传入的是字符串
  • writelines()传入的数一个数组
lines = ['line1', 'line2']
with open('filename.txt', 'w') as f:
    f.write('\n'.join(lines))

lines = ['line1', 'line2']
with open('filename.txt', 'w') as f:
    f.writelines("%s\n" % l for l in lines)

不要重复造轮子

在处理文件时可能会遇到常见情况。大多数情况可以使用其他模块处理。您可能需要使用的两种常见文件类型是.csv和.json。

此外,还有内置库,可以使用它们来帮助你:

  • wave:读写WAV文件(音频)
  • aifc:读写AIFF和AIFC文件(音频)
  • sunau:读取和写入Sun AU文件
  • tarfile:读取和写入tar归档文件
  • zipfile:使用ZIP存档
  • configparser:轻松创建和解析配置文件
  • msilib:读取和写入Microsoft Installer文件
  • plistlib:生成并解析Mac OS X .plist文件

还有更多的东西。此外,PyPI还有更多第三方工具可用。以下是一些流行的包:

  • PyPDF2:一个可以分割,合并和转换 PDF 页面的库。
  • Pillow:图像阅读和操作
  • python-docx:读取,查询以及修改 Microsoft Word 2007+ docx 文件
  • python-pptx:读取,查询以及修改 Microsoft PowerPoint 2007+ pptx 文件
  • unp:一个用来方便解包归档文件的命令行工具

参考链接:

发表评论

邮箱地址不会被公开。 必填项已用*标注