小菜鸟的python学习之路(9)

学习阶梯

《Python编程:从入门到实践》
  • 第一部分:基础知识

第10章 文件和异常
  • 处理文件:让程序能够快速地分析大量的数据

  • 错误处理:避免程序在面对意外情形时崩溃

  • 异常:用于管理程序运行时出现的错误

  • 模块json:能够保存用户数据,以免在程序停止运行后丢失。

学习处理文件和保存数据可以让程序使用起来更容易:用户将能够选择输入什么样的数据,以及在什么时候输入;用户使用你的程序做一些工作后,可将程序关闭,以后再接着往下做。

学习处理异常可帮助应对文件不存在的情形,以及处理其他可能导致程序崩溃的问题。这让程序在面对错误的数据时更健壮,提高程序的适用性、可用性和稳定性。

  1. 从文件中读取数据

每当需要分析或修改存储在文件中的信息时,读取文件都很有用。

要使用文本文件中的信息,首先需要将信息读取到内存中。

可以一次性读取文件的全部内容,也可以每次一行的方式逐步读取。

  • 读取整个文件

pi_digiits.txt

3.141592653589793238462643383279

file_reader.py

下面代码中单引号内的路径是文本文档所在的路径,根据自己文件所在位置进行修改。

with open('G:\Python\第10章\pi_digits.txt') as file_object:
    contents=file_object.read()
    print(contents)

函数open()接受一个参数:要打开的文件的名称。Python在当前执行的文件所在的目录中查找指定的文件。这里运行的file_reader.py,因此Python在file_reader.py所在的目录中查找pi_digits.txt。函数open()返回一个表示文件的对象。Python将这个对象存储在我们将在后面使用的变量中。

关键字with在不再需要访问文件后将其关闭。在这个程序汇总,注意到我们调用了open(),但是没有调用close();也可以调用open()和close()俩打开和关闭文件

如果程序存在bug也许会导致close()语句不被执行,文件也不会被关闭。

文件未被正确关闭也许会导致里面数据的丢失和受损。
如果在程序中过早地调用close(),你会发现需要使用文件时文件关闭无法访问,这会导致更多的错误。并非在任何情况下都能轻松确定关闭文件的恰当时机,但通过使用前面所展示的结构,可让Python去确定:只需要打开文件,并在需要时使用它,Python自会在合适的时候自动将其关闭。

踩坑
文本文档放在了程序文件所在的目录下,没有加具体路径,只是’pi_digits.txt’这样写的,然后报错;

后来把路径补全,可以正常运行了;

尝试把文本文档换个位置,放在桌面,路径是’C:\Users\Administrator\Desktop\pi_digits.txt’还是报错,目前没搞明白是为什么,可能跟用的函数有关。

上面程序输入多出了一个空行。多出的原因是因为read()到达文档末尾时返回一个空字符串,而将这个空字符串显示出来时就是一个空行。要删除多出来的空行,可以在输出语句中使用rstrip():

with open('G:\Python\第10章\pi_digits.txt') as file_object:
    contents=file_object.read()
    print(contents.rstrip())

Python方法rstrip()删除(剥除)字符串末尾的空白。


  • 文件路径

让Python打开不与程序文件位于同一个目录中的文件,需要提供文件路径,让Python到系统的特定位置去查找。

在Windows系统中,在文件路径中使用反斜杠(/)而不是斜杠(\):

with open('第10章\pi_digits.txt') as file_object:
    contents=file_object.read()
    print(contents.rstrip())

明白了上面踩坑的原因…

绝对路径:将文件在计算机中的准确位置告诉Python,这样不需要考虑当前运行的程序存储在什么位置了。

在相对路径行不通的时候,可使用绝对路径。Python会到你提供的完整路径去查找。绝对路径通常比相对路径更长,因此将其存储在一个变量中,再将其传递给open():

file_path=r'C:\Users\Administrator\Desktop\pi_digits.txt'
with open(file_path) as file_object:
    contents = file_object.read()
    print(contents.rstrip())

这里有个问题,单引号内是路径,单引号前面要加一个字母r要不会报错,目前不知道为什么。

通过使用绝对路径,可以读取系统任何地方的文件。就目前而言,最简单的做法是,要么将数据文件存储在程序文件所在的目录,要么将其存储在程序文件所在目录下的一个文件夹中。


  • 逐行读取

读取文件时,常常需要检查其中的每一行:你可能要在文件中查找特定的信息,或者要以某种方式修改文件中的文本。要以每次一行的方式检查文件,可对文件对象使用for循环:

