高级文件处理技术详解
1. 文件创建验证
在进行文件操作时,首先要确保文件确实已被创建。可以使用
os.path
模块中的
getsize
函数来验证文件大小,示例代码如下:
from os.path import getsize
getsize('../data/1gb_file.bin')
运行上述代码,如果文件存在,会返回文件的大小(以字节为单位)。例如,对于一个 1GB 的文件,会返回 1073741824。
需要注意的是,文件指针移动到文件末尾之后的能力是依赖于系统的,并非所有系统都支持。
2. 记录基于时间的二进制数据
在记录基于时间的二进制数据时,使用纪元(epoch)表示法是一种不错的方法。以下是一个示例,使用
time
模块和 Python 的
array
模块来记录数据:
import random, time, array
N = 10
fname = '../data/binary_data.f64'
data = array.array('d')
# 创建数据
for value in range(N):
time.sleep(random.random())
data.append(time.time())
data.append(value)
# 将数据存储到文件
f = open(fname, 'wb')
data.tofile(f)
f.close()
上述代码会生成时间戳和对应的值,并将它们以二进制形式存储到文件中。平均运行时间约为 5 秒。
要从二进制文件中检索数据,可以使用以下代码:
import random, time, array
N = 10
fname = '../data/binary_data.f64'
data = array.array('d')
# 读取数据
f = open(fname, 'rb')
data.fromfile(f, N*2)
f.close()
# 显示数据
L = data.tolist()
for t, val in zip(L[::2], L[1::2]):
print(time.ctime(t), val)
这里使用了一个技巧来重新排列数据。当将数组转换为列表时,值是交错存储的(时间、值、时间、值……)。通过
zip
函数将偶数和奇数索引的切片组合起来,可以方便地获取时间和对应的值。
3. 将 MATLAB 文件读取为 NumPy 数组
对于许多 PyLab 用户来说,读取和写入 MATLAB 风格的
.mat
文件是一项重要的功能,它可以实现 MATLAB 和 Python 之间的数据轻松传输。要读写
.mat
文件,可以使用
scipy.io
模块。示例代码如下:
from pylab import *
from scipy.io import savemat, loadmat
a = arange(10)
savemat('data.mat', {'a': a})
del a
loadmat('data.mat')
data = loadmat('data.mat')
data['a']
首先创建一个 NumPy 数组
a
,然后使用
savemat
函数将其写入
.mat
文件。
savemat
函数需要一个文件名和一个字典,字典中包含变量名和实际值。读取
.mat
文件时,会返回一个 Python 字典,可以通过变量名作为键来访问数据。
需要注意的是,并非所有版本的 MATLAB 文件都受支持,可参考 SciPy 网站(http://www.scipy.org/)获取更多信息。
4. 直接将文本文件读取为 NumPy 数组
可以使用
loadtxt
和
savetxt
函数直接将 NumPy 数组读写为文本文件,这两个函数非常适合处理包含数值数据的文本文件。
loadtxt
函数接受以下可选参数:
| 参数 | 描述 | 说明 |
| ---- | ---- | ---- |
|
comments
| 用于指示注释开始的字符,注释行将被忽略 | |
|
converters
| 一个字典,详细说明用于转换每列的函数 | 例如,
converters={'0': datestr2num}
将使用
datestr2num
函数转换第 0 列 |
|
delimiter
| 用于分隔值的字符,默认为任何空白字符 | |
|
dtype
| 数据类型,默认为
float
| |
|
ndmin
| 强制返回的数组为 n 维,n 可以是 0、1 或 2 | |
|
skiprows
| 要跳过的行数,默认为 0 | |
|
usecols
| 包含要读取的列的序列 | 例如,
usecols=(2, 3)
将读取第 3 和第 4 列(索引从 0 开始) |
|
unpack
| 一个布尔标志,指示是否转置读取的数组 | |
以下是一个读取和写入文本文件到 NumPy 数组的示例:
from pylab import *
def c0(x):
return datestr2num(x.decode('utf-8'))
data = loadtxt('../data/charts.xls', skiprows=2, converters={0:c0})
savetxt('../data/charts_mod.txt', data)
在这个示例中,由于
charts.xls
文件的前两行是标题行,所以使用
skiprows=2
跳过它们。第一列包含日期,不能直接放入 NumPy 数组,因此定义了一个转换函数
c0
将日期转换为数字。
另外,
genfromtxt
函数与
loadtxt
类似,但可以处理缺失数据。要获取完整信息,可以使用
help(genfromtxt)
。
5. 对象序列化
在 Python 交互式会话中,有时需要将变量保存到文件。在写入文件之前,需要对变量进行序列化,即将它们转换为字节流。Python 提供了内置的对象序列化模块
Pickle
来实现这一功能。
要使用
Pickle
,首先导入它:
import pickle
pickle.dump(obj, file[, protocol])
函数用于将对象序列化并写入文件。
protocol
参数可以取 0(ASCII,默认值)、1(二进制)和 2(支持新的 Python 对象)。如果提供负值,将使用最高版本的协议。
pickle.load(file)
函数用于从文件中读取对象。
以下是一个保存和检索 Python 会话变量的示例:
import pickle
from numpy import *
fname = '../data/mysession.pickle'
a = 3
b = "A string"
c = {'dict': 10}
d = eye(3)
fout = open(fname, 'wb')
for var in [a, b, c, d]:
pickle.dump(var, fout)
fout.close()
fin = open(fname, 'rb')
var_index = 0
while True:
try:
var_index += 1
exec("v_%d = pickle.load(fin)" % var_index)
exec("var_type = type(v_%d)" % var_index)
print("Read v_%d, type is: %s" % (var_index, var_type))
except EOFError:
break
在这个示例中,使用
pickle.dump
函数将不同类型的变量写入文件,然后使用
pickle.load
函数从文件中读取对象。由于对象的名称没有存储,使用
exec
函数创建变量来存储对象。
如果使用 NumPy 数组,还可以使用
matplotlib
提供的
save
和
load
函数:
from pylab import *
fname = '../data/session.npy'
save(fname, eye(3))
load(fname)
如果文件名以
.gz
结尾,会自动使用 gzip 压缩。
6. 命令行参数
在 Python 中,命令行参数允许我们直接通过命令行将参数传递给应用程序,以控制其行为。
sys
模块的
sys.argv
变量可以用于处理命令行参数。示例代码如下:
import sys
for i, cmd in enumerate(sys.argv):
print("argv[%d] = '%s'" % (i, cmd))
将上述代码保存为
parse_args.py
,在命令行中运行
python parse_args.py 20 myfile
,会输出:
argv[0] = 'parse_args.py'
argv[1] = '20'
argv[2] = 'myfile'
7. 创建固定大小的文件(独立脚本)
以下是一个创建固定大小文件的独立脚本示例:
from sys import argv, exit
usage = "Usage: python empty_file.py nbytes filename"
# 期望三个参数:脚本名称、大小和文件名
if len(argv) != 3:
print("Improper number of arguments.")
print(usage)
exit()
# 检查大小是否为整数
try:
nbytes = int(argv[1])
except ValueError:
print("First argument is not an integer number.")
print(usage)
exit()
# 获取请求的文件名
filename = argv[2]
# 尝试创建文件
try:
f = open(filename, 'wb')
except IOError:
print("Unable to create file", filename)
print(usage)
exit()
# 创建文件
f.seek(nbytes-1)
f.write(b'0')
f.close()
print("Successfully created file %s of size %d." % (filename, nbytes))
这个脚本会检查用户传递的参数是否正确,确保大小是整数,并且文件可以被创建。最后,会创建一个指定大小的文件。
8. 使用 optparse 模块处理命令行参数
为了使脚本更具用户友好性,可以使用
optparse
模块来处理命令行参数。使用
optparse
模块的步骤如下:
1. 导入
optparse
模块:
import optparse
。
2. 创建一个
OptionParser
对象。
3. 使用
add_option
方法添加选项。
4. 使用
parse_args
方法解析命令行参数。
以下是一个使用
optparse
模块处理命令行参数的示例:
from optparse import OptionParser
from sys import exit
usage = "Usage: python empty_opt.py [options] filename"
# 创建一个 OptionParser 实例
parser = OptionParser(usage)
# 添加选项
parser.add_option("-n", "--numbytes", dest="nbytes",
type = "int", default=1000, help="number of bytes in file")
parser.add_option("-x", "--ext", dest="ext",
action="store_true", default=False,
help="adds 'bin' extension to filename")
(opt, args) = parser.parse_args()
# 必须有一个文件名
if len(args) != 1:
print("Improper number of arguments.")
exit()
# 如果开关打开,添加扩展名
filename = args[0]+'.bin' if opt.ext else args[0]
# 创建文件
try:
f = open(filename, 'wb')
except IOError:
print("Unable to create file", filename)
exit()
f.seek(opt.nbytes-1)
f.write(b'0')
f.close()
print("Successfully created file %s of size %d." %
(filename, opt.nbytes))
在这个示例中,添加了两个选项:
-n
用于指定文件的字节数,
-x
用于指示是否添加
.bin
扩展名。运行脚本时,可以根据需要传递不同的选项。例如:
$ python empty_opt.py
Improper number of arguments.
$ python empty_opt.py -h
Usage: python empty_opt.py [options] filename
Options:
-h, --help show this help message and exit
-n NBYTES, --numbytes=NBYTES
number of bytes in file
-x, --ext adds 'bin' extension to filename
$ python empty_opt.py file1
Successfully created file file1 of size 1000.
$ python empty_opt.py -x file1
Successfully created file file1.bin of size 1000.
$ python empty_opt.py -n 2000 --ext file1
Successfully created file file1.bin of size 2000.
通过以上介绍,我们学习了多种文件处理技术,包括文件创建验证、记录基于时间的二进制数据、读写 MATLAB 文件、读写文本文件、对象序列化以及处理命令行参数等。这些技术可以帮助我们更高效地进行文件操作和数据处理。
高级文件处理技术详解(续)
9. 命令行参数处理流程总结
为了更清晰地理解命令行参数的处理过程,我们可以用 mermaid 流程图来展示:
graph TD;
A[开始] --> B[导入必要模块];
B --> C[定义使用说明];
C --> D[创建 OptionParser 实例];
D --> E[添加选项];
E --> F[解析命令行参数];
F --> G{参数数量是否正确};
G -- 是 --> H{选项值是否有效};
G -- 否 --> I[输出错误信息并退出];
H -- 是 --> J[处理选项和参数];
H -- 否 --> I;
J --> K[执行主要操作];
K --> L[输出结果];
L --> M[结束];
这个流程图展示了使用
optparse
模块处理命令行参数的一般流程。首先导入必要的模块,然后定义使用说明,接着创建
OptionParser
实例并添加选项。解析参数后,会检查参数数量和选项值的有效性,如果都通过则执行主要操作并输出结果。
10. 不同文件处理方法对比
为了帮助大家更好地选择合适的文件处理方法,下面对前面介绍的几种方法进行对比:
| 处理方法 | 适用场景 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- |
| 记录基于时间的二进制数据 | 记录随时间变化的变量 | 数据存储紧凑,读写速度快 | 数据格式不直观,不易手动查看 |
| 读写 MATLAB 文件 | 在 MATLAB 和 Python 之间传输数据 | 方便数据共享 | 并非所有 MATLAB 文件版本都支持 |
| 直接读写文本文件为 NumPy 数组 | 处理包含数值数据的文本文件 | 数据格式直观,易于处理 | 读写速度相对较慢,占用空间较大 |
| 对象序列化(Pickle) | 保存 Python 会话中的变量 | 可以处理各种类型的变量 | 可能存在安全风险,不同 Python 版本兼容性可能有问题 |
| 命令行参数处理 | 控制脚本的行为 | 提高脚本的灵活性和用户友好性 | 增加了代码的复杂度 |
11. 实际应用案例分析
假设我们需要开发一个数据采集系统,该系统需要记录传感器的实时数据,并将数据保存到文件中。我们可以结合前面介绍的技术来实现这个系统。
首先,使用记录基于时间的二进制数据的方法来记录传感器数据:
import random, time, array
N = 100 # 采集数据的次数
fname = '../data/sensor_data.f64'
data = array.array('d')
# 模拟传感器数据采集
for value in range(N):
time.sleep(0.1) # 模拟采集间隔
data.append(time.time())
data.append(random.random()) # 模拟传感器数据
# 存储数据到文件
f = open(fname, 'wb')
data.tofile(f)
f.close()
然后,我们可以使用
Pickle
模块将采集到的数据进行序列化保存,以便后续分析:
import pickle
sensor_data = {'time': data[::2], 'value': data[1::2]}
with open('../data/sensor_data.pickle', 'wb') as f:
pickle.dump(sensor_data, f)
如果需要将数据分享给使用 MATLAB 的同事,我们可以将数据保存为
.mat
文件:
from scipy.io import savemat
import numpy as np
time_data = np.array(data[::2])
value_data = np.array(data[1::2])
savemat('../data/sensor_data.mat', {'time': time_data, 'value': value_data})
12. 总结与建议
通过前面的介绍和案例分析,我们可以看到不同的文件处理技术在不同的场景下有各自的优势。在实际应用中,我们可以根据具体需求选择合适的方法。
- 如果需要处理大量的数值数据,并且对读写速度有较高要求,可以考虑使用二进制文件或 NumPy 数组的读写方法。
- 如果需要在不同的编程语言之间共享数据,使用 MATLAB 文件或对象序列化(Pickle)是不错的选择。
- 如果需要让脚本更具灵活性,方便用户控制脚本的行为,使用命令行参数处理技术可以提高用户体验。
同时,在使用这些技术时,也需要注意一些问题,如 MATLAB 文件版本的兼容性、
Pickle
的安全风险等。希望这些内容能帮助大家更好地进行文件处理和数据管理。
超级会员免费看

被折叠的 条评论
为什么被折叠?



