Python 物联网入门指南(五)(1)

简介: Python 物联网入门指南(五)

第十四章:文件 I/O 和 Python 工具

在本章中,我们将详细讨论文件 I/O,即读取、写入和追加文件。我们还将讨论 Python 工具,这些工具使得操作文件和与操作系统交互成为可能。每个主题都有不同的复杂程度,我们将通过一个例子来讨论。让我们开始吧!

文件 I/O

我们讨论文件 I/O 有两个原因:

  • Linux 操作系统的世界中,一切都是文件。与树莓派上的外围设备交互类似于读取/写入文件。例如:在第十二章中,通信接口,我们讨论了串口通信。您应该能够观察到串口通信类似于文件读写操作。
  • 我们在每个项目中以某种形式使用文件 I/O。例如:将传感器数据写入 CSV 文件,或者读取 Web 服务器的预配置选项等。

因此,我们认为讨论 Python 中的文件 I/O 作为一个单独的章节会很有用(详细文档请参阅:docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files),并讨论它在开发树莓派 Zero 应用程序时可能发挥作用的示例。

从文件中读取

让我们创建一个简单的文本文件read_file.txt,其中包含以下文本:我正在使用树莓派 Zero 学习 Python 编程,并将其保存到代码示例目录(或您选择的任何位置)。

要从文件中读取,我们需要使用 Python 的内置函数:open来打开文件。让我们快速看一下一个代码片段,演示如何打开一个文本文件以读取其内容并将其打印到屏幕上:

if __name__ == "__main__":
    # open text file to read
    file = open('read_line.txt', 'r')
    # read from file and store it to data
    data = file.read()
    print(data)
    file.close()

让我们详细讨论这段代码片段:

  1. 读取文本文件内容的第一步是使用内置函数open打开文件。需要将所需的文件作为参数传递,并且还需要一个标志r,表示我们打开文件以读取内容(随着我们讨论每个读取/写入文件时,我们将讨论其他标志选项)。
  2. 打开文件时,open函数返回一个指针(文件对象的地址),并将其存储在file变量中。
file = open('read_line.txt', 'r')
  1. 这个文件指针用于读取文件的内容并将其打印到屏幕上:
data = file.read() 
       print(data)
  1. 读取文件的内容后,通过调用close()函数关闭文件。

运行前面的代码片段(可与本章一起下载的read_from_file.py)使用 IDLE3 或命令行终端。文本文件的内容将如下打印到屏幕上:

I am learning Python Programming using the Raspberry Pi Zero

读取行

有时,有必要逐行读取文件的内容。在 Python 中,有两种选项可以做到这一点:readline()readlines()

  • readline(): 正如其名称所示,这个内置函数使得逐行读取成为可能。让我们通过一个例子来复习一下:
if __name__ == "__main__": 
          # open text file to read
          file = open('read_line.txt', 'r') 
          # read a line from the file
          data = file.readline() 
          print(data) 
          # read another line from the file 
          data = file.readline() 
          print(data) 
          file.close()

当执行前面的代码片段(可与本章一起下载,文件名为read_line_from_file.py)时,read_line.txt文件被打开,并且readline()函数返回一行。这一行被存储在变量 data 中。由于该函数在程序中被调用两次,输出如下:

I am learning Python Programming using the Raspberry Pi Zero. 
 This is the second line.

每次调用readline函数时都会返回一个新行,并且当到达文件结尾时会返回一个空字符串。

  • readlines(): 这个函数逐行读取文件的全部内容,并将每一行存储到一个列表中:
if __name__ == "__main__": 
           # open text file to read
           file = open('read_lines.txt', 'r') 
           # read a line from the file
           data = file.readlines() 
           for line in data: 
               print(line) 
           file.close()

由于文件的行被存储为一个列表,可以通过对列表进行迭代来检索它:

data = file.readlines() 
           for line in data: 
               print(line)

前面的代码片段可与本章一起下载,文件名为read_lines_from_file.py

写入文件

