ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python编程 | 系统编程 | 文件和目录工具 |文件工具

2021-07-31 23:00:52  阅读:103  来源: 互联网

标签:文件 file os 编程 data str 工具 open


文章目录

文件工具

Python中的内建函数open是脚本用来在计算机底层系统下访问文件的主要工具。open函数被调用时将返回一个新的与外部文件相连的文件对象。这个文件对象有一些方法,可以与文件双向传输数据,并且能执行多种文件的相关操作。open函数还为底层文件系统提供了可移植接口。

回忆一下,可以对打开的对象运行dir(obj)来查看其属性、方法等;可以运行help(obj)获取通用帮助信息,还可以运行help(obj.method)获取method特定方法的帮助信息。

Python 3.X中的文件对象类型

Python 3.X中的str字符串总是代表Unicode文本(ASCII或更广的字符集),而bytesbytearray字符串代表原始二进制数据。

  • 文本文件含有Unicode文本。在你的脚本中,文本文件内容始终是一个**str字符串——字符构成的序列**(准确地说是Unicode“代码点”)。在本章文本文件默认执行换行符自动转换,而且自动将 Unicode 编码应用于文件内容:它们在文件进行双向传输时依照一个给定或默认的编码名称,对原始二进制字节进行编码和解码。编码对 ASCII 文本来说很简单,但在其他情况下可能比较复杂。
  • 二进制文件包含原始的8位字节。在你的脚本中,二进制文本文件的文件内容始终是一个字节字符串, 通常是一个字节对象——小整数构成的序列,支持大多数 str 操作并尽量显示为 ASCII 字符。二进制文件与其他文件进行双向传输的时候不进行换行符转换和 Unicode 编码

在实践中,文本文件用于所有真正的文本相关数据,而二进制文件则用于存储内容

从程序员的角度看,这两种文件类型是在通过传入open的模式字符串参数来区分的:参数中填上“b”(比如rbwb等),将意味着文件包含二级制数据。在对编码新文件内容时,对文本使用普通字符串(如’spam’或bytes.decode),而对二进制使用字节字符串(如b’spam’或str.encode)。

由于文本模式要求文件内容能够按照某个Unicode编码方案的内容进行解码,所以你必须在二进制模式下将不可解码的内容读取为字节字符串(或者在try语句中捕获Unicode异常,然后整个跳过该文件)。这些文件可能包括真正的二进制文件以及使用非默认的未知编码的文本文件。

使用内建文件对象

输出文件

>>> file = open('data.txt', 'w')				# 打开输出文件对象:创建
>>> file.write('1')								# 逐字写入字符串
1												# 返回所写入的字符或字节数目
>>> file.write('2')
1
>>> file.writelines(['3', '4'])					# 将列表里的所有字符串一次性写入
>>> file.writelines(['5', '6'])
>>> file.close()								# 在垃圾回收和退出时也会关闭文件
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/Python编程_Markdown笔记/pp4e/system$ cat data.txt
123456
open('file', 'w').write('Good night\n')			# 写入临时对象
open('file', 'r').read()						# 从临时对象中读取
  • 因为以上两个表达式生成了一个临时文件对象,并且马上被使用,而且没有保存对对象的引用,所以在数据传输后,文件对象马上就被回收并在进程中自动关闭。在这种代码中通常没有必要去显示调用close方法

确保文件关闭:异常处理器和上下文管理器

当程序抛出异常,文件还未手动关闭时,首先要确认是否必须关闭——文件回收时会自动关闭,关闭最终会执行,即便发生异常。

如果必须关闭,那么又两种基本解决方法。

  • try ... finally ...语句
my_file = open(filename, 'w')

try:
    ...process my_file...
finally:
    my_file.close()
  • with ...语句。这个语句依赖于文件对象的上下文管理器:无论发生什么异常行为,代码在进入和离开语句时都能自动运行。with语句也可指定多个(嵌套)上下文管理器。