filename='第10章\pi_digits.txt'

with open(filename) as file_object:
    for line in file_object:
        print(line.rstrip().rjust(12))

输出中的rjust()函数只是为了输出的时候右对齐好看,可以去掉~ヾ(◍°∇°◍)ノ゙

输出结果如下所示:
3.1415926535
  8979323846
  2643383279

这里filename表示的不是真实的文件名,只是一行字符串,可以表示去哪里查找这些文件的字符串,可以根据实际情况进行替换查找。

关键字with让Python妥当地打开和关闭文件。


  • 创建一个包含各行内容的列表

使用关键字with时,open()返回的文件对象只在with代码块内可用。

如果要在with代码块外为访问文件的内容,可在with代码块内将文件的各行存储在一个列表中,并在with代码块外使用该列表:你可以立即处理文件的各个部分,也可推迟到程序后面再处理。

filename='第10章\pi_digits.txt'

with open(filename) as file_object:
    lines=file_object.readlines()
    
for line in lines:
    print(line.rstrip().rjust(12))

这里不明白的是变量lines是默认设置为列表了吗?就因为文件是一行一行读进去的吗?那么为什么不是其它类型呢?这里不太理解

方法readlines()从文件中读取每一行,并将其存储在一个列表中,可以使用一个简单的for循环来打印lines中的各行。由于列表lines的每个元素都对应于文件中的一行,因此输出内容完全一致。


  • 使用文件的内容

将文件读取到内存中后,就可以以任何方式使用这些数据了。

pi_string.py

filename='第10章\pi_digits.txt'

with open(filename) as file_object:
    lines=file_object.readlines()

pi_string=''
for line in lines:
    pi_string+=line.strip()

print(pi_string)
print(len(pi_string))

:读取文本文件时,Python将其中的所有文本都解读为字符串。如果你读取的是数字,并将其作为数值使用,就必须使用函数int()将其转换为整数,或使用float()将其转化为浮点数。


  • 包含一百万位的大型文件

处理一个文本文件,其中包含精确到小数点后1 000 000位而不是30位的圆周率值,也可创建一个包含所有这些数字的字符串。这里的程序只打印到小数点后50位,以免终端为显示全部1000 000位而不断翻滚,最后输出长度确认文本文件中的字符数是否与预期一致。

filename='第10章\pi_million_digits.txt'
with open(filename) as file_object:
    lines = file_object.readlines()

pi_string=''
for line in lines:
    pi_string+=line.strip()

print(pi_string[:52]+"...")
print(len(pi_string))

对于你可处理的数据量,Python没有任何限制;只要系统的内存足够多,想处理多少数据都可以


  • 圆周率值中包含你的生日吗?

可将生日表示为一个由数字组成的字符串,再检查这个字符串是否包含在pi_string中:

filename='第10章\pi_million_digits.txt'

with open(filename) as file_object:
    lines=file_object.readlines()

pi_string=''
for line in lines:
    pi_string+=line.strip()

birthday=input("Enter your birthday, in the form mmddyy:")
if birthday in pi_string:
    print("Your birthday appears in the first million digits of pi!")
else:
    print("Your birthday does not appear in the first million digits of pi.")

练习

10-1 Python 学习笔记:在文本编辑器中新建一个文件,写几句话来总结一下你至此学到的 Python 知识,其中每一行都以“In Python you can”打头。将这个文件命名为learning_python.txt,并将其存储到为完成本章练习而编写的程序所在的目录中。

编写一个程序,它读取这个文件,并将你所写的内容打印三次:

第一次打印时读取整个文件;

第二次打印时遍历文件对象;

第三次打印时将各行存储在一个列表中,再在 with 代码块外打印它们。

filename='第10章\learning_python.txt'

with open(filename) as file_object:
    contents=file_object.read()
    print("第一次打印读取整个文件:")
    print(contents.strip())

with open(filename) as file_object:
    print("第二次打印遍历文件对象:")
    for line in file_object:
        print(line.strip())

with open(filename) as file_object:  
    lines=file_object.readlines()
print("第三次打印将各行存储在一个列表中,再在with代码外打印它们:")
for line in lines:
    print(line.strip())

这里本来想只写一遍with open的,但是发现,运行正常,但是第二三次没有打印,思考了一下,觉得应该是文件关闭,后面无法进行文件读取,试用了一下open()和close()发现也是一样,所以写了三遍with open用来进行文件读取,希望以后能遇到更好的解决办法

