一切皆文件

Linux的设计哲学之一:一切皆文件。

因此,设备也是文件,对设备的操作可以转换成对文件的I/O操作。

冯诺依曼体系架构

  • CPU由运算器和控制器组成,计算机的五大部件如下:

    • 运算器:完成各种算数运算、逻辑运算、数据传输等数据加工处理
    • 控制器:控制程序的执行
    • 存储器:用于记忆程序和数据,如内存
    • 输入设备:将数据或程序输入到计算机中,如键盘、鼠标
    • 输出设备:将数据或程序的处理结果展示给用户,如显示器、打印机

一般地,IO操作直的是文件IO。如果指的是网络IO,会直接说成网络IO。

文件操作

文件的概念

在磁盘中存储为一个个字节,形成字节流bytes。其存储位置很可能是随机存放,需要通过磁头的转动来访问数据,此时为随机访问。

文件常用操作

名称 含义
open 打开文件
read 读取全部内容
write 写入内容
close 关闭文件
readline 单行读取
readlines 多行读取
seek 文件指针操作
tell 指针位置

open

open(file,mode='r',buffering=-1,encoding=None,errors=None,newline=None,closefd=True,opener=None)

打开一个文件,返回一个文件对象(流对象)和文件描述符。若打开文件失败,则返回异常。

基本使用:创建一个文件,然后打开它,用完关闭。

In [1]: f = open('test')  #建立文件对象,文件必须事先存在
In [2]: type(f)
Out[2]: _io.TextIOWrapperIn [3]: f.read()        # 读操作,读取全部内容
Out[3]: 'This is test file for PyIO\n'In [4]: f.close()      # 关闭

注意:

  1. 文件内容的编码需要和Python默认字符编码兼容才能正常打印,否则会抛出字符编码非法异常
  2. 如果f.open()文件后,其他编辑器对该文件修改,此时f.read()只能看到修改前的内容。重新打开才能看到新 内容。

文件操作中,最常用的操作就是读和写。

文件访问的模式有两种:文本模式和二进制模式。不同模式下,操作函数不尽相同,表现的结果也不一样。

open的参数

file

打开或者要创建的文件名。如果不指定路径,默认是当前路径。

mode

打开模式:

描述字符 含义
r 缺省,表示只读打开
w 只写打开
x 创建,并写入一个新文件
a 写入打开。如果文件存在,则追加
b 二进制模式
t 缺省,文本模式
+ 读写打开一个文件。给原来只读、只写方式打开提供缺失的读或写能力

上例中,可知默认是文本模式打开,且是只读。

  • r

    表示只读打开,如果使用write方法,会抛异常

    如果文件不存在,则抛出FileNotFoundError异常

  • w

    表示只写打开,如果使用read方法,会抛异常

    如果文件不存在,则直接创建文件

    如果文件存在,则清空文件内容

  • x

    文件不存在,则创建文件,并以只写方式打开

    文件存在,则抛出FileExistsError异常

  • a

    文件存在,则只写打开,追加内容

    文件不存在,则创建后,只写打开,追加内容

    提示:

    r是只读,wxa都是只写。

    wxa都可产生新文件,w不管文件存在与否,都会生成全新内容的文件;a不管文件是否存在,都能在打开的文件尾部追加;x必须要求文件事先不存在,自己造一个新文件

  • 文本模式t

    字符流,将文件的字节按照某种字符编码理解,按照字符操作。open的默认mode为rt

  • 二进制模式b

    字节流,将文件就按照字节理解,与字符编码无关。二进制模式操作时,字节操作使用bytes类型。

    提示:字符转字节,需要encode()编码;反之,则decode()解码

二进制模式的读取和写入

In [1]: f = open('test','rb')    # 二进制模式读取
In [2]: f.read()
Out[2]: b'This is test file for PyIO\n' # 读取的类型字节类型bytesIn [3]: f1 = open('test2','a')  # test2文本模式追加                                 In [4]: f1.write('啊')    # 文本
Out[4]: 1In [5]: f.close()
In [6]: f1.close()                                                           In [7]: !cat test2
啊
In [8]: f = open('test2','rb')   # test2二进制模式读取
In [9]: f.read()
Out[9]: b'\xe5\x95\x8a' # 字节类型,对应ASCII
In [1]: f = open('test3','wb')             # 二进制模式写入
In [2]: f.write(b'啊')         # 必须以ASCII码的方式写入                                                      File "<ipython-input-2-5f74f1f1adca>", line 1f.write(b'啊')^
SyntaxError: bytes can only contain ASCII literal characters.In [3]: f.write('啊'.encode())     # 文本模式encode为二进制字节流类型
Out[3]: 3In [4]: f.close()
In [5]: !cat test3
啊

