十四、Python第十四课——文件和异常

十四、Python第十四课——文件和异常,第1张

概述(请先看这篇文章:https://blog.csdn.net/GenuineMonster/article/details/104495419)    如果看完这篇博文,你的问题还是没有解决,那么请关注我的公众号,后台发消息给我吧,当天回复!  学习文件处理,你将能够快速的分析大量的数据;学习错误处理,你将会编写在意外情形下也不崩溃 (请先看这篇文章:https://blog.csdn.net/GenuineMonster/article/details/104495419)       如果看完这篇博文,你的问题还是没有解决,那么请关注我的公众号,后台发消息给我吧,当天回复!

 

 

学习文件处理,你将能够快速的分析大量的数据;学习错误处理,你将会编写在意外情形下也不崩溃的程序;学习异常,你将会管理程序运行时出现的错误;你还将学习Json,从而能够保存用户数据,以免在程序停止运行后丢失。异常一般来源于破坏程序的恶意企图以及错误数据。举个例子:假设QQ的密码不能包含字母,并且后台程序在判别时也忽略了对字母的识别(没有相关代码)。那么此时,如果你在设置密码时,输了字母,后台程序接受到了,但不知道如何处理这个“不按常理出牌”的密码,此时程序就又可能瘫痪,进而出现错误。“错误及异常”和其他面向对象语言中的一样,就是Java中俗称的“异常抛出机制”。学习这些机制的主要目的是提高程序的适用性、可用性和稳定性。

一、从文件中读取数据

文本文件中可存储的数据量多得难以置信,每当需要分析或修改存储在文件中的信息时,读取文件是很有必要的。要使用文本文件中的信息,首先需要将信息读取到内存中。为此,可以一次性读取文件的全部内容,也可以以每一次的方式逐步读取。

1.1 读取整个文件

我们先来创建一个包含几行文本的文件,名称为pi_digits.txt。内容如下截图:

接下来我们编写代码,读取这个txt文件中的内容,并输出:

with open('pi_digits.txt') as file_object:    contents =file_object.read()    print(contents)

      要知道,无论我们以任何方式使用文件——哪怕仅仅是打印其内容,都得先打开文件,这样才能访问它。

A、函数open()接受一个参数:要打开的文件的名称。Python在当前执行的程序文件所在的目录中查找指定的文件,找到后,执行open()函数,然后返回一个表示文件的对象,并存储在file_object中。

B、关键字with的作用是在不再需要访问文件后将其关闭。在这个过程中,我们只调用了open(),没调用close()。你也可以调用open()和close()来打开和关闭文件,但这样做时,如果程序存在BUG,导致close()语句未执行,文件将不会关闭。看似微不足道,但未妥善地关闭文件可能会导致数据丢失或受损。如果在程序中过早的调用close(),你会发现需要使用文件时它已关闭(无法访问),这会导致更多地错误。(并非在任何情况下都能轻松确定关闭文件的恰当时机)但我们可以通过使用with,让Python去确定:我们只管打开文件,并在需要时使用它,Python自会在合适地时候自动将其关闭。

C、有了表示pi_digits.txt的文件对象后,我们使用read()方法读取这个文件的全部内容,并将其作为一个长长的字符串存储在变量contents中。这样我们就可以打印全部的内容了。

细心的人已经发现问题了,为什么输出后有这么多空行?这是因为read()到达文件末尾时返回一个空字符串,而将这个空字符串显示出来时就是一个空行。要删除末尾的空行,可在print语句中使用rstrip():(我实验了,貌似没什么用啊)

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

1.2 文件路径

如果要Python打开不与程序文件位于同一目录中的文件,需要提供文件路径,它让Python到系统的特定位置去查找。文件路径分为绝对路径和相对路径。

A、相对路径:文件所处的位置是相对于当前运行的程序文件所属目录而言的路径,举个例子:(注意使用的是反斜杠)

C:\Users\Jeffery_PC\Desktop\code          # py文件的目录C:\Users\Jeffery_PC\Desktop\code3      # 目标文件目录目标文件目录和py文件目录的前面一部分都是一样的,所以省略不写(相当于以py文件的目录为参考)所以目标文件的相对路径最终表示为:123\pi_digits.txt我们可以实验一下,看看能否读取文件成功:代码如下:with open('123\pi_digits.txt') as file_object:    contents =file_object.read()    print(contents.rstrip())

B、绝对路径:目标文件在计算机中的准确位置就是绝对路径,举个例子:

C:\Users\xxxxxx\Desktop\code3\pi_digits.txt   # xxxxxx是我的信息,这里隐写了

 在相对路径行不通时,可使用绝对路径。绝对路径通常比相对路径更长,因此将其存储在一个变量中,再将这个变量传递给open()会有所帮助。就目前而言,最简单的做法是:要么将数据文件存储在程序文件所在的目录,要么将其存储在程序文件所在目录下的一个文件夹中。

1.3 逐行读取

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

filename = 'pi_digits.txt'with open(filename) as file_object:    for line in file_object:        print(line)

这段代码与之前的代码类似,就不解释了。出现空白行的原因是:读取文档每行的末尾都有一个看不见的换行符,而print语句也会加上一个换行符,因此每行末尾都有两个换行符:一个来自文件,一个来自print语句。要消除这些多余的空白行,可在print语句末尾使用rstrip():

filename = 'pi_digits.txt'with open(filename) as file_object:    for line in file_object:        print(line.rstrip())

1.4 创建一个包含文件各行内容的列表

使用关键字with时,open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件的内容,可在with代码块内将文件的各行存储在一个列表里,并在with代码块外使用该列表:你可以立即处理文件的各个部分,也可以推迟到程序后面再处理。

例代码:

filename = 'pi_digits.txt'with open(filename) as file_object:    lines = file_object.readlines()  # 按行读取,并存储在列表中for line in lines:    print(line.rstrip())   # 使用for循环来打印每一行的内容

1.5 使用从文件中读取的内容

我们将会读取文件内容,并组成一个新的字符串(组成新的字符串就是我们的“使用”)(将文件读取到内存中后,就可以以任何方式使用这些数据了)

filename = 'pi_digits.txt'with open(filename) as file_object:    lines = file_object.readlines()  # 按行读取,并存储在列表中pi_string = ''for line in lines:    pi_string += line.rstrip()print(pi_string)print(len(pi_string))

看出这个字符串中间还有空格,所以可以使用strip()函数不是rstrip():

filename = '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)   # 精确到30位小数的圆周率值print(len(pi_string))  # 输出字符串的长度

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

1.6 处理大体量文件

即将要处理的文件内部包含圆周率小数点后一百万位,我们将其打印出来。

filename = '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] + "...") # 打印到小数点后50位,因为整数和小数点也需要2位print(len(pi_string))

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

1.7 牛刀小试

如果大家关注过北大数院微信公众号,那么对其中一个功能“在圆周率中寻找你的生日”就很熟悉了,这里我们自己也实现一下,检测某个人的生日是否在圆周率的100万当中。逻辑是:将自己的生日字符串化,再检查这个字符串是否包含在pi_string中。

filename = '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:  ") # input()函数需要用户输入信息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!")

作者的生日的确出现在Π的前100万位中。

二 将数据写入文件

保存数据的最简单的方式之一是将其写入到文件中。通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出也依然存在。 

2.1 写入空文件

要将文本写入文件,你在调用open()时需要提供另一个实参,告诉Python你要写入打开的文件。接下来以一段具体代码演示一下:

filename = 'programming.txt'# 如果你写入的文件不存在,open()将自动创建with open(filename, 'w') as file_object:       # 为open()函数提供两个实参:第一个指明要打开的文件名称    # 第二个实参指明:我们将以“写入模式”打开这个文件    # ‘r’:读取模式    # ‘w’:写入模式    # ‘a’:附加模式    # ‘r+’:读取写入模式    # 如果省略了模式实参,Python将以默认的只读模式打开文件       file_object.write("I love programming!")

 

成功写入!这里需要注意的是:

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

B、Python只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数str()将其转换为字符串格式。 

2.2 写入多行

这里只需要注意一点,函数write()不会在你写入的文本末尾自动添加换行符,需要你自己添加。如果还需要其他格式,可以使用“空格”、“制表符”、“空行”来设置文本格式:

filename = 'programming.txt'with open(filename, 'w') as file_object:    # 为open()函数提供两个实参:第一个指明要打开的文件名称    # 第二个实参指明:我们将以“写入模式”打开这个文件    # ‘r’:读取模式    # ‘w’:写入模式    # ‘a’:附加模式    # ‘r+’:读取写入模式    # 如果省略了模式实参,Python将以默认的只读模式打开文件    file_object.write("I love programming!\n")    file_object.write("I love creating new games.\n")

2.3 附加到文件(追加到文件)

如果你要给文件添加内容,而不是覆盖原有的内容,可以以附加模式打开文件。如果你以附加模式打开文件时,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")

 

三 异常

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

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

3.1 处理ZerodivisionError异常

举个简单的例子:在数学中,0不可以作除数。那如果在Python中出现这样的问题呢?

print(5/0)

同样,程序因为出错了被迫停止,Python指出了出错的地方及错误内容。接下来,我们使用try-except代码块来避免这种情况发生:告诉Python,如果出现了这样的情况该怎么办。

3.2 使用try-except代码块处理异常

在编写代码时,我们可能会预知某些异常的发生,此时可以写一个try-except代码块来处理可能引发的异常。就像英文的意思一样,让Python试一下,如果行就继续;不行的话,那就执行别的代码(except),反正程序别停止了。我们对上述代码进行改写:

try:    # 我们将导致错误的代码行print(5/0)放在了一个try代码块中。    # 如果try代码块运行起来没问题,那就自动跳过except代码块    # 如果try代码块中的代码导致了错误,Python将查找这样的except代码块,    # 并运行其中的代码,即其中指定的错误与引发的错误相同    print(5/0)except ZerodivisionError:    print("You can't divIDe by zero! ")

3.3 使用异常避免崩溃

发生错误时,如果程序还有工作没有完成,妥善地处理错误就尤其重要。这种情况经常会出现在要求用户提供输人的程序中;如果程序能够妥善地处理无效输入,就能再提示用户提供有效输入,而不至于崩溃。下面来创建一个只执行除法运算的简单计算器:

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)