10-2 C语言学习笔记:可使用方法 replace()将字符串中的特定单词都替换为另一个单词。下面是一个简单的示例,演示了如何将句子中的’dog’替换为’cat’:

>>> message = "I really like dogs." 
>>> message.replace('dog', 'cat') 
'I really like cats.' 

读取你刚创建的文件 learning_python.txt 中的每一行,将其中的 Python 都替换为另一门语言的名称,如 C。将修改后的各行都打印到屏幕上。

filename='第10章\learning_python.txt'

with open(filename) as file_object:
    lines=file_object.readlines()

for line in lines:
    line=line.replace('Python','C')
    print(line.strip())
  1. 写入文件

保存数据的最简单的方式之一是将其写入到文件中。通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出也依然存在:你可以在程序结束运行后查看这些输出,可与别人分享输出文件,还可编写程序来将这些输出读取到内存中并进行处理。

  • 写入空文件

要将文本写入文件,在调用open()时需要应该另一个实参,告诉Python你呀写入打开的文件。

write_message.py

filename='programing.txt'

with open(filename,'w') as file_object:
    file_object.write("I love programming.")

这里当filename='文件名’时,默认保存在G:\python下,filename=‘文件夹名称\文件名’,则保存在G:\Python\文件夹名称 下

上述程序在调用open()时提供了两个实参。第一个实参是要打开的文件名称;第二个实参(‘w’)告诉Python,要以写入模式打开这个文件。打开文件时,可指定读取模式(‘r’)、写入模式(‘w’)、附加模式(‘a’)或让你能够读取和写入文件的模式(‘r+’)。如果你省略了模式实参,Python将以默认的只读模式打开文件。

如果要写入的文件不存在,函数open()将自动创建它。

以写入(‘w’)模式打开文件时千万要小心,因为如果指定的文件已经存在,Python将在返回文件对象前清空该文件。

**注:**Python只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须系那是用函数str()将其转换为字符串格式。


  • 写入多行

函数write()不会在你写入的文本末尾添加换行符,因此写入多行时没有指定换行符。

filename='第10章\programming.txt'

with open(filename,'w') as file_object:
    file_object.write("I love programming.")
    file_object.write("I love creating new games.")

要让每个字符串都单独占一行,需要在write()语句中包含换行符:

filename='programming.txt'

with open(filename,'w') as file_object:
    file_object.write("I love programming.\n")
    file_object.write("I love creating new games.\n")

与显示到终端的输出一样,还可以使用空格、制表符和空行来设置这些输出的格式。


  • 附加到文件

如果要给文件添加内容,而不是覆盖原有的内容,可以以附加模式打开文件。以附加模式打开文件时,Python不会在返回文件对象前清空文件,而写入到文件的行都将添加到文件末尾。如果指定的文件不存在,Python将为你创建一个空文件。

filename='programming.txt'

with open(filename,'a') as file_object:
    file_object.write("I also love finding meaning in large datasets.\n")
    file_object.write("I love creating apps that can run in a browser.\n")

打开文件时指定了实参’a’,以便将内容附加到文件末尾,而不是覆盖文件原来的内容。

练习

10-3 访客:编写一个程序,提示用户输入其名字;用户作出响应后,将其名字写入到文件 guest.txt 中。

filename='第10章\guest.txt'

name=input("Please enter your name:")
with open(filename,'a') as file_object:
    file_object.write(name.title()+"\n")

10-4 访客名单:编写一个 while 循环,提示用户输入其名字。用户输入其名字后,在屏幕上打印一句问候语,并将一条访问记录添加到文件 guest_book.txt 中。确保这个文件中的每条记录都独占一行。

filename='第10章\guest.txt'

prompt="\n(Enter 'quit' when you are finished.)"
prompt+="\nPlease enter your name:"
name=" "
while name!='quit':
    name = input(prompt)
    if name!='quit':
        with open(filename,'a') as fileobject:
            fileobject.write(name.title()+"\n")
            print("Hello, "+name.title()+"!")

10-5 关于编程的调查:编写一个 while 循环,询问用户为何喜欢编程。每当用户输入一个原因后,都将其添加到一个存储所有原因的文件中。

filename=r'第10章\reasons.txt'

prompt="\n(Enter 'quit' when you are finished.)"
prompt+="\nWhy do you like programming?\n"

reason=" "
while reason!='quit':
    reason=input(prompt)
    if reason!='quit':
        with open(filename,'a') as fileobject:
            fileobject.write(reason+"\n")