功能增强+

为r、w、a、x提供缺失的读写能力。但是,获取文件对象依旧按照r、w、a、x特征,+不能单独使用,可认为它是为前面的模式字符增强功能的。

读写访问r+

In [15]: f = open('test','r+')     # 添加缺失的写能力,要求文件事先存在                                           In [16]: f.write('abcdefg')      # 在开头写入
Out[16]: 7In [17]: f.read()
Out[17]: ' test file for PyIO\n' # 文件指针指向写入内容后,因此只能看到后面的内容In [18]: f.close()                                                                   In [19]: !cat test
abcdefg test file for PyIO

可指定读取字符个数或字节个数:

In [35]: f = open('test','r+')                                                       In [36]: f.read(1)
Out[36]: '啊'In [38]: f = open('test2','rb')                                                      In [39]: f.read(1)
Out[39]: b'\xe5'

读写访问w+

In [20]: f = open('test','w+')                                                       In [21]: f.read()
Out[21]: ''In [22]: f.write('啊 Python')
Out[22]: 8In [23]: f.read()
Out[23]: ''In [24]: f.close()                                                                   In [25]: !cat test
啊 Python

二进制的r+,w+

In [81]: f = open('test3','wb+')
In [82]: f.write('啊Python'.encode())
Out[82]: 9In [83]: f.close()                                                                                                       In [84]: !cat test3
啊PythonIn [85]: f = open('test3','rb+')                                                                                         In [86]: f.read()
Out[86]: b'\xe5\x95\x8aPython'In [87]: f.write('啊python'.encode()) 追加内容
Out[87]: 9In [88]: f.close()
In [89]: !cat test3
啊Python啊python

注意:

如果读写能力都具备,使用r打开。如果打开后直接写,则从头开始写。如果打开后先读取,然后再写,则从尾部写。实际上是文件指针的原因。

追加 a+

直接从尾部开始追加内容,或者直接从尾部读取内容,因此是无法读取到任何内容的。

也可使用x+

文件指针

文件指针,指向当前字节位置

mode=r,指针起始在0

mode=a,指针起始在EOF

tell()显示指针当前位置

seek(offset[,whence])

移动文件指针位置。offset偏移多少字节,whence从哪里开始

whence 0缺省值,表示从头开始,offset只能正整数

whence 1 表示从当前位置,offset只接受0,比如f.seek(0,1) 表示指针在文件开头或当前位置

whence 2 表示从EOF开始,offset只接受0,比如f.seek(0,2)表示指针在文件结尾

In [2]: f = open('test','r')                                                         In [3]: f.tell()
Out[3]: 0In [4]: f.read(1)
Out[4]: '啊'In [5]: f.tell()
Out[5]: 3In [6]: f.read(1)
Out[6]: ' 'In [7]: f.tell()
Out[7]: 4

指针一边写,一边移动。因此若f.write(‘xyz’)后,f.read()无法读取到内容,因为指针已经移动到结尾。此时可以seek重新指定指针的位置。

In [8]: f.seek(0)
Out[8]: 0
In [10]: f.read()
Out[10]: '啊 Python'

注意下面的特性:如果在数据中间覆盖,会自动追加到末尾。

In [12]: f = open('test','r+')
In [13]: f.read()
Out[13]: '啊 Python'
In [14]: f.seek(3)
Out[14]: 3
In [15]: f.read(3)    # 指针移动到Py处
Out[15]: ' Py'
In [16]: f.write('THON')     # 在Py处写入
Out[16]: 4
In [17]: f.seek(0)
Out[17]: 0
In [18]: f.read()
Out[18]: '啊 PythonTHON'  # 追加到末尾

文本模式

文本模式支持从开头向后偏移的方式。

whence为1表示从当前位置开始偏移,但只支持偏移0,相当于原地不动。

whence为2表示从EOF开始,只支持偏移0,相当于移动文件指针到EOF。

seek是按照字节偏移的。

二进制模式