# 单个上下文管理器
with open(filename, 'w') as my_file:
    ...process my_file, auto-closed on statement exit...

# 多个上下文管理器:可用逗号隔开,或者嵌套
with open(file_name_1, 'w') as file_1, open(file_name_2, 'w') as file_2:
    ...statements...

with open(file_name_1, 'w') as file_1:
    with open(file_name_2, 'w') as file_2:
		...statements...

with仅应用于支持上下文管理协议的对象,而try...finally允许任意异常情况下的任意退出操作。

输入文件

>>> file = open('data.txt')
>>> 
>>> file.read()
'123456'
>>> 
>>> file.seek(0)
0
>>> file.read(1)
'1'
>>> file.read(3)
'234'
>>> file.read(-1)
'56'
>>> 
>>> file.seek(0)
0
>>> file.readline()
'123456'
>>> 
>>> file.seek(0)
0
>>> file.readlines()
['123456']
  • file.seek(N):调用seek只是让我们下次的传输操作移动到偏移值为N的新位置,文件中所有的读取和写入操作都发生于当前位置。
  • file.read():返回一个字符串,它包含返回在文件中的所有字符。
  • file.read(N):返回一个字符串,它包含文件中接下来的N个字符(或字节)。
  • file.readline():读取下一个\n之前的内容并返回一个行字符串。
  • file.readlines():读取整个文件并返回一个行字符串列表。

使用文件迭代器读取行

演示需要,修改了data.txt:

beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/Python编程_Markdown笔记/pp4e/system$ cat data.txt 
1
2
3
4
5
6

无需使用readlines,迭代器会自动读取行:

>>> for line_str in open('data.txt'):
...     print(line_str, end='')
... 
1
2
3
4
5
6
  • 你可以在这个循环本身把文件作为临时对象打开,后者将在循环结束后在垃圾回收时自动关闭。

你可以手动使用文件迭代器:它只是一个__next__方法(由内建函数next运行),与每次调用readline方法类似,只是readline方法在文件末尾(EOF)返回一个空字符串,而迭代器则抛出一个异常来结束迭代。

>>> file = open('data.txt')
>>> 
>>> file.readline()
'1\n'
>>> file.readline()
'2\n'
>>> file.readline()
'3\n'
>>> file.readline()
'4\n'
>>> file.readline()
'5\n'
>>> file.readline()
'6\n'
>>> file.readline()
''
>>> 
>>> file.seek(0)
0
>>> file.__next__()
'1\n'
>>> file.__next__()
'2\n'
>>> file.__next__()
'3\n'
>>> file.__next__()
'4\n'
>>> file.__next__()
'5\n'
>>> file.__next__()
'6\n'
>>> file.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> 
>>> file.seek(0)
0
>>> next(file)
'1\n'
>>> next(file)
'2\n'
>>> next(file)
'3\n'
>>> next(file)
'4\n'
>>> next(file)
'5\n'
>>> next(file)
'6\n'
>>> next(file)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

有趣的是,在所有迭代场景下,迭代器都自动得以调用,包括但不仅限于以下几种情况:

>>> list(open('data.txt'))													# 强制逐行迭代
['1\n', '2\n', '3\n', '4\n', '5\n', '6\n']
>>> lines_list = [line_str.rstrip() for line_str in open('data.txt')]		# 解析
>>> lines_list
['1', '2', '3', '4', '5', '6']
>>> lines_list = [int(line_str) ** 2 for line_str in open('data.txt')]		# 任意操作
>>> lines_list
[1, 4, 9, 16, 25, 36]
>>> list(map(int, open('data.txt')))										# 应用函数
[1, 2, 3, 4, 5, 6]
>>> line = '3\n'
>>> line in open('data.txt')												# 判断文件是否含有该行
True

<<<<<<< HEAD

二进制和文本文件

>>> file = open('data.txt', 'wb')