不解:上面程序突然报错OSError: [Errno 22] Invalid argument,但是写的代码跟之前的差不多,在文件名前加了一个“r”就好了,不知道为什么

  1. 异常

Python使用被称为异常的特殊对象来管理程序执行期间发生的错误。

每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果编写了处理该异常的代码,程序将继续运行;如果未对异常进行处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。

异常是使用try-except代码块让Python执行指定的操作,同时告诉Python发生异常时怎么办。使用了try-except代码块时,即便出现异常,程序也将继续运行:显示编写的又好的错误消息,而不是另用户迷惑的traceback。

  • 处理ZeroDivisionError异常

不能将一个数字除以0

division.py

print(5/0)

程序报错显示trackback

Traceback (most recent call last):
  File "division.py", line X, in <module>
    print(5/0)
ZeroDivisionError: division by zero

在这里,ZeroDivisionError是一个异常对象。Python无法按你的要求做时,就会创建这种对象,停止运行程序,并指出引发了哪种异常,可以根据这些信息对程序进行修改。


  • 使用try-except代码块

当认为可能发生错误时,可以编写一个try-except代码块来处理可能引发的异常。让Python尝试运行一些代码,并告诉如果这些代码引发了指定的异常,该怎么办。

try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")

如果try代码块中的代码运行起来没有问题,Python将跳过except代码块;如果try代码块中的代码导致了错误,Python将查找这样的except代码块,并运行其中的代码,即其中指定的错误与引发的错误相同。

如果try-except代码块后面还有其他代码,程序将接着运行,因为已经告诉了Python如何处理这种错误。


  • 使用异常避免崩溃

发生错误时,如果程序还有工作没有完成,妥善地处理错误就尤其重要。这种情况经常会出现在要求用户提供输入的程序中;如果程序能够妥善地处理无效输入,就能提示用户提供有效输入,而不至于崩溃。

下面来创建一个只执行除法运算的简单计算器:

print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
    first_number=input("\nFirst number: ")
    if first_number == 'q':
        break
    second_number=input("second number: ")
    if second_number == 'q':
        break
    answer= int(first_number) / int(second_number)
    print(answer)

程序崩溃可不好,但让用户看到traceback也不是好主意。不懂技术的用户会被它们搞糊涂,而且如果用户怀有恶意,他会通过traceback获悉你不希望他知道的信息。例如,他将知道你的程序文件的名称,还将看到部分不能正确运行的代码。有时候,训练有素的攻击者可根据这些信息判断出可对你的代码发起什么样的攻击。

报错如下:

Traceback (most recent call last):
  File "division.py", line X, in <module>
    answer= int(first_number) / int(second_number)
ZeroDivisionError: division by zero

  • else 代码块

通过将可能引发错误的代码放在try-except代码块中,可提高这个程序抵御错误的能力。错误是执行除法运算的代码行导致的,因此我们需要将它放到try-except代码块中。这个示例还包含一个else代码块;依赖于try代码块成功执行的代码都应放到else代码块中

print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")
while True:
    first_number=input("\nFirst number: ")
    if first_number == 'q':
        break
    second_number=input("second number: ")
    if second_number == 'q':
        break
    try:
        answer= int(first_number) / int(second_number)
    except ZeroDivisionError:
        print("You can't divide by 0!")
    else:
        print(answer)

通过预测可能发生错误的代码,可编写健壮的程序,它们即便面临无效数据或缺少资源,也能继续运行,从而能够抵御无意的用户错误和恶意的攻击.


  • 处理FileNotFoundError异常

使用文件时,一种常见的问题是找不到文件:你要查找的文件可能在其他地方、文件名可能不正确或者这个文件根本就不存在。对于所有这些情形,都可使用try-except代码块以直观的方式进行处理。

alice.py

filename='alice.txt'

with open(filename) as file_object:
    contents=file_object.read()

python无法读取不存在的文件,因此引发一个异常:

Traceback (most recent call last):
  File "alice.py", line X, in <module>
    with open(filename) as file_object:
FileNotFoundError: [Errno 2] No such file or directory: 'alice.txt'

处理异常

filename='alice.txt'
try:
    with open(filename) as file_object:
        contents=file_object.read()
except FileNotFoundError:
    message="Sorry, the file "+filename+" does not exist."
    print(message)

如果文件不存在,这个程序什么都不做,因此错误处理代码的意义不大。


  • 分析文本

使用方法split()

title="Alice in Wonderland"
word=title.split()
print(word)