whence 0 缺省值,表示从头开始,offset只能是正整数

whence 1表示从当前位置,offset可正可负

whence 2表示从EOF开始,offset可正可负

二进制模式支持任意起点的偏移,从头、从尾、从中间位置开始均可。

向后seek可超界,但是向前seek时不可超界,否则抛出异常。

缓冲区

-1表示使用缺省大小的buffer。如果是二进制模式,使用io.DEFAULT_BUFFER_SIZE值,默认是4096或者8192。如果是文本模式,如果是终端设备,则是行缓存方式,否则,使用二进制模式策略。

  • 0只在二进制模式使用,关闭关闭buffer
  • 1只在文本模式使用,表示使用行缓冲。意思为遇到换行符就flush。
  • 大于1用于指定buffer的大小。

buffer缓冲区

缓冲区是一个内存空间,一般而言是一个FIFO队列,到缓冲区满了或者达到阈值时,数据才会flush到磁盘中。

flush()将缓冲区数据写入磁盘

close()关闭前会调用flush()

io.DEFAULT_BUFFER_SIZE缺省缓冲区大小,单位是字节

In [1]: import io
In [2]: print(io.DEFAULT_BUFFER_SIZE)
8192

文本模式:默认开启buffer,默认为-1.

如果是1,则表示使用行缓冲

读写打开:w+

In [3]: f = open('test','w+')                                                        In [4]: !cat test                                                                    In [5]: f.write('!'*10)
Out[5]: 10In [6]: f.read()
Out[6]: ''In [7]: !cat test
!!!!!!!!!!

即使没有flush,因为write执行完毕了,相当于程序执行完毕了,系统会自动触发缓冲清理,就会触发flush。

如果while True,只要程序没执行完毕,则必须写满缓冲区才会flush。

只写打开:w

In [3]: f = open('test','w')                                                         In [4]: f.write('!' * 1024)
Out[4]: 1024In [5]: !cat test

只写方式打开,不会马上刷写到磁盘,无论遇到换行符,直到缓冲区溢出为止。

二进制模式:默认为-1,启动buffer

如果是二进制模式,默认buffer模式为-1,启动buffer

In [14]: f = open('test','wb+')                                                      In [15]: !cat test                                                                   In [16]: f.write(b'!'*1024)
Out[16]: 1024In [17]: !cat test

内容写到了buffer中。

如果关闭buffer,置为0,一旦写入则马上落到磁盘中。

In [19]: f = open('test','wb+',0)
In [20]: !cat test
In [21]: f.write(b'abc')
Out[21]: 3In [22]: !cat test
abc

一般,都需要打开缓冲区。

指定buffer大小:仅限于二进制模式下

在文本模式下,指定的buffer size无效。

指定buffer大小,比如指定为4字节:

In [24]: f = open('test','wb+',4)                                                    In [25]: !cat test                                                                   In [26]: f.write(b'abc')
Out[26]: 3In [27]: !cat test                                                                   In [28]: f.write(b'de')
Out[28]: 2In [29]: !cat test
abc

缓冲区超过的部分,会把此部分置回到磁盘中。

注意:文本模式无法关闭缓冲区。

Buffering 总结

buffering=0

这是一种特殊的二进制模式,不需要内存的buffer,可看做是一个FIFO的文件。

In [85]: f = open('test4','wb+',0)                                                          In [86]: f.write(b"m")
Out[86]: 1In [87]: !cat test4
m
In [88]: f.write(b"a")
Out[88]: 1In [89]: !cat test4
ma
In [90]: f.write(b"g")
Out[90]: 1In [91]: !cat test4
mag

如果字节流关闭buffer,相当于先进先出的文件。此时一直在尾部追加数据,一般不使用f.seek()重新移动指针,否则FIFO失去意义。

如果buffer的模式为1,则用于文本模式下的行缓冲。缓冲区一旦满,则会刷写到磁盘中。不过,只要给定换行符,则会马上把内容刷写到磁盘中。

buffering 说明
buffering=-1 t和b,都是io.DEFAULT_BUFFER_SIZE
buffering=0 b关闭缓冲区;t不支持
buffering=1 b是1个字节;t是行缓冲,遇到换行符才flush
buffering>1 b模式表示行缓冲大小,缓冲区的值可超过io.DEFAULT_BUFFER_SIZE的值,直到溢出后把超过部分flush到磁盘中;t模式,是io.DEFAULT_BUFFER_SIZE。