>>> file = open('data.txt', 'wb')
  • 打开二进制文件时,传入的open模式参数要加上b
  • readwrite方法用来读取文件内容。在这里,readlinereadlines方法仍可使用,但是对于真正的二进制数据时,这下操作是没有意义的(换行符字节即使存在,也毫无意义)。

在所有情况下,文件与程序间传输的数据,即使是二进制数据,在脚本中还是表示为Python字符串。然而如果使用二进制模式打开文件,文件内容则显示为字节字符串。

>>> open('data.txt', 'r').read()
'1\n2\n3\n4\n5\n6\n'
>>> open('data.txt', 'rb').read()
b'1\n2\n3\n4\n5\n6\n'
>>> 
>>> for line_bstr in open('data.txt', 'rb'):
...     print(line_bstr)
... 
b'1\n'
b'2\n'
b'3\n'
b'4\n'
b'5\n'
b'6\n'
  • 产生这种结果的原因是Python 3.X把文本模式文件当做Unicode来处理,并自动的在输入时对文件内容进行解码,在输出时又进行编码。二进制模式文件则允许我们把文件字符串作为原始字节字符串来访问,而不进行转换,因此它们忠实地反映了该文件的存储内容。但要谨记,你必须在二进制模式下打开真正的二进制数据,因为它作为 Unicode 文本时是不可解码的

你也必须为二进制输出文件提供字节字符串

>>> open('data.bin', 'wb').write(b'spam\n')
5
>>> open('data.bin', 'rb').read()
b'spam\n'

请注意该文件每行仅以“\n”结尾,在Windows下以“\r\n”结尾。严格说来,二进制模式不仅使Unicode转码无法进行,还阻止了文本模式下默认换行符的自动转换。

文本文件的Unicode编码

下面这个字符串包含一个Unicode字符的字符串,其二进制值在ASCII编码标准的7位范围之外。

>>> open('data.bin', 'wb').write(b'spam\n')
5
>>> open('data.bin', 'rb').read()
b'spam\n'
>>> data_str = 'sp\xe4m'
>>> data_str
'späm'
>>> 0xe4, bin(0xe4), chr(0xe4)
(228, '0b11100100', 'ä')

也可以手动编码:

>>> data_str.encode('latin1')						# 8位字符:ascii + 额外字符
b'sp\xe4m'
>>> data_str.encode('utf8')							# 仅特殊字符转换为二进制序列
b'sp\xc3\xa4m'
>>> data_str.encode('ascii')						# 不能遵照ascii进行编码
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\xe4' in position 2: ordinal not in range(128)
>>> data_str.encode('utf16')						# 每个字符2字节,加上前缀
b'\xff\xfes\x00p\x00\xe4\x00m\x00'
>>> data_str.encode('cp500')						# 另一种ebcdic编码:很不一样
b'\xa2\x97C\x94'

不过,如果我们在二进制模式下打开文件,是不会进行编码转换的。

>>> open('data.txt', 'w', encoding='latin1').write(data_str)
4
>>> open('data.txt', 'r', encoding='latin1').read()
'späm'
>>> open('data.txt', 'rb').read()
b'sp\xe4m'
>>> open('data.txt', 'w', encoding='utf8').write(data_str)			# utf8编码并写入
4
>>> open('data.txt', 'r', encoding='utf8').read()					# utf8解码
'späm'
>>> open('data.txt', 'r', encoding='latin1').read()					# latin1解码
'späm'
>>> open('data.txt', 'rb').read()
b'sp\xc3\xa4m'

这一次,虽然原始文件内容有所不同,但文本模式的自动解码使得字符串在脚本读取返回之前变得相同。请注意,尝试写入不可编码的数据或阅读不可编码的数据都将引起错误。

除非使用了二进制模式,否则以下代码可以在编码类型已知时重新创建原始字符串,在编码类型未知时操作失败。