方法split()以空格为分隔符将字符串分拆成多个部分,并将这些部分都存储到一个列中。结果是一个包含字符串中所有单词的列表,虽然有些单词可能包含标点。

filename='alice.txt'
filepath='第10章/alice.txt'
try:
    with open(filepath,'rb') as file_object:
        contents=file_object.read()
except FileNotFoundError:
    message="Sorry, the file "+filename+" does not exist."
    print(message)
else:
    #计算文件大致包含多少个单词
    words=contents.split()
    num_words=len(words)
    print("The file "+filename+" has about "+str(num_words)+" words.")

不解:一是,文件名中间用“\”报错,和后面的文件名称的第一个字母a连在一起出问题;二是,打开文件的第二个参数报错,得用rb了

r —只读模式
r+ —可读可写模式
rb —以二进制方式读取文件
w —只写模式
w+ —写读模式
wb+ —以二进制方式读写文件
a —追加写模式
a+ —追加读写模式


  • 使用多个文件

word_count.py

def count_words(filename,filepath):
    """计算一个文件大致包含多少个单词"""
    try:
        with open(filepath,'rb') as file_object:
            contents=file_object.read()
    except FileNotFoundError:
        message="Sorry, the file "+filename+" does not exist."
        print(message)
    else:
        words=contents.split()
        num_words=len(words)
        print("The file "+filename+" has about "+str(num_words)+" words.")

filename='alice.txt'
filepath='第10章/alice.txt'
count_words(filename,filepath)

编写简单的循环,对多个文件调用函数

def count_words(filename):
    """计算一个文件大致包含多少个单词"""
    try:
        with open(filename,'rb') as file_object:
            contents=file_object.read()
    except FileNotFoundError:
        message="Sorry, the file "+filename+" does not exist."
        print(message)
    else:
        #计算文件大致包含多少个单词
        words=contents.split()
        num_words=len(words)
        print("The file "+filename+" has about "+str(num_words)+" words.")

filenames=['Alice in Wonderland.txt','Hamlet.txt','siddhartha.txt','Romeo and Juliet.txt','The Thousand and One Nights.txt']
for filename in filenames:
    count_words(filename)

文件siddhartha.txt不存在,但这丝毫不影响这个程序处理其他文件。

输出如下:

The file Alice in Wonderland.txt has about 29647 words.
The file Hamlet.txt has about 35065 words.
Sorry, the file siddhartha.txt does not exist.
The file Romeo and Juliet.txt has about 29092 words.
The file The Thousand and One Nights.txt has about 266005 words.

使用try-except代码块提供了两个重要的优点:避免让用户看到traceback;让程序能够继续分析能够找到的其他文件。如果不捕获因找不到siddhartha.txt而引发的FileNotFoundError异常,用户将看到完整的traceback,而程序将在尝试分析Siddhartha后停止运行——根本不分析Romeo and Juliet和The Thousand and One Nights。


  • 失败时一声不吭

并非每次捕获到异常时都需要告诉用户,有时候希望程序在发生异常时不提示,就像什么都没有发生一样继续运行。在except代码块中明确地告诉Python什么都不要做,可在代码块中使用pass语句来让Python什么都不要做。

def count_words(filename):
    """计算一个文件大致包含多少个单词"""
    try:
        with open(filename,'rb') as file_object:
            contents=file_object.read()
    except FileNotFoundError:
        pass
    else:
        #计算文件大致包含多少个单词
        words=contents.split()
        num_words=len(words)
        print("The file "+filename+" has about "+str(num_words)+" words.")

filenames=['Alice in Wonderland.txt','Hamlet.txt','siddhartha.txt','Romeo and Juliet.txt','The Thousand and One Nights.txt']
for filename in filenames:
    count_words(filename)

出现异常时,将执行except代码块中的代码,但什么都不会发生。错误发生时,不会出现traceback,也没有任何输出。pass语句还充当了占位符,它提醒你在程序的某个地方什么都没有做,并且以后也许要在这里做些什么。


  • 决定报告哪些错误

Python的错误处理结构让你能够细致地控制与用户分享错误信息的程度,要分享多少信息由你决定。编写得很好且经过详尽测试的代码不容易出现内部错误,如语法或逻辑错误,但只要程序依赖于外部因素,如用户输入、存在指定的文件、有网络链接,就有可能出现异常。这需要凭借经验判断该在程序的什么地方包含异常处理块,以及出现错误时该向用户提供多少相关的信息。

练习