总结:

  • 文本模式下,一般都用默认缓冲区大小
  • 二进制模式下,是一个个字节操作,因此可指定buffer的大小
  • 一般来说,默认缓冲区大小是个比较好的选择,除非明确知道自己在做什么,否则不要调整
  • 一般编程中,明确知道需要写磁盘,都会手动调用一次flush

encoding:编码,仅在文本模式使用

None表示使用缺省编码,依赖操作系统。windows下缺省编码为GBK(0xB0A1),Linux下缺省UTF-8(0xE5 95 8A)

在文本模式下,要求统一编码,比如全部使用UTF-8:英文为一个字节,为ASCII;而中文则是三个字节,某些字可能使用四个字节。

在跨平台的场景中,使用UTF-8比较合适和通用。但是在存储中文时,由于它多用了字节,因此会增大存储压力。而且在多副本的情况下,存储效率会降低,并且增加网络传输的字节数,这是它的坏处。

Unit code

Unit Code:用字节,表示世界上所有的文字。而且是全球统一编码,它使用两个字节,全部表示了全世界的文字,而且它把ASCII也使用了两个字节来表示。但是这样比较浪费存储空间。因此,它不太能推行得下去。

到了网络时代,出现了UTF(通过Unit Code转换而来)。而UTF-8,则是8位传递的。

编码的相互转换:unitCode -> GBK。此时需要编码转换表。

open的其他参数

errors

什么样的编码错误将被捕获

None和strict表示有编码错误将抛出ValueError异常;ignore表示忽略。

newline

文本模式下,换行的转换。可以为None、‘空串’、’\r’、’\n’、’\r\n’

读取时,None表示’\r’、’\n’、’\r\n’都被转换为’\n’;空串’'表示不会自动转换通用换行符;其他合法字符表示换行符就是指定字符,按照指定字符分行。

写入时,None表示’\n’都会被替换为系统缺省行分隔符os.linesep;’\n’或空串’‘表示’\n’不替换;其他合法字符表示’\n’会被替换为指定字符

In [7]: import os                                                                    In [8]: os.linesep
Out[8]: '\n'In [9]: f = open('test','w')                                                         In [10]: f.write('hello\npython!\r\n')
Out[10]: 15In [11]: f.close()                                                                   In [12]: !cat test
hello
python!In [13]: f = open('test','r')                                                        In [14]: f.read()
Out[14]: 'hello\npython!\n'In [15]: f.close()                                                                   In [16]: f = open('test','rb')                                                       In [17]: f.read()
Out[17]: b'hello\npython!\r\n'

closefd

关闭文件描述符,True表示关闭它。Flase会在文件关闭后,保持这个文件描述符。可用fileobj.fileno()查看。

文件描述符:文件打开后会占用一个文件对象,它和文件建立连接。文件对象保存了文件描述符。比如0,1,2分别代表标准输入,标准输出,错误输出的文件描述符。

如果文件关闭后,文件描述符会被回收。

读、写、关闭和其他方法

read 字符/字节读取

read(size=-1)

size表示读取的多少个字符或字节;负数或者None表示读取到EOF。

文本模式下为字符读取;二进制模式下为字节读取。

# 文本模式
In [12]: f = open('test','r+')
In [13]: f.read(3)
Out[13]: 'www'# 二进制模式:
In [15]: f = open('test','rb+')
In [16]: f.read(3)
Out[16]: b'www'

行读取的问题:f对象,read,readline(),readlines()

readline(size=-1)

一行行读取内容,size设置一次能读取行内几个字符或者字节。

readlines(hint=-1)

读取所有行的列表,指定hint则返回指定的函数。

In [18]: f = open('test','r+')
In [19]: f.readline()    # 按行读取                                                                             Out[19]: 'www.python.org\n'In [20]: f.readline()
Out[20]: 'www.jaywin.com\n'In [21]: f.seek(0)
Out[21]: 0In [22]: f.readlines()      # 读取所有行,返回列表                                            Out[22]: ['www.python.org\n', 'www.jaywin.com\n', '\n']In [23]: f.seek(0)
Out[23]: 0In [24]: f.readline(1)    # 读取指定字符数                                                   Out[24]: 'w'In [25]: f.readline(1)
Out[25]: 'w'
In [27]: for line in f.readlines(): ...:     print(line.strip()) ...:
www.python.org
www.jaywin.com