>>> open('data.txt', 'w', encoding='cp500').write('I LOVE U\n')
9
>>> open('data.txt', 'r', encoding='cp500').read()
'I LOVE U\n'
>>> open('data.txt', 'r').read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/alone/anaconda3/lib/python3.7/codecs.py", line 322, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc9 in position 0: invalid continuation byte
>>> open('data.txt', 'rb').read()
b'\xc9@\xd3\xd6\xe5\xc5@\xe4%'

文本文件的换行符转换

如果Python脚本在Windows下运行,在默认情况下,文件对象会自动将DOS “\r\n”序列映射为单独的“\n”。

  • 对于文本模式下打开的文件,输入“\r\n”会转换成“\n”。
  • 对于文本模式下打开的文件,输出时“\n”会转换成“\r\n”。
  • 对于二进制模式下打开的文件,输入输出都不会发生转换。

在类Unix平台上之所以不会发生任何转换,是因为文件里使用的是“\n”。这些规则中,有两条重要结果必须要牢记:

  • 首先,不论以何种形式存储于顶层平台的外部文件中,文本模式文件换行符几乎总在Python中显示为“\n”。通过在输入时映射为“\n”,在输出时映射为“\n”,Python得以隐藏特定平台间的差异。
  • 其次,在处理二进制文件中,二进制打开模式有效地关闭了换行转换。

最后,需要牢记的是:文本文件的内容一般都应当以“\n”来代表结尾,而二进制数据应当总是在二进制模式下打开,以便阻止换行符转换和Unicode编码

struct模块解析打包的二进制数据

struct模块提供用于打包和解压二进制的调用。它能够用你想用的任何一种字节序来组合和分解(字节序决定了二进制数字的最高有效位是居左还是居右)。

>>> import struct
>>> 
>>> data_b = struct.pack('>i4shf', 2, b'spam', 3, 1.234)
>>> data_b
b'\x00\x00\x00\x02spam\x00\x03?\x9d\xf3\xb6'
>>> open('data.bin', 'wb').write(data_b)
14
  • 这里pack调用的格式字符串是一个高位优先(>),它包含一个整数(i)、一个四字符的字节字符串(4s)、一个半整数(h),以及一个浮点数(f)。

也可以解析数据:

>>> values_tuple = struct.unpack('>i4shf', open('data.bin', 'rb').read())
>>> values_tuple
(2, b'spam', 3, 1.2339999675750732)

我们可以对字符串和按位运算进行深层探索:

>>> bin(values_tuple[0] | 0b1)
'0b11'
>>> values_tuple[1], list(values_tuple[1]), values_tuple[1][0]
(b'spam', [115, 112, 97, 109], 115)

随机访问文件

文件的open模式字符串添加+号后可读取又可写入。这种模式通常与文件对象的seek方法联合使用,以便支持随机读取或写入访问。这种灵活的文件处理模式允许我们执行从一处读取字节,再在另一处写入等操作。当脚本把这一性能与二进制文件模式结合在一起时,便可在一个文件内获取和更新任意字节

Pythonseek方法也接受可选的第二参数,这个参数有3种值:0表示绝对文件位置(默认值);1表示基于当前所在的相对位置;2表示寻求基于文件结尾的相对位置。

为了证明上述结论,让我们在wb+的模式下创建一个文件,但如果文件已经存在,此模式会清空文档内容(所有w模式都会)。

>>> records_list = [bytes([i_str] * 8) for i_str in b'spam']
>>> records_list
[b'ssssssss', b'pppppppp', b'aaaaaaaa', b'mmmmmmmm']
>>> 
>>> file = open('random.bin', 'wb+')
>>> file.writelines(records_list)
>>> file.seek(0)
0
>>> file.read()
b'ssssssssppppppppaaaaaaaammmmmmmm'

现在让我们在rb+模式下重新打开文件,这个模式也允许读取和写入,但是初始化时并不会清空文件。

