Python实现基于多线程、多用户的FTP服务器与客户端功能完整实例


Posted in Python onAugust 18, 2017

本文实例讲述了Python实现基于多线程、多用户的FTP服务器与客户端功能。分享给大家供大家参考,具体如下:

项目介绍:

1. 用户加密认证
2. 允许同时多用户登录
3. 每个用户有自己的家目录 ,且只能访问自己的家目录
4. 对用户进行磁盘配额,每个用户的可用空间不同
5. 允许用户在ftp server上随意切换目录
6. 允许用户查看当前目录下文件
7. 允许上传和下载文件,保证文件一致性
8. 文件传输过程中显示进度条

实现的原理:

服务器端启用端口监听,并对每一连接启用一个线程,对用户登陆密码采用SHA512进行加密并进行匹配,当用户登陆成功后,实例化FTPS,并引导客户端进入主命令模式,

然后实现FTP的上传功能、下载功能、新建目录、删除文件或目录、切换目录等实例化操作,同时对相关上传下载进行进度条显示,服务器端显示下载或上传文件的大小等

客户端与服务器协商建立连接后,进行用户身份登陆,登陆成功接收服务器指令,转入命令输入窗口,同时对put 与 get命令进行判断,实现特定的上传与下载功能

核心代码实现如下:

服务器端

main.py

#!/usr/bin/env python3.5
# -*-coding:utf8-*-
import os,sys,socket,pickle
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR)
from conf import setting
from core import file_handler
from core import db_handler
import select,hashlib
import threading
def login(username,password):
  """
  FTP登陆验证函数
  :param username:
  :param password:
  :return:
  # testDict ={"username":"jjb","password":"123456","file_dir":"E:\python","file_size":500}
  # file = 'jjb.pkl'
  # fp = open(file,'wb')
  # pickle.dump(testDict,fp)
  # fp.close()
  f = open("jjb.pkl","rb")
  data = pickle.loads(f.read())
  f.close()
  print(data)
  """
  #实例化加密函数
  hash = hashlib.sha512()
  db= db_handler.handler(setting.DATABASE,username)
  if os.path.isfile(db):
    f = open(db,"rb")
    data = pickle.loads(f.read())
    f.close()
    if username == data["name"]:
      hash.update(bytes(data["password"],"utf8"))
      hash_pwd = hash.hexdigest()
      if hash_pwd == password:
        filedir = data["file_dir"]
        filesize = data["file_size"]
        return "True|%s|%s"%(filedir,filesize)
      else:
        return "False||"
    else:
      return "False||"
  else:
    return "False||"
def process(conn,addr):
  flage = "False"
  # 接收客户端连接请求信息
  info = conn.recv(1000)
  if info.decode() == "connect":
    conn.send(bytes("login","utf8"))
  # 接收用户及密码信息
  while flage =="False":
    user_check =conn.recv(8000)
    # 分割用户名及密码
    username,password = str(user_check.decode()).split("|")
    # 调用登陆验证函数
    login_ack = login(username,password)
    flage,home,size = str(login_ack).split("|")
    # print(flage,home,size)
    # print("user_input:",username,"user_pass:",password)
    if flage =="True":
      # 登陆成功发送登陆确认信息给客户端
      conn.send(bytes("login_ack","utf8"))
      # 实例化FTPserver
      ftp = file_handler.FTPs(username,conn,home,size) # 登陆用户,数据连接,工作目录,磁盘配额
      ftp.run()
      break
    else:
      # 登陆失败,发送给客户端重新验证
      conn.send(bytes("登陆失败!","utf8"))
def ftp_server():
  '''
  启动FTP服务器端,开启线程监听
  :return:
  '''
  server = socket.socket()
  server.bind((setting.IP_PORT["host"],setting.IP_PORT["port"]))
  server.listen(10)
  while True:
    r,w,e = select.select([server,], [], [], 1)
    for i,server in enumerate(r):
      conn,addr = server.accept()
      # 创建线程
      t = threading.Thread(target=process, args=(conn, addr))
      # 启动线程
      t.start()
  server.close()
def run():
  ftp_server()
if __name__ =="__main__":
  run()

file_handler.py:

