• Python笔记3


    一、文件的操作

    1、文件的读、写、新增

    读文件的获取句柄的语法:f=open('path/file')

    句柄的理解:

    <_io.TextIOWrapper name='/root/myfile/test_directory/file1' mode='r' encoding='utf-8'>

    文件句柄包含硬盘位置,文件大小,字符集等信息 封装成一个内存对象给这个变量,句柄如光标只能往下移动

    文件全文读:read()

    f=open('tmpfile')
    f1=open('tmpfile')
    print(f)
    x=f.read()
    print(f)
    print(x)
    print('------------------------------------------')
    print(x)
    print('------------------------------------------')
    print(f1.read())
    print('------------------------------------------')
    print(f1.read())
    print('------------------------------------------')
    *******************************************************************************************************

    <_io.TextIOWrapper name='tmpfile' mode='r' encoding='utf-8'>
    <_io.TextIOWrapper name='tmpfile' mode='r' encoding='utf-8'>
    Doe, a deer, a female deer
      Ray, a drop of golden sun
      Me, a name I call myself
      Far, a long, long way to run
      Sew, a needle pulling thread
      La, a note to follow Sew
      Tea, a drink with jam and bread
      That will bring us back to Do (oh-oh-oh)
      Do-re-mi-fa-so-la-ti-do
      So-do!

    ------------------------------------------
    Doe, a deer, a female deer
      Ray, a drop of golden sun
      Me, a name I call myself
      Far, a long, long way to run
      Sew, a needle pulling thread
      La, a note to follow Sew
      Tea, a drink with jam and bread
      That will bring us back to Do (oh-oh-oh)
      Do-re-mi-fa-so-la-ti-do
      So-do!

    ------------------------------------------
    Doe, a deer, a female deer
      Ray, a drop of golden sun
      Me, a name I call myself
      Far, a long, long way to run
      Sew, a needle pulling thread
      La, a note to follow Sew
      Tea, a drink with jam and bread
      That will bring us back to Do (oh-oh-oh)
      Do-re-mi-fa-so-la-ti-do
      So-do!

    ------------------------------------------

    ------------------------------------------

    此代码的注意点:句柄被read()后,指向了文末,此时要再调用次句柄对文本进行read()则无法读到新内容。我们可以将句柄的初始状态赋值给一个变量来方便多次读取。一个文件可以打开多次获得多个句柄,如代码中的f、f1。默认在不指定方法的时候,文件以r方式打开。

    文件逐行读与显示行号的方法;readline()、readlines()、for i in f

    f=open('tmpfile','r',encoding='utf-8')
    f1=open('tmpfile','r',encoding='utf-8')
    f2=open('tmpfile','r',encoding='utf-8')
    print(f)
    x=f.readlines()
    w=f1.readline()
    print(''.center(50,'-'))
    print(x)
    print(''.center(50,'-'))
    print(w)
    print(''.center(50,'-'))
    for i in x :
       print(i.strip())
    for i ,j in enumerate(x):
          print(i,j.strip())
    count=0
    for i in f2:
        print(count,i.strip())
        count+=1
    》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

    <_io.TextIOWrapper name='tmpfile' mode='r' encoding='utf-8'>
    --------------------------------------------------
    ['Doe, a deer, a female deer\n', '\u3000\u3000Ray, a drop of golden sun\n', '\u3000\u3000Me, a name I call myself\n', '\u3000\u3000Far, a long, long way to run\n', '\u3000\u3000Sew, a needle pulling thread\n', '\u3000\u3000La, a note to follow Sew\n', '\u3000\u3000Tea, a drink with jam and bread\n', '\u3000\u3000That will bring us back to Do (oh-oh-oh)\n', '\u3000\u3000Do-re-mi-fa-so-la-ti-do\n', '\u3000\u3000So-do!\n']
    --------------------------------------------------
    Doe, a deer, a female deer

    --------------------------------------------------
    Doe, a deer, a female deer
    Ray, a drop of golden sun
    Me, a name I call myself
    Far, a long, long way to run
    Sew, a needle pulling thread
    La, a note to follow Sew
    Tea, a drink with jam and bread
    That will bring us back to Do (oh-oh-oh)
    Do-re-mi-fa-so-la-ti-do
    So-do!
    0 Doe, a deer, a female deer
    1 Ray, a drop of golden sun
    2 Me, a name I call myself
    3 Far, a long, long way to run
    4 Sew, a needle pulling thread
    5 La, a note to follow Sew
    6 Tea, a drink with jam and bread
    7 That will bring us back to Do (oh-oh-oh)
    8 Do-re-mi-fa-so-la-ti-do
    9 So-do!
    0 Doe, a deer, a female deer
    1 Ray, a drop of golden sun
    2 Me, a name I call myself
    3 Far, a long, long way to run
    4 Sew, a needle pulling thread
    5 La, a note to follow Sew
    6 Tea, a drink with jam and bread
    7 That will bring us back to Do (oh-oh-oh)
    8 Do-re-mi-fa-so-la-ti-do
    9 So-do!

    此代码的注意点:readline()为逐行读,readlines()可以将文本转换为列表。通过列表的特性,我们可以轻易的输出文本及行号。同时使用for i in f句柄的方式我们也可以逐行输出文本。

    文件的写入操作

    文件的读和写操作是分开的,在获得文件句柄的时候需要标注方法,如果没有写,默认为r,读的方法。同时需要强调一袋奶w方法属于新建,会清空文本的所有内容需要非常注意。

    f2=open('/root/myfile/test_directory/file2','w',encoding='utf-8')

    f=open('writetmp','w',encoding='utf-8')
    f.write('abc')
    f.write('def')
    f.write('\nggg')
    ------------------------------------------------------- [root@VM_61_212_centos my_python_file]
    # cat writetmp abcdef ggg

    该代码的注意点:write文件不存在会新建,文件存在会情况重写,如果没有手工换行,每次write将直接连着写入

    文件的追加w的方式处理文件,原有的内容会被清空重新写,使用a的方式处理文件,可以在保留文本内容的情况下在文档后进行追加

    f=open('writetmp','a',encoding='utf-8')
    f.write('111')
    f.write('222')
    f.write('\n333')
    *******************************************
    [root@VM_61_212_centos my_python_file]# cat writetmp 
    abcdef
    ggg
    [root@VM_61_212_centos my_python_file]# ./tmp_1.py 
    [root@VM_61_212_centos my_python_file]# cat writetmp 
    abcdef
    ggg111222
    333

    with获得句柄的方法:with open('file') a f:,使用次方法在with接受后,句柄能自动close()

    2、了解文件的组合型打开方式

    打开文件的模式有:

    • r,只读模式(默认)。
    • w,只写模式。【不可读;不存在则创建;存在则删除内容;】
    • a,追加模式。【可读;不存在则创建;存在则只追加内容;】

    "+" 表示可以同时读写某个文件

    • r+,可读写文件。【可读;可写;可追加】
    • w+,写读
    • a+,同a

    "U"表示在读取时,可以将 \r \n \r\n自动转换成 \n (与 r 或 r+ 模式同使用)rU

    • r+U

    "b"表示处理二进制文件(如:FTP发送上传ISO镜像文件,linux可忽略,windows处理二进制文件时需标注)

    • rb
    • wb
    • ab

    3、文件的替换、判断、定位、转换等操作

    输出文档名:使用f.name,注意f.name,name后面无需加()

    with open('tmpfile') as f:
         print(f.name)

    批量修改文档内容:使用字符串replace()

    #!/bin/env python3.6
    f=open("/root/myfile/test_directory/jueqi",'r',encoding='utf-8')
    f_new = open("/root/myfile/test_directory/jueqi.new",'w',encoding='utf-8')
    
    for i in f:
        if '崛起' in i:
           i = i.replace("崛起","***崛起***")
        f_new.write(i)
    f.close()
    f_new.close()

    此代码的说明:打开两个文件,读一行写一行来节省内存使用率

    对于句柄的定向移动:使用seek()、tell()来定位

    >>> f=open('/root/myfile/my_python_file/tmpfile')
    >>> f.readline()
    'Doe, a deer, a female deer\n'
    >>> f.tell()
    27
    >>> f.readline()
    '\u3000\u3000Ray, a drop of golden sun\n'
    >>> f.tell()
    59
    >>> f.readline()
    '\u3000\u3000Me, a name I call myself\n'
    >>> f.tell()
    90
    >>> f.seek(27)
    27
    >>> f.readline()
    '\u3000\u3000Ray, a drop of golden sun\n'
    >>> 
    >>> f.close()
    >>> f1=open('/root/myfile/my_python_file/tmpfile','a',encoding='utf-8')
    >>> f1.tell()
    319
    >>> f1.seek(27)
    27
    >>> f1.tell()
    27
    >>> f1.write('----------------------')
    22
    >>> f1.tell()
    341
    >>> f=open('/root/myfile/my_python_file/tmpfile')
    >>> f.readline()
    'Doe, a deer, a female deer\n'
    >>> f.readline()
    '\u3000\u3000Ray, a drop of golden sun\n'
    >>> f.readline()
    '\u3000\u3000Me, a name I call myself\n'
    >>> f.readline()
    '\u3000\u3000Far, a long, long way to run\n'
    >>> f.readline()
    '\u3000\u3000Sew, a needle pulling thread\n'
    >>> f.readline()
    '\u3000\u3000La, a note to follow Sew\n'
    >>> f.readline()
    '\u3000\u3000Tea, a drink with jam and bread\n'
    >>> f.readline()
    '\u3000\u3000That will bring us back to Do (oh-oh-oh)\n'
    >>> f.readline()
    '\u3000\u3000Do-re-mi-fa-so-la-ti-do\n'
    >>> f.readline()
    '\u3000\u3000So-do!\n'
    >>> f.readline()
    '----------------------'
    >>> f.readline()
    ''
    >>> f.readline()
    ''
    >>> f2=open('tmpxxx','w',encoding='utf-8')
    >>> f2.write('hello\n')
    6
    >>> f2.write('world\n')
    6
    >>> f2.tell()
    12
    >>> f2.seek(6)
    6
    >>> f2.tell()
    6
    >>> f2.write('hi\n')
    3
    >>> f3=open('tmpxxx','r',encoding='utf-8')
    >>> f3.read()
    'hello\nworld\n'
    >>> f2.write('hi\n')
    3
    >>> f2.write('hi\n')
    3
    >>> f2.write('hi\n')
    3
    >>> f2.write('hi\n')
    3
    >>> f3.read()
    ''
    >>> f3.seek(0)
    0
    >>> f3.read()
    'hello\nworld\n'
    >>> f2.tell()
    21
    >>> exit()
    [root@VM_61_212_centos my_python_file]# cat tmpxxx 
    hello
    hi
    hi
    hi
    hi
    hi

    该代码的注意点:使用seek()和tell()的时候,对于a方式打开的文件,动作依然是追加,不会因为seek到上文而在文中添加。做write操作的时候因为内容保存在内存中还未flush到硬盘的原因,在查看文件的时候会出现没有修改成功的假象

    对于句柄权限的判断:writealbe(),readable()、seekable(),判断句柄是否可读、可写、可跳(seek)

    print(f.seekable(),"如果句柄可跳回则打印true,如果不行则返回false,seekable")
    print(f.readable(),"如果句柄可读则打印true,如果不行则返回false,readable")
    print(f.writable(),"如果句柄可写则打印true,如果不行则返回false,writable")

    硬盘实时刷新:flush()的使用,在我们对硬盘文件进行操作的过程中,我们操作的内容是保存在内存中的,系统按照一点的规律将内容在保存到硬盘,在一些对修改时效性要求比较高的场景下,我们要求将修改实时同步到硬盘。此时需要刷新硬盘,flush()

    import sys
    import time
    for
    i in range(20): sys.stdout.write('#') #此处不是print sys.stdout.flush() time.sleep(0.2) print("100%")
    ----------------------------------------------

    ####################100%

    此代码的说明:需要使用两个模块,sys和time。对比flush()和没有flush()的情况。没有flush()的时候,进度条是一口气输出的。

    文档内列表字典列表的转换:eval(),我们可以将一个大型的列表、字典、字符串按照格式写在文件中。通过eval进行赋值,用以程序使用。

    f_str=open('str_tmp')
    f_list=open('list_tmp')
    f_dic=open('dic_tmp')
    x=f_str.read().strip()
    y=f_list.read().strip()
    z=f_dic.read().strip()
    print(type(x),type(y),type(z))
    #x=eval(x)
    y=eval(y)
    z=eval(z)
    print(x,y,z,type(x),type(y),type(z))
    -------------------------------------------------------

    <class 'str'> <class 'str'> <class 'str'>
    we are ready ['a', 'b', 'c', 'd', 1, 2, 3, 4] {'1': {'1.1': '1.1.1'}, '2': {'2.2': '2.2.2'}, '3': {'3.3.': '3.3.3'}} <class 'str'> <class 'list'> <class 'dict'>

    -----------------------------------------------------------

    [root@VM_61_212_centos my_python_file]# cat list_tmp
    ['a','b','c','d',1,2,3,4]
    [root@VM_61_212_centos my_python_file]# cat dic_tmp
    {'1':{'1.1':'1.1.1'},'2':{'2.2':'2.2.2'},'3':{'3.3.':'3.3.3'}}

    [root@VM_61_212_centos my_python_file]# cat list_tmp
    ['a','b','c','d',1,2,3,4]

    本代码的注意点:代码注释部分,将str转为str会报错,因为本身就已经是str

    4、 encoding='utf-8'的理解

     [root@VM_61_212_centos my_python_file]# cat xxx.txt 
    אτ 
    [root@VM_61_212_centos my_python_file]# python3.6
    Python 3.6.3 (default, Nov 14 2017, 15:55:17) 
    >>> f=open('/root/myfile/my_python_file/xxx.txt',encoding='gbk')
    >>> print(f.encoding)
    gbk
    >>> x=f.read()
    >>> print(x)
    中文
    [root@VM_61_212_centos my_python_file]# cat tmp_1.py 
    #!/bin/env python3.6
    with open('xxx.txt',encoding='gbk') as f:
         x=f.read().strip()
         print(x,type(x))
    with open('bbb','w',encoding='utf-8') as f:
         f.write(x)
    with open('ccc','w',encoding='gbk') as f: 
         f.write(x)
    [root@VM_61_212_centos my_python_file]# cat xxx.txt 
    אτ
    [root@VM_61_212_centos my_python_file]# cat bbb
    中文
    [root@VM_61_212_centos my_python_file]# cat ccc
    אτ

     此代码的说明

    • xxx.txt是用windows写的一个gbk格式的文件,发送到linux中,使用cat打开后,由于xshell是utf-8格式,所以显示为乱码。此时可以调整xshell的显示格式,可以正常显示文字
    • 使用python3.x打开此文件,encoding=gbk,用来告诉python此文件的格式以及需要把这个文件最后处理为什么格式
    • 本代码中,将xxx.txt打开后,分别保存为utf-8以及gbk格式的文件bbb、ccc。在xshell中调整编码,分别cat来查看及验证。

    二、函数的概念及使用

    定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可

    特性:

    • 减少重复代码
    • 使程序变的可扩展
    • 使程序变得易维护

    1、定义函数:def

    import time
    _custom=('%Y-%m-%d %X')

    def
    func1(): x=time.strftime(_custom) time.sleep(1) print(x,'func1 is ok') def func2(): x=time.strftime(_custom) time.sleep(1) print(x,'func2 is ok') func1() func2()
    -----------------------------------------------------
    2017-12-06 20:38:28 func1 is ok
    2017-12-06 20:38:29 func2 is ok

    此代码的注意点:time.strftime()指令需要制定一个输出格式的模板,如果没有模板会报错,先记住%Y-%m-%d %X这种格式即可

    2、函数返回值:5种情况:有一个返回值/无返回值/有一大串返回值/返回值为函数/返回值为函数运行结果。

    import time
    def fun1():
        return 1,'hello',[1,2,3,4,5],{'123':"666",'222':"hello"}
    def fun2():
         return fun1
    def fun3():
          return fun2() #如果有括号则返回调用函数的函数值
    def fun4():
        time.sleep(0.1)
    x=fun1()
    y=fun2()
    z=fun3()
    i=fun4()
    print(x)
    print(y)
    print(z)
    print(i)

    此代码的说明:func1为一大串返回值,则结果为一个元组、func2为返回一个函数,则返回这个函数在内存中的地址、func3返回值为函数的运行结果。func4无指定返回值故放回null、/3

    3、函数的参数及传入

    函数的参数有形参、实参、非固定参( *args *.kwargs) 及参数传入。

    形参:定义函数时的占位替代,实参:实际传入函数中被使用的。默认参数(定义函数的时候写入,不赋值则保持默认)关键参数:赋值的另外一种形式 ,不按照顺序

    *.args的使用:两种赋值方式,一种为直接写,一种为*[]或*()

     

    def test1(x,y,*args):
        print(x,y)
        print(args)
    test1(1,2,3,4,5)
    test1('a','b','c','d',[1,2,3,4,5],[6,7],{'a1':'aa','b1':'xxx'})
    test1('a','b','c','d',*[1,2,3,4,5],*(6,7),{'a1':'aa','b1':'xxx'})
    --------------------------------------------------------------------
    1 2
    (3, 4, 5)
    a b
    ('c', 'd', [1, 2, 3, 4, 5], [6, 7], {'a1': 'aa', 'b1': 'xxx'})
    ('c', 'd', 1, 2, 3, 4, 5, 6, 7, {'a1': 'aa', 'b1': 'xxx'})

     

    此程序的注意点:*.argv在参数传入的时候会将参数转换为一个元组,参数可以是列表或字典。如果再列表和元组前面加上*,则元素会被单独提取出来

    需要和sys.argv对比,sys.argv,sys.argv是将参数转换为列表

    import sys
    x=sys.argv
    print(x)
    [root@VM_61_212_centos my_python_file]# ./tmp_1.py 1,2,3,4
    ['./tmp_1.py', '1,2,3,4']

    **kwagrs的使用:两种赋值方式,一种key=“value”一种**+字典,输入之后就是字典

    def test1(**kwargs):
        print(kwargs)
    def test2(name,**kwarg2):
        print(name)
        print(kwarg2)
    def test3(name,age=18,**kwargs):
        print(name)
        print(age)
        print(kwargs)
    def test4(name,age=18,*args,**kwargs):
          print(name)
          print(age)
          print(args)
          print(kwargs)
    test1(name="yomi",age='123')
    test1(**{'name':'360yomi','age':"567"})
    test2('tom',k1='hello',k2='world')
    test3('xxx',3,hobby='badminton',car='aodi')
    test3('yyy',hobby='football',car='tesla',age=66)#用关键字的形式来传递这个参数
    test4('xiaoming',22,company='360')
    test4('xiaodong',52,1,2,3,4,5,company='qq',lang="ok")
    ----------------------------------------------------------------------------
    {'name': 'yomi', 'age': '123'}
    {'name': '360yomi', 'age': '567'}
    tom
    {'k1': 'hello', 'k2': 'world'}
    xxx
    3
    {'hobby': 'badminton', 'car': 'aodi'}
    yyy
    66
    {'hobby': 'football', 'car': 'tesla'}
    xiaoming
    22
    ()
    {'company': '360'}
    xiaodong
    52
    (1, 2, 3, 4, 5)
    {'company': 'qq', 'lang': 'ok'}

    4、局部变量与全局变量

    • 全局变量,可以在函数中直接引用,不用传入
    • 局部变量,在函数体内可以和全局变量同名,在函数体内生效
    • 退出函数后,局部变量对全局变量的赋值不生效
    • 函数内定义的局部变量,退出函数后不能使用
    • 可以在函数中使用global来定义变量,则影响全局(包括新建变量)
    • 不能将形参定义为global
    • 大部分情况下,不要再函数中去更改全局变量的值
      school = '北大'
      name = '马云'
      print('进入函数前的name和school:',name,school)
      def change_name(name):
           global school
           print('进入函数'.center(50,'*'))
           print('befor change',name,school)
           name = "马化腾"
           school ="清华"
           print('after change',name,school)
           global age
           age = 25
           print('school:',school,'age:',age)
           print('退出函数'.center(50,'*'))
      change_name(name)
      print('退出函数后的name和school:',name,school,age)
      -----------------------------------------------------------------------------------
      进入函数前的name和school: 马云 北大
      ***********************进入函数***********************
      befor change 马云 北大
      after change 马化腾 清华
      school: 清华 age: 25
      ***********************退出函数***********************
      退出函数后的name和school: 马云 清华 25

      对于字典和列表的局部操作:

    a=[1,2,3,4,5]
    b='kkk'
    c={1:'aaa',2:'bbb'}
    def myprint():
         global b
         b=b.replace('k','q') #不能再函数内对全局变量本身进行改变,除非用globe
         a[1]=6
         c[1]='good'
    print('befor change:')
    print(b,a,c)
    print('after change:')
    myprint()
    print(b,a,c)
    -----------------------------------------------------------
    befor change:
    kkk [1, 2, 3, 4, 5] {1: 'aaa', 2: 'bbb'}
    after change:
    qqq [1, 6, 3, 4, 5] {1: 'good', 2: 'bbb'

    此代码的注意点:在函数内,我们可以通过下标或关键字的方式去给列表和字典进行修改,能作用到全局,但是,如果是进行新建,则不会作用到全局,此处应和浅copy机制相结合,深入理解python中变量在内存中的形式。对比下面的代码

    a=[1,2,3,4,5]
    b='kkk'
    c={1:'aaa',2:'bbb'}
    def myprint():
         b='xxxooo'
         a=[8988,891823,122]
         c={1:'xxx',2:'ooo'}
         print(b,a,c)
    myprint()
    print(b,a,c)
    ---------------------------------------------------------
    xxxooo [8988, 891823, 122] {1: 'xxx', 2: 'ooo'}
    kkk [1, 2, 3, 4, 5] {1: 'aaa', 2: 'bbb'}

    5、函数的嵌套与递归

    在函数内部,可以调用其他函数,这种方式叫函数的嵌套,如果一个函数在内部调用自身本身,这个函数就是递归函数。

    def digui(n):
        print(n,'准备执行digui(%s)'%int(n/2))
        if n > 0:
            return (digui(int(n/2)))
        print('--> ok!')
    def digui1(n):
        print(n)
        if n > 0:
            return (int (n/2)) #----->直接返回值函数退出 不打印
        print('--> ok!')
    x=10
    digui(x)
    digui1(x)
    -------------------------------------------------
    10 准备执行digui(5)
    5 准备执行digui(2)
    2 准备执行digui(1)
    1 准备执行digui(0)
    0 准备执行digui(0)
    --> ok!
    10

    此代码的注意点:函数在有明确的返回值后,就不会再进行下一步操作。 如图中标记的print ok,为什么没有被打印出来。

    6、高阶函数

    高阶函数->使主函数本身很简洁

    变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

    def double_x(a):
        i=a*2
        return i
    def gaojie(x,y,z):
        result = z(x)+z(y)
        print(result)
    gaojie(100,200,double_x)#此处double_x不用加()
    -------------------------------------------------------
    600

    三、补充一点:字体颜色的操作

    语法:print("\033[31;1m余额不足,当前余额为%s\033[0m"%(salary))

    \33[0m 关闭所有属性
    \33[1m 设置高亮度
    \33[4m 下划线
    \33[5m 闪烁
    \33[7m 反显
    \33[8m 消隐
    \33[30m -- \33[37m 设置前景色
    字颜色:30-----------37
    30:黑
    31:红
    32:绿
    33:黄
    34:蓝色
    35:紫色
    36:深绿
    37:白色

    \33[40m -- \33[47m 设置背景色
    字背景颜色范围:40----47
    40:黑
    41:深红
    42:绿
    43:黄色
    44:蓝色
    45:紫色
    46:深绿
    47:白色
    \33[90m -- \33[97m 黑底彩色
    90:黑
    91:深红
    92:绿
    93:黄色
    94:蓝色
    95:紫色
    96:深绿
    97:白色

     

     

     

     

  • 相关阅读:
    JavaEE基础(01):Servlet实现方式,生命周期执行过程
    Spring 框架基础(06):Mvc架构模式简介,执行流程详解
    Spring 框架基础(05):事务管理机制,和实现方式
    多线程搜索与排序
    mybatis的Mapper代理原理
    spring的RestTemplate使用指南
    探索CAS无锁技术
    两年Java的面试经验
    HashMap多线程并发的问题
    解析Mybaits的insert方法返回数字-2147482646的原因
  • 原文地址:https://www.cnblogs.com/yomi/p/7981828.html
Copyright © 2020-2023  润新知