当以上这段代码输入除数为0的算式时,问题就出现了:

 这种计算器除法程序可不是我们需要的,如果被怀有恶意的用户遇到,他们会得知很多信息,从而做一些破坏代码的事情。所以我们得使用try-except代码块,不过这里还得加一个“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代码块里只能包含可能导致错误的代码。    try :        answer = int(first_number) / int(second_number)    except ZerodivisionError:    # 出现错误后的解决代码        print("You can't divIDe by 0. ")    # 依赖于try代码块成功执行的代码都放在else代码块中    else:        print(answer)

try-except-else代码块工作原理小结:Python尝试执行try代码块中的代码;只有可能引发异常的代码才需要放在try语句中。有时候,有一些仅在try代码块成功执行时才需要运行的代码,这些代码应放在else代码块中。except代码块告诉Python,如果它尝试运行try代码块中的代码时引发了指定的异常,该怎么办。通过预测可能发生错误的代码,可编写健壮的程序,它们即便面临无效数据或缺少资源,也能继续运行,从而能够抵御无意的用户错误和恶意的攻击。

3.4 处理fileNotFundError异常

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

filename = 'alice.txt'with open(filename) as f_obj:    contents = f_obj.read()

这个错误是函数open()导致的,因此要处理这个错误,必须将try语句放在包含open()的代码之前:

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

 

       