#!/usr/bin/env python3.5
# -*-coding:utf8-*-
import os,sys
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR)
import re
from core import db_handler
from conf import setting
import pickle
class FTPs(object):
  '''
  ftp操作命令方法:
  '''
  def __init__(self,username,conn,home,total_size):
    '''
    初始化参数
    :param username: 操作用户名
    :param conn: sock连接
    :param home: 用户根目录
    :param total_size: 磁盘配额
    :return:
    '''
    self.username = username
    self.conn = conn
    self.root = home
    self.home = self.root
    self.total_size = int(total_size)
    self.cmd_file = None # 文件指令
    self.psize = 4096 # 文件分片
  def getdirsize(self,space):
    '''
    计算磁盘空间大小
    :return:
    '''
    self.dirsize = 0
    for root,dirs,files in os.walk(space):
      self.dirsize += (sum([os.path.getsize(os.path.join(root,name))for name in files])/1024)
    return int(self.dirsize)
  def put(self):
    '''
    上传文件
    :return:
    '''
    if self.cmd_file:
      self.user_space = int(self.getdirsize(self.root)/1024)
      # 组合接收字符串
      self.file_root = '%s\\%s'% (self.home,self.cmd_file)
      # # 获取文件名
      self.f =os.path.basename(self.file_root)
      if os.path.isdir(self.home):
        os.chdir(self.home)
      else:
        os.makedirs(self.home)
        os.chdir(self.home)
      try:
        self.conn.send(bytes("f_ack","utf8"))
        self.size = str(self.conn.recv(1024).decode()).split("|")
        if self.size[0]== "fsize":
          self.fss = int(self.size[1])
          self.f_total_size = int(self.user_space + (self.fss/1024/1024))
          if self.f_total_size < self.total_size: # 判断空间是否超额
            self.conn.send(bytes("f_ack_ready","utf8"))
            self.bsize = 0
            print("需要上传文件大小:",self.fss)
            # 打开文件
            f=open(self.f,'wb')
            while self.bsize < self.fss:
              data = self.conn.recv(self.psize)
              self.bsize += len(data)
              f.write(data)
            self.conn.send(bytes("ok","utf8"))
            print("实际已上传文件大小:",self.bsize)
          else:
            self.conn.send(bytes("上传空间不足!无法上传,你当前磁盘配额为%sM"%self.total_size,"utf8"))
      except Exception as ex:
        self.conn.send(bytes(ex,"utf8"))
    else:
      self.conn.send(bytes("请上传文件,文件不能为空","utf8"))
  def get(self):
    '''
    下载文件
    :return:
    '''
    if self.cmd_file:
      os.chdir(self.home) # 进入用户根目录
      self.file = os.getcwd()+"\\"+ self.cmd_file
      if os.path.isfile(self.file):
        f = open(self.file, 'rb')
        self.fsize = os.path.getsize(self.file) # 获取要发送文件的大小
        self.conn.send(bytes("f_ack_read","utf8"))
        self.conn.recv(1000)
        print("需发送文件大小:",self.fsize)
        self.conn.send(bytes("fsize|%s"%self.fsize,"utf8")) # 发送文件大小及要发送准备完毕指令
        if self.conn.recv(1000).decode() == "f_ack": # 接收对方是否准备就绪
          self.fsize = int(self.fsize)
          self.size = 0
          ack =""
          while self.size < self.fsize and ack !="ok":
            data = f.read(self.fsize) # 一次读取分片大小4096
            self.conn.send(data)
            self.size += len(data)
          print("实际发送文件大小:",self.size)
          ack = self.conn.recv(1000).decode() # 接收客户端是否下载完指令
          self.conn.send(bytes("成功","utf8"))
        else:
          self.conn.send(bytes("接收失败","utf8"))
      else:
        self.conn.send(bytes("文件不存在","utf8"))
    else:
      self.conn.send(bytes("请输入文件名","utf8"))
  def dir(self):
    '''
    查看文件
    :return:
    '''
    self.current_space =int(self.getdirsize(self.home))
    # 文件列表
    self.li = ""
    # 目录列表
    self.dl = ""
    try:
      os.chdir(self.home)
    except:
      os.makedirs(self.home)
      os.chdir(self.home)
    try:
      if os.listdir(os.getcwd()):
        for self.i in os.listdir(os.getcwd()):
          self.file = os.getcwd()+'\\'+self.i
          if os.path.isfile(self.file):
            # 获取文件大小
            self.fsize = int(os.path.getsize(self.file)/1024)
            if self.fsize < 1:
              self.fsize = 4
            else:
              self.fsize +=4
            self.li += '%s -rw-rw-rw- 占用大小:%skb\r\n'% (self.i,self.fsize)
          else:
            self.dl += '%s\r\n'%self.i
        self.conn.send(bytes("目录:\r\n\r\n%s 文件:\r\n%s\r\n \r\n当前目录空间大小:%skb"%(self.dl,self.li,self.current_space),"utf8"))
      else:
        self.conn.send(bytes("当前目录为:%s"%(self.home),"utf8"))
    except Exception as ex:
      self.conn.send(bytes(ex,"utf8"))
  def cd(self):
    '''
    进入目录
    :return:
    '''
    if self.cmd_file:
      os.chdir(self.home) # 先进入到工作目录
      self.dir_change = os.path.abspath(os.path.join(self.home,"%s\%s"%(self.home,self.cmd_file)))
      if self.root in self.dir_change:
        try:
          os.chdir(self.dir_change)
          self.home = self.dir_change
          self.conn.send(bytes("当前工作目录为:%s"%self.home,"utf8"))
        except:
          os.makedirs(self.dir_change)
          os.chdir(self.dir_change)
          self.home = self.dir_change
          self.conn.send(bytes("当前工作目录为:%s"%self.home,"utf8"))
      else:
          self.conn.send(bytes("当前工作目录为:%s  更改失败!"%self.home,"utf8"))
    else:
      os.chdir(self.home)
      self.conn.send(bytes("当前工作目录为:%s"%self.home,"utf8"))
  def mkd(self):
    '''
    创建目录
    :return:
    '''
    if self.cmd_file:
      try:
        os.makedirs(self.cmd_file)
        self.conn.send(bytes("创建目录成功!","utf8"))
      except Exception as ex:
        self.conn.send(bytes("创建目录失败!原因:%s"%ex,"utf8"))
    else:
      self.conn.send(bytes("请输入文件夹名!","utf8"))
  def delete(self):
    '''
    删除文件
    :return:
    '''
    os.chdir(self.home) # 进入用户根目录
    try:
      self.file = self.home+'\\'+ self.cmd_file
      if os.path.isfile(self.file):
        os.remove(self.cmd_file)
        self.conn.send(bytes("文件:%s删除成功!"%self.cmd_file,"utf8"))
      else:
        os.removedirs(self.cmd_file)
        self.conn.send(bytes("目录删除成功!","utf8"))
        os.chdir(self.root)
    except Exception:
      if os.path.isdir(self.root):
        self.conn.send(bytes("删除失败!","utf8"))
      else:
        os.makedirs(self.root)
        self.conn.send(bytes("删除失败!","utf8"))
  def help(self):
    '''
    FTP帮助信息
    :return:
    '''
    self.conn.send(bytes("""
    FTP服务器操作方法有: put------>上传文件至服务器
               get------>从服务器上下载文件
               dir------>查看服务器文件列表
               cd------->进入指定文件夹
               delete--->删除文件
               mkd ----->创建目录
               help----->帮助信息
               q ------->退出
    ""","utf8"))
  def run(self):
    while True:
      # try:
       # # 接收客户端发来的命令信息
      self.cmd = self.conn.recv(1000)
      self.cmd_action = str(self.cmd.decode())
      # 判断命令是否含有空格
      self.fg = re.search("\s","%s"%self.cmd_action)
      if self.fg:
        self.cmd_action,self.cmd_file = str(self.cmd_action).split(" ")
      else:
        self.cmd_file =None
      # print("cmd_action:",self.cmd_action,"cmd_file:",self.cmd_file)
      if hasattr(FTPs,self.cmd_action):
        func = getattr(self,self.cmd_action)
        func()
        continue
      else:
        self.conn.send(b'command is not found!')
        continue
      # except Exception as ex:
      #   print("系统异常:%s"%ex)