>>> file = open('random.bin', 'rb+')
>>> file.read()
b'ssssssssppppppppaaaaaaaammmmmmmm'
>>> file.seek(0)
0
>>> file.write(b'X' * 8)				# 更新第一条记录
8
>>> file.seek(0)
0
>>> file.read(8)
b'XXXXXXXX'
>>> file.write(b'Y' * 8)				# 更新第二条记录
8
>>> file.seek(0)
0
>>> file.read()
b'XXXXXXXXYYYYYYYYaaaaaaaammmmmmmm'

os模块中的底层文件工具

os模块包含一个文件处理函数的附加集合。下面列出了部分os文件的相关调用:

  • os.open(path, flags, mode):打开文件并返回其描述符。
  • os.read(descriptor, N):最多读取N个字节并返回字节字符串。
  • os.write(descriptor, string):把文件字符串string中的字节写入文件。
  • os.lseek(descriptor, position, how):在文件中移至position

严格地说,os调用通过文件的描述符来处理文件,描述符是整数代码或“句柄”,代表着操作系统中的文件。基于描述符的文件以原始字节的形式来处理,而且没有我们之前所学的文本的换行符和Unicode转换的概念。事实上,除了缓冲等额外性能,基于描述符的文件一般都能对应上二进制模式文件对象,而且我们也可以类似地读取和写入bytes字符串而非str字符串。然而,与带有open内建函数的内建文件对象相比,os中基于文件描述符的文件工具更底层且更复杂,所以,除非有非常特殊的文件处理需求,否则一般使用open函数。

使用os.open文件

>>> import sys
>>> 
>>> for stream_file in (sys.stdin, sys.stdout, sys.stderr):
...     print(stream_file.fileno())
... 
0
1
2
>>> 
>>> 
>>> import os
>>> sys.stdout.write('Hello stdio world!\n')			# 借助文件方法写入
Hello stdio world!
19
>>> os.write(1, b'Hello descriptor world!\n')			# 借助os模块写入
Hello descriptor world!
24
  • fileno文件对象方法返回的整数描述符是与某个内建文件对象相关联的。例如,标准流文件对象拥有描述符0、1、2;调用os.write函数,通过描述符将数据发送至stdout,与调用sys.stdout.write方法的效果是一样的。

我们可以通过内建函数openos模块·中的工具或者二者结合起来使用处理给定的外部文件。

>>> file = open('spam.txt', 'w')
>>> file.write('Hello stdio world!\n')
19
>>> file.flush()
>>> file.fileno()
3
>>> 
>>> 
>>> os.write(3, b'Hello descriptor world!\n')
24
>>> file.close()
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/Python编程_Markdown笔记/pp4e/system$ cat spam.txt 
Hello stdio world!
Hello descriptor world!

需要注意两点:

  • os.write写入的是字节字符串(bytes)。
  • 必须在os.write之后使用file.close()

os.open的模式标识符

为什么Python会提供额外的文件工具呢?简而言之,它们为文件处理提供更多底层控制。内建的open函数受限于所使用的的底层文件系统,os模块让脚本进行更精细地控制。

>>> fdfile = os.open('spam.txt', os.O_RDWR)
>>> os.read(fdfile, 20)
b'Hello stdio world!\nH'
>>> os.lseek(fdfile, 0, 0)							# 回到文件起始处
0
>>> os.read(fdfile, 100)							# 在二进制模式下保留
b'Hello stdio world!\nHello descriptor world!\n'
>>> os.lseek(fdfile, 0, 0)
0
>>> os.write(fdfile, b'HELLO')						# 覆盖前5个字节
5
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/Python编程_Markdown笔记/pp4e/system$ cat spam.txt 
HELLO stdio world!
Hello descriptor world!
  • 在某些系统上,第一行代码需改成fdfile = os.open('spam.txt', (os.O_RDWR | os.O_BINARY)),这样通过对os导出的两个模式标识符进行二进制的“或”操作,从而以读-写和二进制模式打开一个基于描述符的文件。