四 处理文本4.1 分析文本

             我们将会分析整本书的文本文件,下面我们来提取童话“Alice in Wonderland”的文本,并尝试计算它包含多少个单词。我们将使用方法split(),它根据一个字符串创建一个单词列表。下面是对只包含童话名“”Alice in Wonderland“的字符串调用方法split(
)的结果:

Title = 'Alice in Wonderland'print(Title.split())

    方法split()以空格为分隔符将字符串拆成多个部分,并将这些部分都存储到一个列表中。结果是一个包含字符串中所有单词的列表,虽然有些单词可能包含标点。接下来我们使用这个方法,对整篇小说进行处理分析,统计这篇童话大致包含多少个单词:

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

4.2 使用多个文件

下面多分析几本书:

def count_words(filename):    """计算一个文件大致包含多少个单词"""    try:        with open(filename) as f_obj:            contents = f_obj.read()    except fileNotFoundError:        msg = "Sorry, the file " + filename + " does not exist. "        print(msg)    else:         # 计算文件大致包含多少个单词         words = contents.split()         num_words = len(words)         print("The file " + filename + "has about " + str(num_words) + " words. ")# sIDdhartha.txt文件不存在,以此来展示程序的出色。filenames = ['alice.txt','sIDdhartha.txt','moby_dict.txt','little_women.txt']for filename in filenames:    count_words(filename)