客户端

client.py:

#!/usr/bin/env python3.5
# -*-coding:utf8-*-
import sys,os,re
import socket,hashlib
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR)
from core import file_handler
from conf import setting
def login():
  hash = hashlib.sha512()
  while True:
    user_input = input("请输入用户名:").strip()
    pass_input = input("请输入密码:").strip()
    if len(user_input) !=0 and len(pass_input) != 0:
      hash.update(bytes(pass_input,"utf8"))
      sha_pwd = hash.hexdigest()
      user = "%s|%s"% (user_input,sha_pwd)
      return user
      break
def ftp_client():
  sk = socket.socket()
  sk.connect((setting.IP_PORT["host"],setting.IP_PORT["port"]))
  while True:
    flage = False
    sk.send(bytes("connect","utf8"))
    msg = sk.recv(100)
    print("欢迎访问FTP服务器,请根据提示进行操作")
    if msg.decode() == "login":
      while flage == False:
        login_user =login()
        username,password = str(login_user).split("|")
        sk.send(bytes(login_user,"utf8"))
        user_info = sk.recv(1000)
        if user_info.decode() == "login_ack":
          print("登陆成功!")
          flage = True
          break
        print(user_info.decode())
      while flage:
        cmd_action = input("请输入操作命令如:get fy.py or help :").strip()
        if len(cmd_action) == 0:continue
        if cmd_action == "q":
          sys.exit()
        # 判断命令是否含有空格
        fg = re.search("\s","%s"%cmd_action)
        if fg:
          cmd,cmd_file = str(cmd_action).split(" ")
          ftp = file_handler.ftpc(sk,username,cmd_action,setting.DATABASE["local"])
          if hasattr(ftp,cmd):
            func = getattr(ftp,cmd)
            func()
            continue
        else:
          cmd_file =None
        sk.send(bytes(cmd_action,"utf8"))
        rec_msg = sk.recv(8000)
        print(rec_msg.decode())
      if flage == "False":
        sk.send(bytes("connect","utf8"))
  sk.close()