>>> spam_file_rbplus = open('spam.txt', 'rb+')				# 同上,只是采用open文件对象
>>> spam_file_rbplus.read()
b'HELLO stdio world!\nHello descriptor world!\n'
>>> spam_file_rbplus.seek(0)
0
>>> spam_file_rbplus.write(b'Hello')
5
>>> spam_file_rbplus.flush()
>>> spam_file_rbplus.close()
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/Python编程_Markdown笔记/pp4e/system$ cat spam.txt 
Hello stdio world!
Hello descriptor world!

但在某些系统上,os.open标识符可指定更高级的参数,因此有些标识符是不可移植的。

把描述符封装进文件对象

我们可以利用os.fdopen调用把文件描述符封装进文件对象。

>>> import os
>>> 
>>> fd_int = os.open('spam.txt', os.O_RDWR)
>>> fdfile = os.fdopen(fd_int, 'rb')
>>> fdfile.read()
b'Hello stdio world!\nHello descriptor world!\n'

文件描述符封装的文件对象:在文本模式下,读取和写入时将执行我们之前学过的Unicode编码和换行符转换,并且处理的是str字符串,而非bytes字符串:

>>> fd_int = os.open('spam.txt', os.O_RDWR)
>>> fdfile = os.fdopen(fd_int, 'r')
>>> fdfile.read()
'Hello stdio world!\nHello descriptor world!\n'
>>> type(fdfile.read())
<class 'str'>

openos.fdopen都可接受额外的控制参数。

>>> fd_int = os.open('spam.txt', os.O_RDWR)
>>> 
>>> file = open(fd_int, 'r', encoding='latin1', closefd=False)
>>> file.read()
'Hello stdio world!\nHello descriptor world!\n'
>>> fdfile = os.fdopen(fd_int, 'r', encoding='latin1', closefd=False)
>>> fdfile.read()
'Hello stdio world!\nHello descriptor world!\n'
>>> fdfile.close()

其他的os模块文件工具

>>> os.chmod('spam.txt', 0o777)			# 修改权限
>>> os.rename(fn_1, fn_2)				# 文件名fn_1 -> 文件名fn_2
>>> os.remove(fn)						# 删除文件fn 与os.unlink同义
>>> open('spam.txt', 'w').write('Hello stat world\n')
17
>>> info_tuple = os.stat('spam.txt')
>>> info_tuple
os.stat_result(st_mode=33279, st_ino=256914, st_dev=2049, st_nlink=1, st_uid=1000, st_gid=1000, st_size=17, st_atime=1627653023, st_mtime=1627655368, st_ctime=1627655368)
>>> info_tuple.st_mode, info_tuple.st_size
(33279, 17)

>>> import stat
>>> info_tuple[stat.ST_MODE], info_tuple[stat.ST_SIZE]
(33279, 17)
>>> stat.S_ISDIR(info_tuple.st_mode), stat.S_ISREG(info_tuple.st_mode)
(False, True)

文件扫描器

现在让我们做一个工具,它能演示目前为止我们已经学习过的内容。

下面的模块定义了通用文件扫描例行程序:

示例:scan_file.py

#!/usr/bin/env python


def file_scanner(fn_str, function):
	"通用文件扫描例行函数"

	file = open(fn_str, 'r')					# 创建文件对象

	while True:
		line_str = file.readline()				# 调用文件方法
		if not line_str:
			break								# 直到文件末尾
		function(line_str)						# 调用一个函数对象

	file.close()

下面是一个进行简单逐行转换的客户端脚本:

示例:commands.py

#!/usr/bin/env python

from sys import argv
from scan_file import file_scanner


class UnknownCommand(Exception):
	"一个“未知命令”异常的类"

	pass