在这个示例中,使用try-except代码块提供了两个重要的优点:A、避免让用户看到traceback;B、让程序继续分析找到其他文件。

4.3 失败时一声不吭

上面这个例子,在我们找不到对应的文件夹时,Python会提示用户有一个文件没有找到。但并非每次捕获到异常时都需要告诉用户,有时候你希望程序发生异常时一声不吭:

我们可以将“except fileNotFoundError:”中的代码全部删除,改为pass。

def count_words(filename):    """计算一个文件大致包含多少个单词"""    try:        with open(filename) as f_obj:            contents = f_obj.read()    except fileNotFoundError:         # msg = "Sorry, the file " + filename + " does not exist. "         # print(msg)         pass    else:         # 计算文件大致包含多少个单词         words = contents.split()         num_words = len(words)         print("The file " + filename + "has about " + str(num_words) + " words. ")# sIDdhartha.txt文件不存在,以此来展示程序的出色。filenames = ['alice.txt','sIDdhartha.txt','moby_dict.txt','little_women.txt']for filename in filenames:    count_words(filename)

又多了一个关键字“pass”,pass语句还充当了占位符,他提醒你在程序的某个地方什么都没做,并且以后也许要在这里做些什么。

4.4 决定报告哪些错误

       在什么情况下该向用户报告错误?在什么情况下又应该在失败时一声不吭呢? 如果用户知道要分析哪些文件,他们可能希望在有文件没有分析时出现一条消息,将其中的原因告诉他们。如果用户只想看到结果,而并不知道要分析哪些文件,可能就无需在有些文件不存在时告知他们。

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

五 存储数据

       很多程序都要求用户输入某种信息,程序都把用户提供的信息存储在列表和字典等数据结构中。用户关闭程序时,你几乎总是要保存他们提供的信息。一种简单的方式是使用模块Json来存储数据。

       模块Json让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件中的数据。你还可以使用Json在Python程序之间分享数据。更重要的是,JsON数据格式并非Python专用的,这让你能够将以JsON格式存储的数据与使用其他编程语言的人分享。这是一种轻便格式,很有用,也易于学习。(JsON(JavaScript Object Notation)格式最初是为JavaScript开发的,但随后成了一种常见格式,被包括Python在内的众多语言采用)

5.1 使用Json.dump()和Json.load()

我们编写一个存储一组数字的简短程序,再编写一个将这些数字读取到内存中的程序。

import Jsonnumbers = [2,3,5,7,11,13]filename = 'number.Json'# 以写的形式打开文件,使用的是Json.dump()with open(filename,'w') as f_obj:    # 把数字写入文件    Json.dump(numbers,f_obj)# 读取数据,使用的是Json.load()with open(filename) as f_obj:    # 为了区别numbers,后面加了下划线和2    numbers_2 = Json.load(f_obj)# 输出print(numbers_2)

用记事本打开numbers.Json文件,显示如下:

代码先导入了Json模块,随后创建了一个数字列表,存储了一些数字。先是将数字存储到了Json文件里,后又从Json文件中读取出来。其中使用了Json.dump()和Json.load()方法。

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

对于用户生成的数据,使用Json保存将大有好处。因为如果不以某种方式进行存储,等程序停止运行时,用户的信息将会丢失。举个例子来说明:

将用户的姓名存储在Json文件里:

# 导入Json包import Json# 需要用户输入一个名字username = input("What is your name? ")# 将Json文件名存储在filename变量中filename = 'username.Json'# 以写的方式打开文件,没有就创建一个Json文件,并重新命名为f_objwith open(filename,'w') as f_obj:    # 使用Json.dump()方法写入输入的名字    Json.dump(username,f_obj)    # 输出提示,表明已将用户输入的名字存储了起来    print("We'll remember you when you come back, " + username +"! ")