def run():
  ftp_client()
if __name__ == "__main__":
  run()

file_handler.py:

#!/usr/bin/env python3.5
# -*-coding:utf8-*-
import sys,os,re
import socket
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR)
class ftpc(object):
  def __init__(self,sk,username,cmd_action,home):
    self.sk = sk
    self.username = username
    self.cmd_action = cmd_action
    self.home = home
  def put(self):
    '''
    上传文件
    :return:
    '''
    try:
      os.chdir(self.home)
    except:
      os.makedirs(self.home)
      os.chdir(self.home)
    # 判断命令是否含有空格
    fg = re.search("\s","%s"%self.cmd_action)
    if fg:
      self.cmd,self.cmd_file = str(self.cmd_action).split(" ")
      if os.path.isfile(os.getcwd()+"\\"+self.cmd_file):
        self.sk.send(bytes(self.cmd_action,"utf8")) # 发送动作命令
        rec_msg = self.sk.recv(8000)
        if rec_msg.decode() == "f_ack":
          f = open(self.cmd_file, 'rb')
          self.fsize = os.path.getsize(self.cmd_file) # 获取要发送文件的大小
          self.sk.send(bytes("fsize|%s"%self.fsize,"utf8")) # 发送文件大小
          self.ack = self.sk.recv(1000)
          if self.ack.decode() =="f_ack_ready":
            self.fsize = int(self.fsize)
            self.size = 0
            ack =""
            while self.size < self.fsize and ack !="ok":
              data = f.read(4095) # 一次读取分片大小4095
              self.sk.send(data)
              self.size += len(data)
              count = int(self.size/self.fsize*100)
              print('#'*count,"->",(count),"%")
            ack = self.sk.recv(1000).decode()
            if ack =="ok":
              print("上传成功")
            else:
              print("上传失败")
          else:
            print(self.ack.decode())
        else:
          print("上传文件失败:%s"%rec_msg.decode())
      else:
        print("上传文件失败,请输入正确的文件名!")
    else:
      print("上传文件失败,请输入正确的文件名!")
  def get(self):
    '''
    下载文件
    :return:
    '''
    try:
      os.chdir(self.home)
    except:
      os.makedirs(self.home)
      os.chdir(self.home)
    # 判断命令是否含有空格
    fg = re.search("\s","%s"%self.cmd_action)
    if fg:
      self.cmd,self.cmd_file = str(self.cmd_action).split(" ")
    else:
      self.cmd_file =None
    self.sk.send(bytes(self.cmd_action,"utf8"))
    rec_msg = self.sk.recv(8000)
    if rec_msg.decode() == "f_ack_read":
      self.rec = self.sk.send(bytes("ok","utf8"))
      self.rec_size = self.sk.recv(2048)
      self.ack_rec= str(self.rec_size.decode()).split("|")
      self.sk.send(bytes("f_ack","utf8"))
      self.ack_s =int(self.ack_rec[1])
      print(self.ack_s)
      self.re_s = 0
      f = open(self.cmd_file,"wb")
      while self.re_s < self.ack_s:
        xx = self.re_s/self.ack_s*100
        data = self.sk.recv(4096)
        self.re_s += len(data)
        # print(data.decode("gbk"))
        f.write(data)
        count = int(xx)
        print('#'*count,"->",(count+1),"%")
      self.sk.send(bytes("ok","utf8"))
      print(self.re_s)
      self.ack_ok = self.sk.recv(1024)
      print("接收文件:%s"%self.ack_ok.decode())
    else:
      print("接收文件失败:%s"%rec_msg.decode())

