1. 写在前面
今天整理几个python关于文件操作的几个案例, 主要包括用python进行文件的读写, 获取文件名, 获取文件的后缀名和批量修改文件的后缀。这次整理的几个方法依然都是从使用的角度出发。比如我需要统计某个txt文件中每个单词的出现次数, 再比如我需要批量的修改文件的后缀等。 以后涉及到python的文件读写操作的内容, 会继续在这篇文章中进行添加。
Ok, let’s go!
2. python进行文件的读写
文件的读写在python中比较常见, 文件读取的时候, 首先要判断文件是否存在, 若文件操作就进行读取, 否则报错。
2.1 文件读操作
文件读操作可以的函数有read(), 这个是读取整个文件, 还有readlines()这个是一行行的进行读取并保存到列表中。 下面直接上代码:
import os
def read_file(filename):
if os.path.exists(filename) is False:
raise FileNotFoundError('%s not exists' % (filename,))
f = open(filename, encoding='utf-8') # 后面那个是指定编码 一般平台默认编码格式是utf-8
content = f.read()
f.close()
return content
# 另一种写法
def read_file(filename):
if os.path.exists(filename) is False:
raise FileNotFoundError('%s not exists' % (filename,))
with open(filename, encoding='utf-8') as f:
content = f.read() # 这时候读完了之后自动关闭
return content
content = read_file('a.txt')
content
##
'Hey, Python\n\nI just love Python so much,\nand want to get the whole Python stack by this 60-days column\nand believe Python !'
下面是按行读的效果:
"""文件按行读"""
def read_file_line(filename):
if not os.path.exists(filename):
raise FileNotFoundError('%s not exists' % filename)
with open(filename, encoding='utf-8') as f:
content = f.readlines()
return content
con = read_file_line('a.txt')
con
##
['Hey, Python\n',
'\n',
'I just love Python so much,\n',
'and want to get the whole Python stack by this 60-days column\n',
'and believe Python !']
好了, 基于上面的这个就可以写一个统计文件里面所有单词的出现次数了。 先走一遍这个过程: 我获取到文件的内容, 然后进行分词, 分开之后才能统计个数。
# 下面是一个统计单词个数的例子
from collections import defaultdict
#这里的defaultdict(function_factory)构建的是一个类似dictionary的对象,
#其中keys的值,自行确定赋值,但是values的类型,是function_factory的类实例,
#而且具有默认值。比如default(int)则创建一个类似dictionary对象,里面任何的values都是int的实例,
#而且就算是一个不存在的key, d[key] 也有一个默认值,这个默认值是int()的默认值0.
import re
rec = re.compile('\s+') # 匹配一个或者多个空格
dd = defaultdict(int)
with open('a.txt', 'r+') as f:
for line in f:
clean_line = line.strip()
if clean_line:
words = rec.split(clean_line)
for word in words:
dd[word] += 1
dd = sorted(dd.items(), key=lambda x: x[1], reverse=True)
dd
## 结果如下:
[('Python', 4),
('and', 2),
('Hey,', 1),
('I', 1),
('just', 1),
('love', 1),
('so', 1),
('much,', 1),
('want', 1),
('to', 1),
('get', 1),
('the', 1),
('whole', 1),
('stack', 1),
('by', 1),
('this', 1),
('60-days', 1),
('column', 1),
('believe', 1),
('!', 1)]
上面这个案例有时候还是比较实用的。
2.2 文件写操作
文件写操作时,需要首先判断要写入的文件路径是否存在。若不存在,通过 mkdir 创建出路径;否则,直接写入文件:
def write_to_file(file_path, file_name):
if not os.path.exists(file_path):
os.mkdir(file_path)
whole_path_filename = os.path.join(file_path, file_name)
to_write_content = '''
Hey, Python
I just love Python so much,
and want to get the whole python stack by this 60-days column
and believe!
'''
with open(whole_path_filename, mode='w', encoding='utf-8') as f:
f.write(to_write_content)
3. 获取文件名及文件的后缀名
有时候我们拿到一个文件名的时候, 名字上带有路径, 这时候我们可以用os.path.split
方法实现路径和文件名的分离。
file_ext = os.path.split('./data/py/test.py')
ipath, ifile = file_ext
print(ipath, ifile)
##
./data/py test.py
os.path
模块,splitext
能够优雅地提取文件后缀。
file_extension = os.path.splitext('./data/py/test.py')
file_extension # .py
基于上面的这个操作, 就可以获取某个目录下面指定后缀名的文件
# 获取某个目录下指定后缀名的文件
def find_file(work_dir, extension='jpg'):
lst = []
for filename in os.listdir(work_dir):
#print(filename)
splits = os.path.splitext(filename)
ext = splits[1] # 拿到扩展名
if ext == '.'+extension:
lst.append(filename)
return lst
r = find_file('E:\Jupyter Notebook\Python', 'ipynb')
print(r)
## 结果
['Day1-Day2.ipynb', 'Day10_python文件操作案例.ipynb', 'Day3-Day4.ipynb', 'Day5-Day6.ipynb', 'Day7-Day8.ipynb', 'Day9_字符串操作与正则.ipynb']
这个还是很实用的, 比如在CNN的一些任务中, 很多图片都是以.jpg的形式存放到文件夹里面的, 我们这时候就需要先获取到所有的.jpg文件, 然后再通过一些方式把图片转成矩阵的表示形式, 这样我们的网络才会认识。
下面再整理一个批量修改文件后缀名的例子, 比如我想把某个目录下所有的.xls转成.xlsx文件。这个要怎么做? 依然是先梳理一下逻辑:
- 首先要遍历所给的目录
- 拿到各个文件的后缀名
- 看看后缀名是否是需要修改的后缀名, 如果是就进行重命名操作
有了这个逻辑, 代码就比较好写了。
def batch_rename(work_dir, old_ext, new_ext):
for filename in os.listdir(work_dir):
# 获取文件后缀
split_file = os.path.splitext(filename)
file_ext = split_file[1]
if old_ext == file_ext: # 定位需要修改后缀名的文件
newfile = split_file[0] + new_ext # 修改后文件的完整名称
# 重命名
os.rename(
os.path.join(work_dir, filename),
os.path.join(work_dir, newfile)
)
下面基于这个整理一点新的知识, 就是如果我是想把上面这个写到.py的文件里面, 我从命令行里运行这个.py文件的时候想把上面的三个参数传进去, 那么我应该怎么传呢?
这时候,就需要用到python的一个模块叫做argparse
, 这是python用于解析命令行参数和选项的标准模块, 具体的我目前也不是了解太多, 但是我见过的一般用这个东西的时候, 基本分为三步:
- 创建 ArgumentParser() 对象
- 调用 add_argument() 方法添加参数
- 使用 parse_args() 解析添加的参数
换成代码的方式就是:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int)
parser.add_argument('y', type=int)
args = parser.parse_args()
# 这样就能通过命令行传递过参数x y来, 我们可以通过下面代码获取到这两个参数
x=args.x
y=args.y
print(x,y)
如果想详细了解每个函数里面具体参数到底是干啥用的, 去官网https://docs.python.org/2/howto/argparse.html 所以这里主要是记录一下这个东西到底咋用, 而放到这里例子里面我们就可以这样写(test.py):
def batch_rename(work_dir, old_ext, new_ext):
print(new_ext)
for filename in os.listdir(work_dir):
# 获取文件后缀
split_file = os.path.splitext(filename)
file_ext = split_file[1]
if old_ext == file_ext: # 定位需要修改后缀名的文件
newfile = split_file[0] + new_ext # 修改后文件的完整名称
# 重命名
os.rename(
os.path.join(work_dir, filename),
os.path.join(work_dir, newfile)
)
print("完成重命名")
print(os.listdir(work_dir))
def main():
# 命令行参数
parser = argparse.ArgumentParser(description='工作目录文件后缀名修改')
parser.add_argument('work_dir', metavar='WORK_DIR', type=str, nargs=1, help='修改后缀名文件目录')
parser.add_argument('old_ext', metavar='OLD_EXT', type=str, nargs=1, help='原来的后缀')
parser.add_argument('new_ext', metavar='NEW_EXT', type=str, nargs=1, help='新的后缀')
args = vars(parser.parse_args())
# 从命令行参数中依次解析出参数
work_dir = args['work_dir'][0]
old_ext = args['old_ext'][0]
if old_ext[0] != '.':
old_ext = '.' + old_ext
new_ext = args['new_ext'][0]
if new_ext[0] != '.':
new_ext = '.' + new_ext
batch_rename(work_dir, old_ext, new_ext)
if __name__ == '__main__':
main()
那么我们应该怎么用呢? 打开命令行, 输入:
python test.py E:\test xls xlsx # 后面三个参数分别就是目录, 旧后缀名, 新后缀名
这代码就不解释了。
为什么非要介绍一下argparse
这个库呢? 当然, 如果是用jupyter notebook这样的工具写点小的python demon 或者用keras啥的搭建些神经网络什么的, 一般是用不着这个东西的。 但是如果是放在项目的角度, 我们一般是习惯把整个任务拆成很多小块, 然后每个块写一个.py文件, 这样组织成一个大的工程。 比如训练一个复杂的神经网络做一个时序预测, 这时候如果用jupyter的话, 写出来的代码会非常的长, 并且不易读和维护, 这时候就可以考虑分成四块, 数据集的构造, 模型的建立, 模型的训练和模型的预测。 每一块写到一个.py文件里面。 这样代码的维护和读起来就比较简单了, 所以比较大的项目一般是分成很多个.py的。
那讲了这么一大堆, 和argparse
有啥关系呢? 这个就比较适合于我通过命令行运行train.py这样的代码训练神经网络的时候传一些必要的参数过去, 比如batch_size, epoch, lr, 输入输出的维度等等吧。 你可能又会说为啥不直接声明一些变量先存好这些参数呢, 这样的话即使用命令行直接python train.py不就完事? 当然这个方法可以, 但是如果想频繁的改变参数,且是在Linux的黑窗口下, 光反复的打开关闭.py文件其实是挺麻烦的, 不如留一个传参的接口, 所以这时候可能会用到这个库。 就比如下面的这个代码的开头(train.py):
这时候,我们就可以直接在命令行python train.py
然后后面带着相应的参数即可。