def process_file(line_str):
	"一个逐行将“*name”和“+name”转换为“Ms.name”和“Mr.name”的函数"

	if line_str[0] == '+':
		# 剥去开头和末尾的字符:\n
		print('Mr.' + line_str[1:-1])
	elif line_str[0] == '*':
		print('Ms.' + line_str[1:-1])
	else:
		# 抛出异常
		raise UnknownCommand(line_str)


if len(argv) == 2:
	# 允许通过文件名命令行参数传入文件
	fn_str = argv[1]
else:
	fn_str = 'data.txt'

# 运行扫描器
file_scanner(fn_str, process_file)

测试:

beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/Python编程_Markdown笔记/pp4e/system$ cat hillbillies.txt 
*Granny
+Jethro
*Elly May
+"Uncle Jed"
beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/Python编程_Markdown笔记/pp4e/system$ ./commands.py hillbillies.txt 
Ms.Granny
Mr.Jethro
Ms.Elly May
Mr."Uncle Jed"

示例:file_scan.py替代方案

# 替代方案 A
def file_scanner(fn_str, function):
	# 使用文件迭代器逐行扫描
	for line_str in open(fn_str, 'r'):
		# 调用一个函数对象
		function(line_str)


# 替代方案 B
def file_scanner(fn_str, function):
	# 使用map代替了for循环
	list(map(function, open(fn_str, 'r')))


# 替代方案 C
def file_scanner(fn_str, function):
	# 使用列表解析代替for循环
	[function(line_str) for line_str in open(fn_str, 'r')]


# 替代方案 D
def file_scanner(fn_str, function):
	# 使用列表解析代替for循环
	list(function(line_str) for line_str in open(fn_str, 'r'))

示例:commands.py替代方案

# 数据比代码更容易拓展
commands_dict = {'+': 'Mr.', '*': 'Ms.'}


def process_file(line_str):
	try:
		print(commands_dict[line_str[0]] + line_str[1:-1])
	except KeyError:
		raise UnknownCommand(line_str)

文件过滤器

#!/usr/bin/env python


import sys


def filter_file(fn_str, function):
	"显示指定文件"

	input_file = open(fn_str, 'r')
	# 显示地指出输出文件
	output_file = open(fn_str + '.out', 'w')

	for line_str in input_file:
		output_file.write(function(line_str))

	input_file.close()
	output_file.close()


# 替代方案
# 利用上下文管理器
# def filter_file(fn_str, function):
# 	with open(fn_str, 'r') as input_file, open(fn_str + '.out', 'w') as output_file:
# 		for line_str in input_file:
# 			output_file.write(function(line_str))


def filter_stream(function):
	"利用标准输入/输出流允许在命令行中重定向"

	while True:
		line_str = sys.stdin.readline()	# 可替换为input()
		if not line_str:
			break
		print(function(line_str), end='')	# 可替换为sys.stdout.write()


# 替代方案
# 利用文件对象的行迭代器
# def filter_stream(function):
# 	for line_str in sys.stdin:
# 		print(function(line_str), end='')


if __name__ == '__main__':
	# 将stdin复制到stdout
	filter_stream(lambda line_str: line_str)

运行结果:

beacherhou@alone-Vostro-14-5401:/media/beacherhou/Coding/code_obsidian_知识库/Python编程_Markdown笔记/pp4e/system$ ./filters.py < hillbillies.txt 
*Granny
+Jethro
*Elly May
+"Uncle Jed"
>>> from filters import filter_file
>>> filter_file('hillbillies.txt', str.upper)
>>> print(open('hillbillies.txt.out').read())
*GRANNY
+JETHRO
*ELLY MAY
+"UNCLE JED"

:)学完博客后,是不是有所启发呢?如果对此还有疑问,欢迎在评论区留言哦。
如果还想了解更多的信息,欢迎大佬们关注我哦,也可以查看我的个人博客网站BeacherHou

标签:文件,file,os,编程,data,str,工具,open
来源: https://blog.csdn.net/weixin_49369658/article/details/119282653

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有