如下是重要模块进行收藏:

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下为"\t\n",Linux下为"\n"
os.pathsep    输出用于分割文件路径的字符串
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所指向的文件或者目录的最后修改时间

sys模块

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

sys.stdout.write('please:')
val = sys.stdin.readline()[:-1]

re 模块

匹配格式

模式 描述
^ 匹配字符串的开头
$ 匹配字符串的末尾。
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[...] 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k
[^...] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
re* 匹配0个或多个的表达式。
re+ 匹配1个或多个的表达式。
re? 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
re{ n}
re{ n,} 精确匹配n个前面表达式。
re{ n, m} 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
a| b 匹配a或b
(re) G匹配括号内的表达式,也表示一个组
(?imx) 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。
(?-imx) 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
(?: re) 类似 (...), 但是不表示一个组
(?imx: re) 在括号中使用i, m, 或 x 可选标志
(?-imx: re) 在括号中不使用i, m, 或 x 可选标志
(?#...) 注释.
(?= re) 前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。
(?! re) 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功
(?> re) 匹配的独立模式,省去回溯。
\w 匹配字母数字
\W 匹配非字母数字
\s 匹配任意空白字符,等价于 [\t\n\r\f].
\S 匹配任意非空字符
\d 匹配任意数字,等价于 [0-9].
\D 匹配任意非数字
\A 匹配字符串开始
\Z 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。c
\z 匹配字符串结束
\G 匹配最后匹配完成的位置。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\n, \t, 等. 匹配一个换行符。匹配一个制表符。等
\1...\9 匹配第n个分组的子表达式。
\10 匹配第n个分组的子表达式,如果它经匹配。否则指的是八进制字符码的表达式。

正则表达式常用5种操作

re.match(pattern, string) # 从头匹配
re.search(pattern, string) # 匹配整个字符串,直到找到一个匹配
re.split() # 将匹配到的格式当做分割点对字符串分割成列表

>>>m = re.split("[0-9]", "alex1rain2jack3helen rachel8")
>>>print(m)

输出:['alex', 'rain', 'jack', 'helen rachel', '']

re.findall() # 找到所有要匹配的字符并返回列表格式

>>>m = re.findall("[0-9]", "alex1rain2jack3helen rachel8")
>>>print(m)

输出:['1', '2', '3', '8']

re.sub(pattern, repl, string, count,flag) # 替换匹配到的字符

m=re.sub("[0-9]","|", "alex1rain2jack3helen rachel8",count=2 )
print(m)

输出:alex|rain|jack3helen rachel8

正则表达式实例

字符匹配

实例 描述
python 匹配 "python".

字符类

实例 描述
[Pp]ython 匹配 "Python" 或 "python
rub[ye] 匹配 "ruby" 或 "rube
[aeiou] 匹配中括号内的任意一个字母
[0-9] 匹配任何数字。类似于 [0123456789]
[a-z] 匹配任何小写字母
[A-Z] 匹配任何大写字母
[a-zA-Z0-9] 匹配任何字母及数字
[^aeiou] 除了aeiou字母以外的所有字符
[^0-9] 匹配除了数字外的字符

特殊字符类

实例 描述
. 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
\W 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。

re.match与re.search的区别

re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

Python 相关文章推荐
Python如何读取MySQL数据库表数据
Mar 11 Python
Python scikit-learn 做线性回归的示例代码
Nov 01 Python
python爬虫之模拟登陆csdn的实例代码
May 18 Python
Python多线程编程之多线程加锁操作示例
Sep 06 Python
python石头剪刀布小游戏(三局两胜制)
Jan 20 Python
Python检查 云备份进程是否正常运行代码实例
Aug 22 Python
Python 生成一个从0到n个数字的列表4种方法小结
Nov 28 Python
python递归函数求n的阶乘,优缺点及递归次数设置方式
Apr 02 Python
对python中arange()和linspace()的区别说明
May 03 Python
浅析Python 多行匹配模式
Jul 24 Python
利用python实现后端写网页(flask框架)
Feb 28 Python
Python实现信息轰炸工具(再也不怕说不过别人了)
Jun 11 Python
对python中return和print的一些理解
Aug 18 #Python
浅析Python中return和finally共同挖的坑
Aug 18 #Python
Python多进程multiprocessing用法实例分析
Aug 18 #Python
利用python求相邻数的方法示例
Aug 18 #Python
Python3.5编程实现修改IIS WEB.CONFIG的方法示例
Aug 18 #Python
Python 中 Virtualenv 和 pip 的简单用法详解
Aug 18 #Python
Python3编程实现获取阿里云ECS实例及监控的方法
Aug 18 #Python
You might like
php 表单数据的获取代码
2009/03/10 PHP
php反弹shell实现代码
2009/04/22 PHP
PHP 面向对象详解
2012/09/13 PHP
php获取数组中重复数据的两种方法
2013/06/28 PHP
PHP开发框架kohana3 自定义路由设置示例
2014/07/14 PHP
PHP中上传多个文件的表单设计例子
2014/11/19 PHP
PHP中array_keys和array_unique函数源码的分析
2016/02/26 PHP
window.event快达到全浏览器支持了,以后使用就方便了
2011/11/30 Javascript
基于jquery的代码显示区域自动拉长效果
2011/12/07 Javascript
javascript函数定义的几种区别小结
2014/01/06 Javascript
两个多选select(multiple左右)添加、删除选项和取值实例
2014/05/12 Javascript
javascript实现浏览器窗口传递参数的方法
2014/09/03 Javascript
javascript设计模式之module(模块)模式
2016/08/19 Javascript
基于javascript实现最简单选项卡切换
2017/02/01 Javascript
Javascript中数组去重与拍平的方法示例
2017/02/03 Javascript
使用jQuery实现两个div中按钮互换位置的实例代码
2017/09/21 jQuery
axios全局请求参数设置,请求及返回拦截器的方法
2018/03/05 Javascript
Node错误处理笔记之挖坑系列教程
2018/06/05 Javascript
Vue一个案例引发的递归组件的使用详解
2018/11/15 Javascript
Ant Design Vue 添加区分中英文的长度校验功能
2020/01/21 Javascript
[00:43]2016完美“圣”典风云人物:单车宣传片
2016/12/02 DOTA
python代码实现ID3决策树算法
2017/12/20 Python
Python回文字符串及回文数字判定功能示例
2018/03/20 Python
Linux下升级安装python3.8并配置pip及yum的教程
2020/01/02 Python
Pytorch Tensor 输出为txt和mat格式方式
2020/01/03 Python
Python文件操作方法详解
2020/02/09 Python
Selenium+BeautifulSoup+json获取Script标签内的json数据
2020/12/07 Python
Wilson体育用品官网:美国著名运动器材品牌
2019/05/12 全球购物
英国羊皮鞋类领先品牌:Just Sheepskin
2019/12/12 全球购物
优秀大学生职业生涯规划书
2014/02/27 职场文书
十八大演讲稿
2014/05/22 职场文书
公司运动会策划方案
2014/05/25 职场文书
党风廉正建设个人工作总结
2015/03/06 职场文书
2015年社区环境卫生工作总结
2015/04/21 职场文书
2015年医院护理部工作总结
2015/04/23 职场文书
Apache POI操作批量导入MySQL数据库
2022/06/21 Servers