zoukankan      html  css  js  c++  java
  • python常用模块

    time模块

      时间表示形式

      在Python中,通常有这三种方式来表示时间:时间戳、元组(struct_time)、格式化的时间字符串:

      (1)时间戳(timestamp) :通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。

    import time
    
    print(time.time())#1504603265.310429

      (2)格式化的时间字符串(Format String): ‘1988-03-16’

    res=time.strftime("%Y-%m-%d %X")
    print(res)#2017-09-05 17:22:14

      (3)元组(struct_time) :struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等)

    res1=time.localtime()
    print(res1)#time.struct_time(tm_year=2017, tm_mon=9, tm_mday=5, tm_hour=17, tm_min=22, tm_sec=59, tm_wday=1, tm_yday=248, tm_isdst=0)

      为什么要搞三种时间方式呢,当然是有用的啊,时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的。

      这三种时间之间的转换方式。

    res2=time.mktime(res1)#结构化时间转换为时间戳
    print(res2)#1504603588.0
    
    res3=time.localtime(time.time())#时间戳转化为结构化时间
    print(res3)
    #time.struct_time(tm_year=2017, tm_mon=9, tm_mday=5, tm_hour=17, tm_min=29, tm_sec=4, tm_wday=1, tm_yday=248, tm_isdst=0)
    res4=time.gmtime(time.time())
    print(res4)#时间戳转化为结构化时间
    #time.struct_time(tm_year=2017, tm_mon=9, tm_mday=5, tm_hour=9, tm_min=31, tm_sec=1, tm_wday=1, tm_yday=248, tm_isdst=0)
    res5=time.strftime("%Y-%m-%d %X", time.localtime())#结构化时间转化为字符串时间
    print(res5)
    res6=time.strptime(time.strftime("%Y-%m-%d %X"),"%Y-%m-%d %X")#字符串时间转化为结构化时间
    print(res6)
    #time.struct_time(tm_year=2017, tm_mon=9, tm_mday=5, tm_hour=17, tm_min=38, tm_sec=13, tm_wday=1, tm_yday=248, tm_isdst=-1)

    res=time.asctime(time.localtime())
    print(res)#Tue Sep  5 17:43:34 2017
    
    res1=time.ctime(time.time())
    print(res1)#Tue Sep  5 17:44:20 2017
    import time
     
     
    # print(time.clock()) #返回处理器时间,3.3开始已废弃 , 改成了time.process_time()测量处理器运算时间,不包括sleep时间,不稳定,mac上测不出来
    # print(time.altzone)  #返回与utc时间的时间差,以秒计算
    # print(time.asctime()) #返回时间格式"Fri Aug 19 11:14:16 2016",
    # print(time.localtime()) #返回本地时间 的struct time对象格式
    # print(time.gmtime(time.time()-800000)) #返回utc时间的struc时间对象格式
     
    # print(time.asctime(time.localtime())) #返回时间格式"Fri Aug 19 11:14:16 2016",
    #print(time.ctime()) #返回Fri Aug 19 12:38:29 2016 格式, 同上
     
     
     
    # 日期字符串 转成  时间戳
    # string_2_struct = time.strptime("2016/05/22","%Y/%m/%d") #将 日期字符串 转成 struct时间对象格式
    # print(string_2_struct)
    # #
    # struct_2_stamp = time.mktime(string_2_struct) #将struct时间对象转成时间戳
    # print(struct_2_stamp)
     
     
     
    #将时间戳转为字符串格式
    # print(time.gmtime(time.time()-86640)) #将utc时间戳转换成struct_time格式
    # print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()) ) #将utc struct_time格式转成指定的字符串格式
     
     
     
     
     
    #时间加减
    import datetime
     
    # print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925
    #print(datetime.date.fromtimestamp(time.time()) )  # 时间戳直接转成日期格式 2016-08-19
    # print(datetime.datetime.now() )
    # print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天
    # print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天
    # print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时
    # print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分
     
     
    #
    # c_time  = datetime.datetime.now()
    # print(c_time.replace(minute=3,hour=2)) #时间替换
    time_test

    random模块

       随机数模块

    >>> import random
    >>> random.random()      # 大于0且小于1之间的小数
    0.7664338663654585
    
    >>> random.randint(1,5)  # 大于等于1且小于等于5之间的整数
    2
    
    >>> random.randrange(1,3) # 大于等于1且小于3之间的整数
    1
    
    >>> random.choice([1,'23',[4,5]])  # #1或者23或者[4,5]
    1
    
    >>> random.sample([1,'23',[4,5]],2) # #列表元素任意2个组合
    [[4, 5], '23']
    
    >>> random.uniform(1,3) #大于1小于3的小数
    1.6270147180533838
    
    >>> item=[1,3,5,7,9]
    >>> random.shuffle(item) # 打乱次序
    >>> item
    [5, 1, 3, 7, 9]
    >>> random.shuffle(item)
    >>> item
    [5, 9, 7, 1, 3]
    def validate():
    
        s=""
        for i in range(5):
             rNum=random.randint(0,9)
             r_alph=chr(random.randint(65,90))
    
             ret=random.choice([str(rNum),r_alph])
             s+=ret
    
        return s
    
    print(validate())
    生成验证码

    hashlib模块

      Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。

      什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。

      摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

      摘要算法MD5为例,计算出一个字符串的MD5值:

    m=hashlib.md5()
    
    m.update("alex".encode("utf8"))  #534b44a19bf18d20b71ecc4eb77c572f
    
    print(m.hexdigest())

      摘要算法应用

      用户登录的网站都会存储用户登录的用户名和口令。如果以明文保存用户口令,如果数据库泄露,后果不堪设想。很多用户喜欢用123456,888888,password这些简单的口令,由于常用口令的MD5值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”:

    hashlib.md5("salt".encode("utf8"))

      摘要算法在很多地方都有广泛的应用。要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。

    os模块

    os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
    os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
    os.curdir  返回当前目录: ('.')
    os.pardir  获取当前目录的父目录字符串名:('..')
    os.makedirs('dirname1/dirname2')    可生成多层递归目录
    os.removedirs('dirname1')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
    os.mkdir('dirname')    生成单级目录;相当于shell中mkdir dirname
    os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
    os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
    os.remove()  删除一个文件
    os.rename("oldname","newname")  重命名文件/目录
    os.stat('path/filename')  获取文件/目录信息
    os.sep    输出操作系统特定的路径分隔符,win下为"\",Linux下为"/"
    os.linesep    输出当前平台使用的行终止符,win下为"	
    ",Linux下为"
    "
    os.pathsep    输出用于分割文件路径的字符串 win下为;,Linux下为:
    os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
    os.system("bash command")  运行shell命令,直接显示
    os.environ  获取系统环境变量
    os.path.abspath(path)  返回path规范化的绝对路径
    os.path.split(path)  将path分割成目录和文件名二元组返回
    os.path.dirname(path)  返回path的目录。其实就是os.path.split(path)的第一个元素
    os.path.basename(path)  返回path最后的文件名。如何path以/或结尾,那么就会返回空值。即os.path.split(path)的第二个元素
    os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False
    os.path.isabs(path)  如果path是绝对路径,返回True
    os.path.isfile(path)  如果path是一个存在的文件,返回True。否则返回False
    os.path.isdir(path)  如果path是一个存在的目录,则返回True。否则返回False
    os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
    os.path.getatime(path)  返回path所指向的文件或者目录的最后访问时间
    os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间
    os.path.getsize(path) 返回path的大小

      os.stat('path/filename') 获取文件/目录信息

    stat 结构:
    
    st_mode: inode 保护模式
    st_ino: inode 节点号。
    st_dev: inode 驻留的设备。
    st_nlink: inode 的链接数。
    st_uid: 所有者的用户ID。
    st_gid: 所有者的组ID。
    st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。
    st_atime: 上次访问的时间。
    st_mtime: 最后一次修改的时间。
    st_ctime: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间(详细信息参见平台的文档)。
    os.stat

     sys模块

    sys.argv           命令行参数List,第一个元素是程序本身路径
    sys.exit(n)        退出程序,正常退出时exit(0)
    sys.version        获取Python解释程序的版本信息
    sys.maxint         最大的Int值
    sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
    sys.platform       返回操作系统平台名称/win32

      常用功能

    sys.arg 获取位置参数
    print(sys.argv)
    
    执行该脚本,加参数的打印结果
    python3 m_sys.py  1 2 3 4 5
    
    ['m_sys.py', '1', '2', '3', '4', '5']
    可以发现 sys.arg返回的是整个位置参数,类似于shell的$0 $1...
    sys.exit(n) 程序退出,n是退出是返回的对象
    sys.version 获取python版本
    >>> sys.version
    '3.5.1 (v3.5.1:37a07cee5969, Dec  5 2015, 21:12:44) 
    [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]'
    sys.path 返回模块的搜索路径列表,可通过添加自定义路径,来添加自定义模块
    >>> sys.path
    ['', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages']
    
    sys.platform 返回当前系统平台 linux平台返回linux,windows平台返回win32,MAC返回darwin
    >>> sys.platform
    'darwin
    sys.stdout.write() 输出内容
    >>> sys.stdout.write('asd')
    asd3
    import sys
    print(sys.argv)
    # name = sys.argv[1]
    # password = sys.argv[2]
    # if name == 'egon' and password == '111':
    #     print('继续执行程序')
    # else:
    #     exit()
    # sys.exit()
    print(sys.version)
    print(sys.maxsize)
    print(sys.path)
    # sys.path.clear()  #清除下载的模块
    import requests
    print(sys.platform)
    python a.py egon 111

    logging模块

      日志模块有五个等级,debug,info,warning(默认),error,critical五个等级。

    logging.debug('debug message')
    num=1000
    logging.info('cost %s'%num)
    
    logging.warning('warning messagegfdsgsdfg') #默认高于warning才显示
    
    logging.error('error message')
    
    logging.critical('critical message')

      congfig函数

    logging.basicConfig(level=logging.DEBUG,#默认的等级
                        format="%(asctime)s [%(lineno)s] %(message)s",#设定格式
                        datefmt="%Y-%m-%d %H:%M:%S",
                        filename="logger",
                        filemode="a"
                        )
    Logger.debug('logger debug message')
    Logger.info('logger info message')
    Logger.warning('logger warning message')
    Logger.error('logger error message')
    Logger.critical('logger critical message')
    logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
    
    filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
    filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
    format:指定handler使用的日志显示格式。
    datefmt:指定日期时间格式。
    level:设置rootlogger(后边会讲解具体概念)的日志级别
    stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
    
    format参数中可能用到的格式化串:
    %(name)s Logger的名字
    %(levelno)s 数字形式的日志级别
    %(levelname)s 文本形式的日志级别
    %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
    %(filename)s 调用日志输出函数的模块的文件名
    %(module)s 调用日志输出函数的模块名
    %(funcName)s 调用日志输出函数的函数名
    %(lineno)d 调用日志输出函数的语句所在的代码行
    %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
    %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
    %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
    %(thread)d 线程ID。可能没有
    %(threadName)s 线程名。可能没有
    %(process)d 进程ID。可能没有
    %(message)s用户输出的消息

      logger模块

    logging.basicConfig(level=logging.DEBUG,#默认的等级
                        format="%(asctime)s [%(lineno)s] %(message)s",#设定格式
                        datefmt="%Y-%m-%d %H:%M:%S",#调格式asctime
                        filename="logger",#写到文件
                        filemode="a"#写的方式
                        )
    def get_logger():
    
        logger=logging.getLogger()#拿到logger对象
    
        fh=logging.FileHandler("logger2")#文件流,写到文件
    
        sh=logging.StreamHandler()#屏幕流,输出到文件
    
        logger.setLevel(logging.DEBUG)  #设定输出等级
    
        fm=logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")#格式对象
    
        logger.addHandler(fh)#添加的动作
    
        logger.addHandler(sh)#添加流
    
        fh.setFormatter(fm)#设置格式
        sh.setFormatter(fm)
    
        return logger
    
    Logger=get_logger()
    
    Logger.debug('logger debug message')
    Logger.info('logger info message')
    Logger.warning('logger warning message')
    Logger.error('logger error message')
    Logger.critical('logger critical message')
    logger模块

     re模块

      在线匹配re的网址,开源中国http://tool.oschina.net/regex/#。

    #字符匹配(普通字符,元字符)
    #普通字符
    #元字符
     .    ^    $   *   ?    +   {}   []    |    ()      
        :   反斜杠后边跟元字符去除特殊功能
              反斜杠后边跟普通字符实现特殊功能
              引用序号对应的字组所匹配的字符串
         
        d  匹配任何十进制数,[0-9]
        D  匹配任何非数字字符,[^0-9]
        s   匹配任何空白字符,[	
    
    fv]
        S   匹配任何非空白字符,[ ^ 	
    
    fv]
        w  匹配任何数字字母字符,[a-zA-Z0-9_]
        W  匹配任何非字母数字字符,[^a-zA-Z0-9_]
           匹配一个单词边界,也就是指单词和空格间的位置
    origin = "hello world hi"
    
    r = re.match("hw+",origin)
    print(r.group())           #获取匹配到的所有结果
        hello
        
    r = re.match("(h)(w+)",origin)
    print(r.groups())          #获取模型中匹配到的分组结果
        ('h', 'ello')
        
    r = re.match("(?P<n1>h)(?P<n2>w+)",origin)
        {'n1': 'h', 'n2': 'ello'}
    re.match(从头匹配)
    origin = "hello alex alex bcd abcd lge acd 19"
    
    r = re.finditer("(a)((w+)(e))(?P<n1>x)",origin)
    for i in r:
        print(i)
            <_sre.SRE_Match object; span=(6, 10), match='alex'>
        print(i.group())
            alex
        print(i.groupdict())
            {'n1': 'x'}
    re.finditer
     #将匹配到的内容放到一个列表里
    import re
    
    origin = "hello alex hello alex bcd abcd lge acd 19"
    
    
    # .
    
    print(re.findall("h...o",origin))
        ['hello', 'hello']
    
    
    # ^
    
    print(re.findall("^h...o",origin))
        ['hello']
    
    # $
    
    print(re.findall("h...o$",origin))
        []
    
    # ?
    
    print(re.findall("d?","aaaddddccc"))
        ['', '', '', 'd', 'd', 'd', 'd', '', '', '', '']
    
    # +
    
    print(re.findall("d+","aaaddddccc"))
        ['dddd']
    
    # *
    
    print(re.findall("d*","aaaddddccc"))
        ['', '', '', 'dddd', '', '', '', '']
    
    # {}
    
    print(re.findall("d{4}","aaaddddccc"))
        ['dddd']
    
    #注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配
    print(re.findall("ad*?","addddccc"))
        ['a']
    
    print(re.findall("ad*?","aaaaddddccc"))
        ['a', 'a', 'a', 'a']
    
    
    # []    []里面只有 ^ -  有特殊含义
    
    
    print(re.findall("a[bc]d","abdfffacd"))
        ['abd', 'acd']
    
    print(re.findall("q[a-z]","qab"))
        ['qa']
    
    print(re.findall("q[^a-z]*","q12355"))
        ['q12355']
    
    
    #匹配括号中没有括号
    print(re.findall("([^()]*)","12+(34*6+2-5*(2-1))"))
        ['(2-1)']
    re.findall
    print(re.search("(?P<name>[a-z]+)","alex36wusir34egon33").group())
        alex
    
    print(re.search("(?P<name>[a-z]+)d+","alex36wusir34egon33").group())
        alex36
    
    print(re.search("(?P<name>[a-z]+)d+","alex36wusir34egon33").group("name"))
        alex
    
    print(re.search("(?P<name>[a-z]+)(?P<age>d+)","alex36wusir34egon33").group("age"))
        36
    re.search
    print(re.split(" ","alex egon hao"))
        ['alex', 'egon', 'hao']
    
    print(re.split("[ |]","alex egon|hao"))
        ['alex', 'egon', 'hao']
    
    print(re.split("[ab]","abc"))
        ['', '', 'c']
    
    print(re.split("[ab]","asdabcd"))
        ['', 'sd', '', 'cd']
    re.split
    #将数字替换成A
    print(re.sub("d+","A","aakk123ddd55kk66"))
        aakkAdddAkkA
    
    #匹配前4次
    print(re.sub("d","A","aakk123ddd55kk66",4))
        aakkAAAdddA5kk66
    
    
    #subn 替换的次数按元组显示出来
    
    print(re.subn("d","A","aakk123ddd55kk66"))
        ('aakkAAAdddAAkkAA', 7)
    re.sub()
    print(re.findall("([^()]*)","12+(34*6+2-5*(2-1))"))
        ['(2-1)']
    
    print(re.findall("www.(baidu|163).com","www.baidu.com"))
        ['baidu']
    
    print(re.findall("www.(?:baidu|163).com","www.baidu.com"))
        ['www.baidu.com']
    
    print(re.findall("(abc)+","abcabcabc"))
    ['abc']
    
    print(re.findall("(?:abc)+","abcabcabc"))
        ['abcabcabc']
    括号匹配

    configparser模块

      好多软件的常见文档格式如下:

    [DEFAULT]
    ServerAliveInterval = 45
    Compression = yes
    CompressionLevel = 9
    ForwardX11 = yes
      
    [bitbucket.org]
    User = hg
      
    [topsecret.server.com]
    Port = 50022
    ForwardX11 = no

      生成这样的文档要使用到configparser模块。

      创建:

    import configparser
      
    config = configparser.ConfigParser()
    config["DEFAULT"] = {'ServerAliveInterval': '45',
                          'Compression': 'yes',
                         'CompressionLevel': '9'}
      
    config['bitbucket.org'] = {}
    config['bitbucket.org']['User'] = 'hg'
    config['topsecret.server.com'] = {}
    topsecret = config['topsecret.server.com']
    topsecret['Host Port'] = '50022'     # mutates the parser
    topsecret['ForwardX11'] = 'no'  # same here
    config['DEFAULT']['ForwardX11'] = 'yes'<br>
    with open('example.ini', 'w') as configfile:
       config.write(configfile)

      基本操作增删改查:

    import configparser
    
    config = configparser.ConfigParser()
    
    #---------------------------------------------查
    print(config.sections())   #[]
    
    config.read('example.ini')
    
    print(config.sections())   #['bitbucket.org', 'topsecret.server.com']
    
    print('bytebong.com' in config)# False
    
    print(config['bitbucket.org']['User']) # hg
    
    print(config['DEFAULT']['Compression']) #yes
    
    print(config['topsecret.server.com']['ForwardX11'])  #no
    
    
    for key in config['bitbucket.org']:
        print(key)
    
    
    # user
    # serveraliveinterval
    # compression
    # compressionlevel
    # forwardx11
    
    
    print(config.options('bitbucket.org'))#['user', 'serveraliveinterval', 'compression', 'compressionlevel', 'forwardx11']
    print(config.items('bitbucket.org'))  #[('serveraliveinterval', '45'), ('compression', 'yes'), ('compressionlevel', '9'), ('forwardx11', 'yes'), ('user', 'hg')]
    
    print(config.get('bitbucket.org','compression'))#yes
    
    
    #---------------------------------------------删,改,增(config.write(open('i.cfg', "w")))
    
    
    config.add_section('yuan')
    
    config.remove_section('topsecret.server.com')
    config.remove_option('bitbucket.org','user')
    
    config.set('bitbucket.org','k1','11111')
    
    config.write(open('i.cfg', "w"))
    configparser模块
    import configparser
      
    config = configparser.ConfigParser()
    config["DEFAULT"= {'ServerAliveInterval''45',
                          'Compression''yes',
                         'CompressionLevel''9'}
      
    config['bitbucket.org'= {}
    config['bitbucket.org']['User'= 'hg'
    config['topsecret.server.com'= {}
    topsecret = config['topsecret.server.com']
    topsecret['Host Port'= '50022'     # mutates the parser
    topsecret['ForwardX11'= 'no'  # same here
    config['DEFAULT']['ForwardX11'= 'yes'<br>
    with open('example.ini''w') as configfile:
       config.write(configfile)
      更多模块相关内容参考博客http://www.cnblogs.com/yuanchenqi/articles/6766020.html。
  • 相关阅读:
    Shell判断文件或目录是否存在
    linux使用wpa_supplicant手动配置wifi
    ubuntu更换apt源后依然搜索旧软件源下载失败问题
    在Ubuntu下解决E: 无法对目录 /var/lib/apt/lists/ 加锁的问题
    Bash中判断一个命令的输出结果是否为空
    [解决]/bin/bash^M: bad interpreter: No such file or directory
    【转】 使用 NetworkManager 命令行工具 nmcli
    【转】wpa_supplicant及wpa_cli使用方法
    [RK3288] Vendor Storage区域知识及探讨
    安装oracle 时“[INS-30014]无法检查指定的位置是否位于 CFS上”问题
  • 原文地址:https://www.cnblogs.com/Jeffding/p/7481553.html
Copyright © 2011-2022 走看看