将Json文件的内容再读取出来,比如对这个人进行问候:

# 导入Json包import Json# 将文件名存入filename变量中filename = 'username.Json'# 读取Json文件with open(filename,'r') as f_obj:    username = Json.load(f_obj)    # 打印问候语    print("Welcome back, " + username + "! ")

@H_463_419@

下面我们将这两个程序合并:

# 导入Json包import Json# 如果以前存储了用户名就加载# 否则,就提示用户输入用户名并存储filename = 'username.Json'try:    with open(filename) as f_obj:        username = Json.load(f_obj)except fileNotFoundError:    username = input("What is your name? ")    with open(filename,'w') as f_obj:        Json.dump(username,f_obj)        print("We will remember you when you back, " + username + "! ")else:    print("Welcome come back, " + username + "! ")

如果这个文件存在,就将其中的用户名读取到内存中,再执行else代码块,即打印一条欢迎用户回来的消息。用户首次运行这个程序时,文件username.,Json不存在,将引发fileNotFoundError异常,因此Python将执行except代码块:提示用户输人其用户名,再使用Json.dump()存储该用户名,并打印一句问候语。

5.3 重构

将代码划分为一系列完成具体工作的函数的过程被称为重构。重构让代码更清晰、更易于理解、更容易扩展。接下来,我们对上述问候用户的程序进行重构:

第一次重构:

# 导入Json包import Jsondef greet_user():    # 考虑到现在使用了一个函数,我们删除注释,取而代之的是一个文档字符串来指出程序是做什么的。    """问候用户,并指出其名字"""    filename = "username.Json"    try:        with open(filename) as f_obj:            username = Json.load(f_obj)    except fileNotFoundError:        username = input("What is your name? ")        with open(filename, 'w') as f_obj:            Json.dump(username, f_obj)            print("We will remember you when you back, " + username + "! ")    else:        print("Welcome come back, " + username + "! ")greet_user()

但上述的函数所做的不仅仅是问候用户,还在存储了用户时获取它,而在没有存储用户名时提示用户输入一个。所以我们可以对greet_user()再次重构:

# 导入Json包import Jsondef get_stored_username():    """如果存储了用户名,就获取它"""    filename = 'username.Json'    try:        with open(filename) as f_obj:             username = Json.load(f_obj)    except fileNotFoundError:        return None    else:        return usernamedef greet_user():    """问候用户,并指出其名字"""    username = get_stored_username()    if username:        print("Welcome come back, " + username + "! ")    else:        username = input("What is your name? ")        filename = "username.Json"        with open(filename,'w') as f_obj:            Json.dump(username,f_obj)            print("We will remember you when you back, " + username + "! ")        greet_user()

这是该文件不存在时的输入输出:

这是该文件存在时的输出:

上述代码的功能更细化了,我们还可以进一步重构:将greet_user()中的提示用户输入的代码放在一个独立的函数中。

# 导入Json包import Jsondef get_stored_username():    """如果存储了用户名,就获取它"""    filename = 'username.Json'    try:        with open(filename) as f_obj:             username = Json.load(f_obj)    except fileNotFoundError:        return None    else:        return usernamedef get_new_username():    """提示用户输入用户名"""    username = input("What is your name? ")    filename = "username.Json"    with open(filename, 'w') as f_obj:        Json.dump(username, f_obj)    return usernamedef greet_user():    """问候用户,并指出其名字"""    username = get_stored_username()    if username:        print("Welcome come back, " + username + "! ")    else:        username = get_new_username()        print("We will remember you when you back, " + username + "! ")        greet_user()

自行体会问候用户程序的三个版本的差异,可以看出代码的功能逐渐具体、清晰,易读性增强。因此,重构是编写代码中必不可少的一环。

总结

以上是内存溢出为你收集整理的十四、Python第十四课——文件和异常全部内容,希望文章能够帮你解决十四、Python第十四课——文件和异常所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1185452.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-03
下一篇 2022-06-03

发表评论

登录后才能评论

评论列表(0条)

保存