10-6 加法运算:提示用户提供数值输入时,常出现的一个问题是,用户提供的是文本而不是数字。在这种情况下,当你尝试将输入转换为整数时,将引发 TypeError 异常。编写一个程序,提示用户输入两个数字,再将它们相加并打印结果。在用户输入的任何一个值不是数字时都捕获 TypeError 异常,并打印一条友好的错误消息。对你编写的程序进行测试:先输入两个数字,再输入一些文本而不是数字。

print("Please give me two numbers, and I'll add them.")
print("Enter 'q' to quit.")
try:
    first_number = input("\nEnter first number: ")
    second_number = input("Enter second number: ")
    if first_number.isdigit() and second_number.isdigit():
        answer=int(first_number) + int(second_number)
    else:
        answer=first_number + int(second_number)
except TypeError:
    print("TypeError")
except ValueError:
    print("ValueError")
else:
    print(first_number+" + "+second_number+" = "+str(answer))

**注:**练习10-6 我遇到了一些问题,我写完的第一份代码运行出错,报错显示不是TyperError而是ValueError,except改成了ValueError就没有报错了,可以正常运行,然后发现写的第一份代码跟10-7的练习题一样,如下10-7,然后我又重新看了一下10-6的题目,然后进行各种调整,想让程序报TypeError的错误,发现怎么都不行,上网查了一下有关TypeError的理解,发现自己理解的并没有问题,但是就是不行,感觉太浪费时间了,就先放弃了,因为我还有工作要做,最后突然有个想法尝试了一下,发现居然可以了,然后就有上面的代码,但是运行的话会有两种报错,所以写了两个except。具体结果如下表所示:

数字1数字2报错
字符字符ValueError
数字数字正常运行
字符数字TypeError
数字字符ValueError

10-7 加法计算器:将你为完成练习 10-6 而编写的代码放在一个 while 循环中,让用户犯错(输入的是文本而不是数字)后能够继续输入数字。

print("Please give me two numbers, and I'll add them.")
print("Enter 'q' to quit.")
while True:
    first_number = input("\nEnter first number: ")
    if first_number == 'q':
        break
    second_number = input("Enter second number: ")
    if second_number == 'q':
        break
    try:
        answer=int(first_number) + int(second_number)
    except ValueError:
        print("The input are not numbers!")
    else:
        print(first_number+" + "+second_number+" = "+str(answer))

10-8 猫和狗:创建两个文件 cats.txt 和 dogs.txt,在第一个文件中至少存储三只猫的名字,在第二个文件中至少存储三条狗的名字。编写一个程序,尝试读取这些文件,并将其内容打印到屏幕上。将这些代码放在一个 try-except 代码块中,以便在文件不存在时捕获 FileNotFound 错误,并打印一条友好的消息。将其中一个文件移到另一个地方,并确认 except 代码块中的代码将正确地执行。

def read_files(fileanme):
    try:
        with open(filename) as file_object:
            lines=file_object.readlines()
    except FileNotFoundError:
        print("File not found!")
    else:
        print("\n"+filename+": ")
        for line in lines:
            print(line.strip().title())

filename='cats.txt'
read_files(filename)
filename='dogs.txt'
read_files(filename)

10-9 沉默的猫和狗:修改你在练习 10-8 中编写的 except 代码块,让程序在文件不存在时一言不发。

def read_files(fileanme):
    try:
        with open(filename) as file_object:
            lines=file_object.readlines()
    except FileNotFoundError:
        pass
    else:
        print("\n"+filename+": ")
        for line in lines:
            print(line.strip().title())

filename='cats.txt'
read_files(filename)
filename='dogs.txt'
read_files(filename)

#10-10 常见单词:访问项目 Gutenberg(http://gutenberg.org/),并找一些你想分析的图书。下载这些作品的文本文件或将浏览器中的原始文本复制到文本文件中。你可以使用方法 count()来确定特定的单词或短语在字符串中出现了多少次。例如,下面的代码计算’row’在一个字符串中出现了多少次:

>>> line = "Row, row, row your boat" 
>>> line.count('row') 
2 
>>> line.lower().count('row')
3 

#请注意,通过使用 lower()将字符串转换为小写,可捕捉要查找的单词出现的所有次数,而不管其大小写格式如何。编写一个程序,它读取你在项目 Gutenberg 中获取的文件,并计算单词’the’在每个文件中分别出现了多少次。