有些初学者往往将以下的情况混淆
假设一个文件的内容如下:

This is first line
This is second line
and so on...

仔细领悟下面四种读取文件内容方式的异同:

f = open('test.txt','r+')
for line in f:print(line)
->
This is first lineThis is second lineand so on...
f = open('test.txt','r+')
for line in f.read():print(line)
->
T
h
i
s 全部内容
...
f = open('test.txt','r+')
for line in f.readline():print(line)
->
T
h
i
s  一行
...
f = open('test.txt','r+')
for line in f.readlines():print(line)
->
This is first lineThis is second lineand so on...

可以看出,f对象和f.readlines()单行打印的内容是一致的。实际上,f对象是一个迭代器,支持next(),而f.readlines()则返回列表。

write

write(s),把字符串s写入到文件中,并返回字符的个数

writelines(lines),把字符串列表写入文件中。注意是字符串列表。

In [29]: f = open('test','w+')                                                                                  In [30]: lines = ['abc','123\n','jaywin.com']                                                                    In [31]: f.writelines(lines)                                                                                    In [32]: f.seek(0)                                                                                              Out[32]: 0In [33]: f.read()                                                                                                Out[33]: 'abc123\njaywin.com'

close

flush并关闭文件对象,此时会把文件描述符归还给操作系统。

若文件已关闭,再次关闭则无任何效果。

其他

seekable() 是否可seek

readable() 是否可读

writeable() 是否可写

closed() 是否已关闭

上下文管理

问题的引出:

f = open('test','w+')
f.write(str(1/0))
print('~~~')
f.close()

考虑上述例子,抛出异常后程序退出,并没有释放文件描述符。如果存在大量的异常退出,将会占据操作系统的文件描述符资源。

在Linux中,可通过ulimt -n命令查看文件最大打开数。

如果文件描述符不及时释放,会导致文件描述符占用过多。如果程序在正常关闭前抛出异常,则程序自动终止,来不及释放文件描述符。

处理方式:

  • 异常处理

    当出现异常时,拦截异常。但是,很多代码都可能出现OSError异常,不好判断异常是否因为资源限制产生。

    f = open('test')
    try:f.write("abc")
    finally:f.close()
    

    使用finally可保证打开的文件被关闭。

  • 上下文管理

    一种特殊的语法,交给解释器去释放文件对象。

    1. 使用with…as关键字
    2. 上下文管理的语句并不会开启新的作用域
    3. with语句执行完毕后,会自动关闭文件对象
    f = open('test','w+')
    with f:f.write('efg')f.write(str(1/0)) # error
    

    即使执行失败,也会自动关闭文件描述符。

    其中,with是一个语法糖。

    所有文件对象都支持上下文管理,支持with。

    另一种写法:

    with open('test') as f: # 把文件对象重命名为ff.write('efg')  # f对象写f.write(str(1/0)) # 保证文件对象退出时,关闭文件对象
    

​ 对于类似于文件对象的IO对象,一般都需要在不使用时关闭、注销以释放资源。IO被打开时,会获得一个文件描述符。计算机资源有限,所有操作系统都会对其做限制,这是为了保护计算机资源不被完全耗尽。一般情况下,除非特别明确知道资源情况,否则不要提高资源的限制值来解决问题。