按照以下步骤进行写入文件:

  1. 写入文件的第一步是使用写入标志w打开文件。如果作为参数传递的文件名不存在,将创建一个新文件:
file = open('write_file.txt', 'w')
  1. 文件打开后,下一步是将要写入的字符串作为参数传递给write()函数:
file.write('I am excited to learn Python using
      Raspberry Pi Zero')
  1. 让我们将代码放在一起,我们将一个字符串写入文本文件,关闭它,重新打开文件并将文件的内容打印到屏幕上:
if __name__ == "__main__": 
          # open text file to write
          file = open('write_file.txt', 'w') 
          # write a line from the file
          file.write('I am excited to learn Python using
          Raspberry Pi Zero \n') 
          file.close() 
          file = open('write_file.txt', 'r') 
          data = file.read() 
          print(data) 
          file.close()
  1. 前面的代码片段可与本章一起下载(write_to_file.py)。
  2. 当执行前面的代码片段时,输出如下所示:
I am excited to learn Python using Raspberry Pi Zero

追加到文件

每当使用写入标志w打开文件时,文件的内容都会被删除,并重新打开以写入数据。还有一个叫做a的替代标志,它使得可以将数据追加到文件的末尾。如果打开的文件(作为打开的参数)不存在,这个标志也会创建一个新文件。让我们考虑下面的代码片段,我们将一行追加到上一节中的文本文件write_file.txt中:

if __name__ == "__main__": 
   # open text file to append
   file = open('write_file.txt', 'a') 
   # append a line from the file
   file.write('This is a line appended to the file\n') 
   file.close() 
   file = open('write_file.txt', 'r') 
   data = file.read() 
   print(data) 
   file.close()

当执行前面的代码片段(可与本章一起下载的append_to_file.py)时,字符串This is a line appended to the file将被追加到文件的文本末尾。文件的内容将包括以下内容:

I am excited to learn Python using Raspberry Pi Zero
 This is a line appended to the file

寻找

一旦文件被打开,文件 I/O 中使用的文件指针会从文件的开头移动到文件的末尾。可以将指针移动到特定位置并从该位置读取数据。当我们对文件的特定行感兴趣时,这是非常有用的。让我们考虑上一个例子中的文本文件write_file.txt。文件的内容包括:

I am excited to learn Python using Raspberry Pi Zero
 This is a line appended to the file

让我们尝试跳过第一行,只读取第二行,使用seek

if __name__ == "__main__": 
   # open text file to read
   file = open('write_file.txt', 'r') 
   # read the second line from the file
   file.seek(53) 
   data = file.read() 
   print(data) 
   file.close()

在前面的例子中(可与本章一起下载的seek_in_file.py),seek函数用于将指针移动到字节53,即第一行的末尾。然后文件的内容被读取并存储到变量中。当执行这个代码片段时,输出如下所示:

This is a line appended to the file

因此,seek 使得移动文件指针到特定位置成为可能。

读取 n 个字节

seek函数使得将指针移动到特定位置并从该位置读取一个字节或n个字节成为可能。让我们重新阅读write_file.txt,并尝试读取句子I am excited to learn Python using Raspberry Pi Zero中的单词excited

if __name__ == "__main__": 
   # open text file to read and write 
   file = open('write_file.txt', 'r') 
   # set the pointer to the desired position 
   file.seek(5) 
   data = file.read(1) 
   print(data) 
   # rewind the pointer
   file.seek(5) 
   data = file.read(7) 
   print(data) 
   file.close()

前面的代码可以通过以下步骤来解释:

  1. 第一步,使用read标志打开文件,并将文件指针设置为第五个字节(使用seek)——文本文件内容中字母e的位置。
  2. 现在,我们通过将文件作为参数传递给read函数来从文件中读取一个字节。当整数作为参数传递时,read函数会从文件中返回相应数量的字节。当没有传递参数时,它会读取整个文件。如果文件为空,read函数会返回一个空字符串:
file.seek(5) 
       data = file.read(1) 
       print(data)
  1. 在第二部分中,我们尝试从文本文件中读取单词excited。我们将指针的位置倒回到第五个字节。然后我们从文件中读取七个字节(单词excited的长度)。
  2. 当执行代码片段时(可与本章一起下载的seek_to_read.py),程序应该打印字母e和单词excited
file.seek(5) 
       data = file.read(7) 
       print(data)

r+

我们讨论了使用rw标志读取和写入文件。还有另一个叫做r+的标志。这个标志使得可以对文件进行读取和写入。让我们回顾一个例子,以便理解这个标志。

让我们再次回顾write_file.txt的内容:

I am excited to learn Python using Raspberry Pi Zero
 This is a line appended to the file

让我们修改第二行,改为:This is a line that was modified。代码示例可与本章一起下载(seek_to_write.py)。

if __name__ == "__main__": 
   # open text file to read and write 
   file = open('write_file.txt', 'r+') 
   # set the pointer to the desired position 
   file.seek(68) 
   file.write('that was modified \n') 
   # rewind the pointer to the beginning of the file
   file.seek(0) 
   data = file.read() 
   print(data) 
   file.close()

让我们回顾一下这个例子是如何工作的:

  1. 这个例子的第一步是使用r+标志打开文件。这使得可以对文件进行读取和写入。
  2. 接下来是移动到文件的第 68 个字节
  3. 在这个位置将that was modified字符串写入文件。字符串末尾的空格用于覆盖第二句原始内容。
  4. 现在,文件指针已设置到文件的开头,并读取其内容。
  5. 当执行前面的代码片段时,修改后的文件内容将打印到屏幕上,如下所示:
I am excited to learn Python using Raspberry Pi Zero
 This is a line that was modified

还有另一个a+标志,它可以使数据追加到文件末尾并同时进行读取。我们将留给读者使用到目前为止讨论的示例来弄清楚这一点。

我们已经讨论了 Python 中读取和写入文件的不同示例。如果没有足够的编程经验,可能会感到不知所措。我们强烈建议通过本章提供的不同代码示例进行实际操作。

读者的挑战

使用a+标志打开write_file.txt文件(在不同的示例中讨论),并向文件追加一行。使用seek设置文件指针并打印其内容。您可以在程序中只打开文件一次。

使用with关键字

到目前为止,我们讨论了可以用于以不同模式打开文件的不同标志。我们讨论的示例遵循一个常见模式——打开文件,执行读/写操作,然后关闭文件。有一种优雅的方式可以使用with关键字与文件交互。

如果在与文件交互的代码块执行过程中出现任何错误,with关键字会确保在退出代码块时关闭文件并清理相关资源。让我们通过一个示例来回顾with关键字:

if __name__ == "__main__": 
   with open('write_file.txt', 'r+') as file: 
         # read the contents of the file and print to the screen 
         print(file.read()) 
         file.write("This is a line appended to the file") 
         #rewind the file and read its contents 
         file.seek(0) 
         print(file.read()) 
   # the file is automatically closed at this point 
   print("Exited the with keyword code block")

在前面的示例(with_keyword_example)中,我们跳过了关闭文件,因为with关键字在缩进的代码块执行完毕后会自动关闭文件。with关键字还会在由于错误离开代码块时关闭文件。这确保了资源在任何情况下都能得到适当的清理。接下来,我们将使用with关键字进行文件 I/O。

configparser

让我们讨论一些在使用树莓派开发应用程序时特别有用的 Python 编程方面。其中一个工具是 Python 中提供的configparserconfigparser模块(docs.python.org/3.4/library/configparser.html)用于读取/写入应用程序的配置文件。

在软件开发中,配置文件通常用于存储常量,如访问凭据、设备 ID 等。在树莓派的上下文中,configparser可以用于存储所有使用的 GPIO 引脚列表,通过 I²C 接口接口的传感器地址等。让我们讨论三个示例,学习如何使用configparser模块。在第一个示例中,我们将使用configparser创建一个config文件。

在第二个示例中,我们将使用configparser来读取配置值,在第三个示例中,我们将讨论修改配置文件的最终示例。

示例 1

在第一个示例中,让我们创建一个配置文件,其中包括设备 ID、使用的 GPIO 引脚、传感器接口地址、调试开关和访问凭据等信息:

import configparser 
if __name__ == "__main__": 
   # initialize ConfigParser 
   config_parser = configparser.ConfigParser() 
   # Let's create a config file 
   with open('raspi.cfg', 'w') as config_file: 
         #Let's add a section called ApplicationInfo 
         config_parser.add_section('AppInfo') 
         #let's add config information under this section 
         config_parser.set('AppInfo', 'id', '123') 
         config_parser.set('AppInfo', 'gpio', '2') 
         config_parser.set('AppInfo', 'debug_switch', 'True') 
         config_parser.set('AppInfo', 'sensor_address', '0x62') 
         #Let's add another section for credentials 
         config_parser.add_section('Credentials') 
         config_parser.set('Credentials', 'token', 'abcxyz123') 
         config_parser.write(config_file) 
   print("Config File Creation Complete")

让我们详细讨论前面的代码示例(可与本章一起下载作为config_parser_write.py):

  1. 第一步是导入configparser模块并创建ConfigParser类的实例。这个实例将被称为config_parser
config_parser = configparser.ConfigParser()
  1. 现在,我们使用with关键字打开名为raspi.cfg的配置文件。由于文件不存在,将创建一个新的配置文件。
  2. 配置文件将包括两个部分,即AppInfoCredentials
  3. 可以使用add_section方法创建两个部分,如下所示:
config_parser.add_section('AppInfo') 
       config_parser.add_section('Credentials')
  1. 每个部分将包含不同的常量集。可以使用set方法将每个常量添加到相关部分。set方法的必需参数包括参数/常量将位于的部分名称,参数/常量的名称及其对应的值。例如:id参数可以添加到AppInfo部分,并分配值123如下:
config_parser.set('AppInfo', 'id', '123')
  1. 最后一步是将这些配置值保存到文件中。这是使用config_parser方法write完成的。一旦程序退出with关键字下的缩进块,文件就会关闭:
config_parser.write(config_file)

我们强烈建议尝试自己尝试代码片段,并将这些片段用作参考。通过犯错误,您将学到很多,并可能得出比这里讨论的更好的解决方案。

执行上述代码片段时,将创建一个名为raspi.cfg的配置文件。配置文件的内容将包括以下内容所示的内容:

[AppInfo] 
id = 123 
gpio = 2 
debug_switch = True 
sensor_address = 0x62 
[Credentials] 
token = abcxyz123

示例 2

让我们讨论一个示例,我们从先前示例中创建的配置文件中读取配置参数:

import configparser 
if __name__ == "__main__": 
   # initialize ConfigParser 
   config_parser = configparser.ConfigParser() 
   # Let's read the config file 
   config_parser.read('raspi.cfg') 
   # Read config variables 
   device_id = config_parser.get('AppInfo', 'id') 
   debug_switch = config_parser.get('AppInfo', 'debug_switch') 
   sensor_address = config_parser.get('AppInfo', 'sensor_address') 
   # execute the code if the debug switch is true 
   if debug_switch == "True":
         print("The device id is " + device_id) 
         print("The sensor_address is " + sensor_address)

如果配置文件以所示格式创建,ConfigParser类应该能够解析它。实际上并不一定要使用 Python 程序创建配置文件。我们只是想展示以编程方式同时为多个设备创建配置文件更容易。

上述示例可与本章一起下载(config_parser_read.py)。让我们讨论一下这个代码示例是如何工作的:

  1. 第一步是初始化名为config_parserConfigParser类的实例。
  2. 第二步是使用实例方法read加载和读取配置文件。
  3. 由于我们知道配置文件的结构,让我们继续阅读位于AppInfo部分下可用的一些常量。可以使用get方法读取配置文件参数。必需的参数包括配置参数所在的部分以及参数的名称。例如:配置id参数位于AppInfo部分下。因此,该方法的必需参数包括AppInfoid
device_id = config_parser.get('AppInfo', 'id')
  1. 现在配置参数已读入变量中,让我们在程序中使用它。例如:让我们测试debug_switch变量(用于确定程序是否处于调试模式)并打印从文件中检索到的其他配置参数:
if debug_switch == "True":
           print("The device id is " + device_id) 
           print("The sensor_address is " + sensor_address)

示例 3

让我们讨论一个示例,我们想要修改现有的配置文件。这在需要在执行固件更新后更新配置文件中的固件版本号时特别有用。

以下代码片段可与本章一起下载,文件名为config_parser_modify.py

import configparser 
if __name__ == "__main__": 
   # initialize ConfigParser 
   config_parser = configparser.ConfigParser() 
   # Let's read the config file 
   config_parser.read('raspi.cfg') 
   # Set firmware version 
   config_parser.set('AppInfo', 'fw_version', 'A3') 
   # write the updated config to the config file 
   with open('raspi.cfg', 'w') as config_file: 
       config_parser.write(config_file)

让我们讨论一下这是如何工作的:

  1. 与往常一样,第一步是初始化ConfigParser类的实例。使用read方法加载配置文件:
# initialize ConfigParser 
       config_parser = configparser.ConfigParser() 
       # Let's read the config file 
       config_parser.read('raspi.cfg')
  1. 使用set方法更新必需参数(在先前的示例中讨论):
# Set firmware version 
       config_parser.set('AppInfo', 'fw_version', 'A3')
  1. 使用write方法将更新后的配置保存到配置文件中:
with open('raspi.cfg', 'w') as config_file: 
          config_parser.write(config_file)

读者的挑战

使用示例 3 作为参考,将配置参数debug_switch更新为值False。重复示例 2,看看会发生什么。

读取/写入 CSV 文件

在本节中,我们将讨论读取/写入 CSV 文件。这个模块(docs.python.org/3.4/library/csv.html)在数据记录应用程序中非常有用。由于我们将在下一章讨论数据记录,让我们回顾一下读取/写入 CSV 文件。

写入 CSV 文件

让我们考虑一个场景,我们正在从不同的传感器读取数据。这些数据需要记录到一个 CSV 文件中,其中每一列对应于来自特定传感器的读数。我们将讨论一个例子,其中我们在 CSV 文件的第一行记录值123456789,第二行将包括值RedGreenBlue

  1. 写入 CSV 文件的第一步是使用with关键字打开 CSV 文件:
with open("csv_example.csv", 'w') as csv_file:
  1. 下一步是初始化 CSV 模块的writer类的实例:
csv_writer = csv.writer(csv_file)
  1. 现在,通过创建一个包含需要添加到行中的所有元素的列表,将每一行添加到文件中。例如:第一行可以按如下方式添加到列表中:
csv_writer.writerow([123, 456, 789])
  1. 将所有内容放在一起,我们有:
import csv 
       if __name__ == "__main__": 
          # initialize csv writer 
          with open("csv_example.csv", 'w') as csv_file: 
                csv_writer = csv.writer(csv_file) 
                csv_writer.writerow([123, 456, 789]) 
                csv_writer.writerow(["Red", "Green", "Blue"])
  1. 当执行上述代码片段(与本章一起提供的csv_write.py可下载)时,在本地目录中创建了一个 CSV 文件,其中包含以下内容:
123,456,789
 Red,Green,Blue

从 CSV 文件中读取

让我们讨论一个例子,我们读取上一节中创建的 CSV 文件的内容:

  1. 读取 CSV 文件的第一步是以读模式打开它:
with open("csv_example.csv", 'r') as csv_file:
  1. 接下来,我们初始化 CSV 模块的reader类的实例。CSV 文件的内容被加载到对象csv_reader中:
csv_reader = csv.reader(csv_file)
  1. 现在 CSV 文件的内容已加载,可以按如下方式检索 CSV 文件的每一行:
for row in csv_reader: 
           print(row)
  1. 将所有内容放在一起:
import csv 
       if __name__ == "__main__": 
          # initialize csv writer 
          with open("csv_example.csv", 'r') as csv_file: 
                csv_reader = csv.reader(csv_file) 
                for row in csv_reader: 
                      print(row)
  1. 当执行上述代码片段(与本章一起提供的csv_read.py可下载)时,文件的内容将逐行打印,其中每一行都是一个包含逗号分隔值的列表:
['123', '456', '789']
 ['Red', 'Green', 'Blue']

Python 物联网入门指南(五)(2)https://developer.aliyun.com/article/1507234

相关实践学习
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
相关文章
|
2月前
|
SQL 关系型数据库 数据库
Python SQLAlchemy模块:从入门到实战的数据库操作指南
免费提供Python+PyCharm编程环境,结合SQLAlchemy ORM框架详解数据库开发。涵盖连接配置、模型定义、CRUD操作、事务控制及Alembic迁移工具,以电商订单系统为例,深入讲解高并发场景下的性能优化与最佳实践,助你高效构建数据驱动应用。
297 7
|
3月前
|
测试技术 开发者 Python
Python单元测试入门:3个核心断言方法,帮你快速定位代码bug
本文介绍Python单元测试基础,详解`unittest`框架中的三大核心断言方法:`assertEqual`验证值相等,`assertTrue`和`assertFalse`判断条件真假。通过实例演示其用法,帮助开发者自动化检测代码逻辑,提升测试效率与可靠性。
331 1
|
4月前
|
API 数据安全/隐私保护 开发者
Python自定义异常:从入门到实践的轻松指南
在Python开发中,自定义异常能提升错误处理的精准度与代码可维护性。本文通过银行系统、电商库存等实例,详解如何创建和使用自定义异常,涵盖异常基础、进阶技巧、最佳实践与真实场景应用,助你写出更专业、易调试的代码。
172 0
|
4月前
|
IDE 开发工具 数据安全/隐私保护
Python循环嵌套:从入门到实战的完整指南
循环嵌套是Python中处理多维数据和复杂逻辑的重要工具。本文通过实例讲解嵌套循环的基本用法、常见组合、性能优化技巧及实战应用,帮助开发者掌握其核心思想,避免常见错误,并探索替代方案与进阶方向。
373 0
|
2月前
|
Cloud Native 算法 API
Python API接口实战指南:从入门到精通
🌟蒋星熠Jaxonic,技术宇宙的星际旅人。深耕API开发,以Python为舟,探索RESTful、GraphQL等接口奥秘。擅长requests、aiohttp实战,专注性能优化与架构设计,用代码连接万物,谱写极客诗篇。
Python API接口实战指南:从入门到精通
|
2月前
|
存储 Java 调度
Python定时任务实战:APScheduler从入门到精通
APScheduler是Python强大的定时任务框架,通过触发器、执行器、任务存储和调度器四大组件,灵活实现各类周期性任务。支持内存、数据库、Redis等持久化存储,适用于Web集成、数据抓取、邮件发送等场景,解决传统sleep循环的诸多缺陷,助力构建稳定可靠的自动化系统。(238字)
486 1
|
3月前
|
调度 数据库 Python
Python异步编程入门:asyncio让并发变得更简单
Python异步编程入门:asyncio让并发变得更简单
189 5
|
6月前
|
Python
Python字符串格式化利器:f-strings入门指南
Python字符串格式化利器:f-strings入门指南
342 80
|
3月前
|
数据采集 存储 XML
Python爬虫入门(1)
在互联网时代,数据成为宝贵资源,Python凭借简洁语法和丰富库支持,成为编写网络爬虫的首选。本文介绍Python爬虫基础,涵盖请求发送、内容解析、数据存储等核心环节,并提供环境配置及实战示例,助你快速入门并掌握数据抓取技巧。

推荐镜像

更多