def read_files(filename):
    try:
        with open(filename,'rb') as file_object:
            contents = file_object.read().lower()
            words = contents.split()
    except FileNotFoundError:
        print("File not found!")
    else:
        word=input("Please enter the word you want to count: \n")
        num=words.count(word.encode('utf-8'))
        print(word+" appears "+str(num))

filename='Alice in Wonderland.txt'
read_files(filename)

**踩坑:**这里的问题第一次遇见,一是lower()这个函数报错,后来找到原因是我用错了地方,应该用在分割文本之前,而不是分割之后,因为lower()函数用于字符串,不能用于一个个分割后的单词;

二是,文件写好可以运行,但是结果一直为0,然后我就是打开文本找到一个出现过的单词,输入进行尝试,发现还是为0,噢,我的程序有问题,然后我就一点点的找问题在哪,发现获取的文本前面都有一个字母’b’,然后分割后的每个单词前面也有个单词’b’,然后我就想办法去掉这个b,没有考虑为什么出现这个b,上网查了一下,才知道Python3的字符串的编码语言用的是unicode编码,由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干字节,如果要在网络上传输,或保存在磁盘上就需要把str变成以字节为单位的bytes,python对bytes类型的数据用带b前缀的单引号或双引号表示,然后尝试在单词前面加了一个字母b,居然好了,如下:

def read_files(filename):
    try:
        with open(filename,'rb') as file_object:
            contents = file_object.read().lower()
            words = contents.split()
    except FileNotFoundError:
        print("File not found!")
    else:
        num=words.count(b'alice')
        print(num)

filename='Alice in Wonderland.txt'
read_files(filename)

三是,我想这只能固定单词啊,我想灵活一点,可以让用户输入自己想要统计的单词,然后我就调整,输入单词,但是这个时候问题就来了,定义一个变量用来接收输入的内容,但是在变量前加字母b,程序就会以为这是一个新变量,然后以未定义报错,尝试各种方法加b都不行,然后我想到前面不是说是bytes类型嘛,然后我输入的内容是字符串,我转换一下应该可以吧,然后尝试把文本转换为字符串,失败率,没有深究,因为时间有限,然后就把输入的内容转换成bytes类型,运行正常了!转换方法如下:

(1)字符串转成bytes:str.encode(‘utf-8’)

(2)bytes转成bytes:bytes.decode(‘utf-8’)

  1. 存储数据

很多程序都要求用户输入某种信息,如让用户存储游戏首选项或提供要可视化的数据。程序都把用户提供的信息存储在列表和字典等数据结构中。

用户关闭程序时,几乎总是要保存他们提供的信息;一种简单的方式是使用模块json来存储数据。模块json让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。

还可以使用json在Python程序之间分享数据。更重要的是,JSON数据格式并非Python专用的,能够将以JSON格式存储的数据与使用其他编程语言的人分享。这是一种轻便格式,很有用,也易于学习。

**注:**JSON(JavaScript Object Notation)格式最初是为JavaScript开发的,但随后成了一种常见格式,被包括Python在内的众多语言采用。

  • 使用json.dump()和json.load()

函数json.dump()接受两个实参:要存储的数据以及可用于存储数据的文件对象。

number_write.py

#使用json.dump()来存储数字列表
import json

numbers=[2,3,5,7,11,13]

filename ='numbers.json'
with open(filename,'w') as file_object:
    json.dump(numbers,file_object)

通常使用文件扩展名.json来指出文件存储的数据为JSON格式。然后以写入模式打开这个文件,让json能够将数据写入其中,使用函数json.dump()将数字列表存储到文件numbers.json中。

#使用json.load()将列表读取到内存
import json

filename ='第10章/numbers.json'
with open(filename) as file_object:
    numbers=json.load(file_object)

print(numbers)

  • 保存和读取用户生成的数据

对于用户生成的数据,使用json保存它们大有裨益,因为如果不以某种方式进行存储,等程序停止运行时用户的信息将丢失。

remember_me.py

import json

username =input("What is your name? ")

filename='第10章/username.json'
with open(filename,'w') as file_object:
    json.dump(username,file_object)
    print("We'll remember you when you come back, "+username+"!")

greet_user.py

import json

filename='第10章/username.json'

with open(filename) as file_object:
    username=json.load(file_object)
    print("Welcome back, "+username+"!")

将上述两个程序合并到一起
remember_me.py

import json

#如果以前存储了用户名,就加载它
#否则,就提示用户输入用户名并存储它
filename='第10章/username.json'
try:
    with open(filename) as file_object:
        username=json.load(file_object)