Python文件IO基础,看这篇文章就够了!相关推荐

  1. python入门书籍推荐,看这篇文章就够,请!

    python入门书籍推荐,看这篇文章就够,请! 事实上,有关python的书籍很多很多,就当当网就有50000件和python有关的书籍,我特地去了当地的新华书店,蹲了大半天,回来给大家推荐,适合想学 ...

  2. python request 等待网页加载_用Python开发爬虫,看这篇文章就够了

    现在Python语言大火,在网络爬虫.人工智能.大数据等领域都有很好的应用.今天我向大家介绍一下Python爬虫的一些知识和常用类库的用法,希望能对大家有所帮助. 其实爬虫这个概念很简单,基本可以分成 ...

  3. Python-Excel 零基础学习xlwings,看这篇文章就够了

    零基础学习xlwings,看这篇文章就够了 | 一起大数据-技术文章心得 (17bigdata.com) 1.xlwings是什么 2.xlwings安装更新与卸载 3.xlwings详细使用 4.案 ...

  4. Vue开发入门看这篇文章就够了

    摘要: 很多值得了解的细节. 原文:Vue开发看这篇文章就够了 作者:Random Fundebug经授权转载,版权归原作者所有. 介绍 Vue 中文网 Vue github Vue.js 是一套构建 ...

  5. 好多人都说存储过程很难?认真看这篇文章就够了

    何为存储过程? 存储过程是在数据库管理系统中保存的.预先编译的并能实现某种功能的sql程序,说直白点,java知道吧?和java的方法一样. 每遇到一个新的知识点时,我们都会看看它的优点,从而加深对它 ...

  6. 使用分层网络模型的两个优点是什么_从零开始学网络|搞懂OSI参考模型和TCP/IP分层模型,看这篇文章就够了...

    从零开始学网络|搞懂OSI参考模型和TCP/IP分层模型,看这篇文章就够了​mp.weixin.qq.com 前言 今天和大家一起谈谈"网络",之前写的文章可能不太通俗易懂,有人就 ...

  7. java黄油刀_ButterKnife原理解析看这篇文章就够了

    原标题:ButterKnife原理解析看这篇文章就够了 作者:SheHuan https://juejin.im/post/5acec2b46fb9a028c6761628 ButterKnife 算 ...

  8. Android 8.0新特性(看这篇文章就够了)

    2019独角兽企业重金招聘Python工程师标准>>> 在刚结束不久的谷歌 I/O2017开发者大会上发布的第二个Android O(安卓8.0)开发者预览,并且向普通用户开放了第二 ...

  9. 万字心得,PMP学习考试那些事儿,看这篇文章就够了

    声明:文章为原创,首发于知乎,链接:万字长文!PMP考试那些事儿,看这篇文章就够了 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/524966002 以下内 ...

  10. 斗鱼html5插件音画不同步,音画不同步在直播中怎么解决?看这篇文章就够了!...

    原标题:音画不同步在直播中怎么解决?看这篇文章就够了! 什么是音画不同步? 很容易判断,就是画面和声音不匹配. 为什么会音画不同步? 首先我们要明白一个概念,虽然人的肉眼很容易辨别音画是否同步的,但是 ...

最新文章

  1. ASP.NET MVC5 高级编程 第3章 视图
  2. OpenCV之imgproc 模块. 图像处理(1)图像平滑处理 腐蚀与膨胀(Eroding and Dilating) 更多形态学变换 图像金字塔 基本的阈值操作
  3. Dubbo自定义日志拦截器
  4. Loadruner压数据库oracle,LoadRunner连接Oracle数据库(转)
  5. Windows安全配置加固
  6. java 实现 堆排序算法_C程序实现堆排序算法
  7. 双向循环链表:字母表实现前后移动
  8. 分治 —— 莫队算法
  9. Java Class 文件结构
  10. 嵌入式Linux入门5:移植总览
  11. Oracle数据同步接口,增量数据从ERP系统到本地临时表封装解决方案
  12. 分享大牛们的刷题经验——比你聪明的人还在拼命努力,你有什么资格浪费时间
  13. 网站开发流程以及HTML5简介(十)
  14. 旅游后台管理系列——SSM框架Web表现层
  15. 数学建模 最优化方法:动态规划 学习笔记
  16. I/O error on GET request for http://userservice/user/point/update: userservice; nested exception
  17. Mysql8.0修改数据库密码
  18. 智能门锁电路图_【干货】智能锁工作原理及技术原理分析
  19. 我上网下载了rar压缩文件,有密码,用arpr工具怎么破解不了,_压缩文件密码
  20. OSChina 周六乱弹 —— 你也有滚床单的这一天呀

热门文章

  1. ios越狱,impactor无法使用的替代方案
  2. 考研视频有点难,以后继续早上锻炼
  3. 9 使用AD滴泪与敷铜
  4. 亚商投资顾问 早餐FM/0407融资余额创新高
  5. Python - 面向对象编程 - 实战(4)
  6. 关于STM32的jlink仿真器突然不能工作的解决方法
  7. 下列哪个不是python的第三方库_以下选项中,不是 Python 中用于开发用户界面的第三方库是()...
  8. 制作美观GIS地图,掌握这些技巧就够了
  9. mysql 数据库字符集转换_字符集介绍及mysql数据库编码转换
  10. 字体使用的侵权是如何判定的?