except FileNotFoundError:
    username=input("What is your name? ")
    with open(filename,'w') as file_object:
        json.dump(username,file_object)
        print("We'll remember you when you come back, "+username+"!")
else:
    print("Welcome back, "+username+"!")

**标注:**这个程序如果在.json文件没有内容的情况下会报错。


  • 重构

代码能够正确地运行,但可做进一步的改进–将代码划分为一系列完成具体工作的函数。这样的过程被称为重构。重构让代码更清晰、更易于理解、更容易扩展。

remember_me.py

将代码放到函数中,大部分逻辑放到一个或多个函数中。

import json

def greet_user():
    """问候用户,并指出其名字"""
    filename='第10章/username.json'
    try:
        with open(filename) as file_object:
            username=json.load(file_object)
    except FileNotFoundError:
        username=input("What is your name? ")
        with open(filename, 'w') as file_object:
            json.dump(username, file_object)
            print("We'll remember you when you come back, "+username+"!")
    else:
        print("Welcome, "+username+"!")

greet_user()

重构函数:

import json

def get_stored_username():
    """如果存储了用户名,就获取它"""
    filename='第10章/username.json'
    try:
        with open(filename) as file_object:
            username=json.load(file_object)
    except FileNotFoundError:
        return None
    else:
        return username

def get_new_username():
    """提示用户输入用户名"""
    username=input("What is your name? ")
    filename='第10章/username.json'
    with open(filename,'W') as file_object:
        json.dump(username,file_object)
    return username

def greet_user():
    """问候用户,并指出其名字"""
    username=get_stored_username()
    if username:
        print("Welcome back, "+username+"!")
    else:
        username=get_new_username()
        print("We'll remember you when you come back, "+username+"!")

greet_user()

在最终版中,每个函数都执行单一而清晰的任务。调用greet_user():要么欢迎老用户回来,要么问候新用户。调用get_stored_username():只负责获取存储的用户名(如果存储了的话);再在必要时调用get_new_username():只负责获取并存储新用户的用户名。要编写出清晰而易于维护和扩展的代码,这种划分工作必不可少。

练习
10-11 喜欢的数字:编写一个程序,提示用户输入他喜欢的数字,并使用json.dump()将这个数字存储到文件中。再编写一个程序,从文件中读取这个值,并打印消息“I know your favorite number! It’s _____.”。

import json

number= input("Please enter your favorite number: ")
filename='第10章/favorite_number.json'
with open(filename,'w') as file_object:
    json.dump(number,file_object)
    print("I know your favorite number!It's "+number+" !")

10-12 记住喜欢的数字:将练习 10-11 中的两个程序合而为一。如果存储了用户喜欢的数字,就向用户显示它,否则提示用户输入他喜欢的数字并将其存储到文件中。运行这个程序两次,看看它是否像预期的那样工作。

import json

filename='第10章/favorite_number.json'
try:
    with open(filename) as file_object:
        number=json.load(file_object)
except FileNotFoundError:
    number= input("Please enter your favorite number: ")
    filename='第10章/favorite_number.json'
    with open(filename,'w') as file_object:
        json.dump(number,file_object)
        print("I know your favorite number!It's "+number+" !")
else:
    print("Welcome back, user who like number "+number+"!")

10-13 验证用户:最后一个 remember_me.py 版本假设用户要么已输入其用户名,要么是首次运行该程序。我们应修改这个程序,以应对这样的情形:当前和最后一次运行该程序的用户并非同一个人。为此,在 greet_user()中打印欢迎用户回来的消息前,先询问他用户名是否是对的。如果不对,就调用 get_new_username()让用户输入正确的用户名。

import json

def get_stored_username():
    """如果存储了用户名,就获取它"""
    filename='第10章/username.json'
    try:
        with open(filename) as file_object:
            username=json.load(file_object)
    except FileNotFoundError:
        return None
    else:
        return username

def get_new_username(newname):
    """创建新用户"""
    filename='第10章/username.json'
    with open(filename,'w') as file_object:
        json.dump(newname,file_object)
    print(newname.title()+" was successfully created as a new username!")
    return newname

def greet_user():
    """判断用户名是否存在"""
    login_name=input("Please enter your username: ")
    username=get_stored_username()
    if username==login_name:
        print("Welcome back, "+username.title()+"!")
    else:
        print(login_name+" doesn't exist!")
        username=get_new_username(login_name)
        print("We'll remember you when you come back, "+username.title()+"!")

greet_user()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值