Python實現(xiàn)基于多線程、多用戶的FTP服務器與客戶端功能完整實例
本文實例講述了Python實現(xiàn)基于多線程、多用戶的FTP服務器與客戶端功能。分享給大家供大家參考,具體如下:
項目介紹:
1. 用戶加密認證
2. 允許同時多用戶登錄
3. 每個用戶有自己的家目錄 ,且只能訪問自己的家目錄
4. 對用戶進行磁盤配額,每個用戶的可用空間不同
5. 允許用戶在ftp server上隨意切換目錄
6. 允許用戶查看當前目錄下文件
7. 允許上傳和下載文件,保證文件一致性
8. 文件傳輸過程中顯示進度條
實現(xiàn)的原理:
服務器端啟用端口監(jiān)聽,并對每一連接啟用一個線程,對用戶登陸密碼采用SHA512進行加密并進行匹配,當用戶登陸成功后,實例化FTPS,并引導客戶端進入主命令模式,
然后實現(xiàn)FTP的上傳功能、下載功能、新建目錄、刪除文件或目錄、切換目錄等實例化操作,同時對相關上傳下載進行進度條顯示,服務器端顯示下載或上傳文件的大小等
客戶端與服務器協(xié)商建立連接后,進行用戶身份登陸,登陸成功接收服務器指令,轉(zhuǎn)入命令輸入窗口,同時對put 與 get命令進行判斷,實現(xiàn)特定的上傳與下載功能
核心代碼實現(xiàn)如下:
服務器端
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登陸驗證函數(shù)
: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)
"""
#實例化加密函數(shù)
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("|")
# 調(diào)用登陸驗證函數(shù)
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":
# 登陸成功發(fā)送登陸確認信息給客戶端
conn.send(bytes("login_ack","utf8"))
# 實例化FTPserver
ftp = file_handler.FTPs(username,conn,home,size) # 登陸用戶,數(shù)據(jù)連接,工作目錄,磁盤配額
ftp.run()
break
else:
# 登陸失敗,發(fā)送給客戶端重新驗證
conn.send(bytes("登陸失??!","utf8"))
def ftp_server():
'''
啟動FTP服務器端,開啟線程監(jiān)聽
: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()
# 創(chuàng)建線程
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):
'''
初始化參數(shù)
: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) # 獲取要發(fā)送文件的大小
self.conn.send(bytes("f_ack_read","utf8"))
self.conn.recv(1000)
print("需發(fā)送文件大小:",self.fsize)
self.conn.send(bytes("fsize|%s"%self.fsize,"utf8")) # 發(fā)送文件大小及要發(fā)送準備完畢指令
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("實際發(fā)送文件大?。?,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):
'''
創(chuàng)建目錄
:return:
'''
if self.cmd_file:
try:
os.makedirs(self.cmd_file)
self.conn.send(bytes("創(chuàng)建目錄成功!","utf8"))
except Exception as ex:
self.conn.send(bytes("創(chuàng)建目錄失敗!原因:%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 ----->創(chuàng)建目錄
help----->幫助信息
q ------->退出
""","utf8"))
def run(self):
while True:
# try:
# # 接收客戶端發(fā)來的命令信息
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("系統(tǒng)異常:%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服務器,請根據(jù)提示進行操作")
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")) # 發(fā)送動作命令
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) # 獲取要發(fā)送文件的大小
self.sk.send(bytes("fsize|%s"%self.fsize,"utf8")) # 發(fā)送文件大小
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 輸出操作系統(tǒng)特定的路徑分隔符,win下為"\\",Linux下為"/"
os.linesep 輸出當前平臺使用的行終止符,win下為"\t\n",Linux下為"\n"
os.pathsep 輸出用于分割文件路徑的字符串
os.name 輸出字符串指示當前使用平臺。win->'nt'; Linux->'posix'
os.system("bash command") 運行shell命令,直接顯示
os.environ 獲取系統(tǒng)環(huán)境變量
os.path.abspath(path) 返回path規(guī)范化的絕對路徑
os.path.split(path) 將path分割成目錄和文件名二元組返回
os.path.dirname(path) 返回path的目錄。其實就是os.path.split(path)的第一個元素
os.path.basename(path) 返回path最后的文件名。如何path以/或\結(jié)尾,那么就會返回空值。即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[, ...]]) 將多個路徑組合后返回,第一個絕對路徑之前的參數(shù)將被忽略
os.path.getatime(path) 返回path所指向的文件或者目錄的最后存取時間
os.path.getmtime(path) 返回path所指向的文件或者目錄的最后修改時間
sys模塊
sys.argv 命令行參數(shù)List,第一個元素是程序本身路徑
sys.exit(n) 退出程序,正常退出時exit(0)
sys.version 獲取Python解釋程序的版本信息
sys.maxint 最大的Int值
sys.path 返回模塊的搜索路徑,初始化時使用PYTHONPATH環(huán)境變量的值
sys.platform 返回操作系統(tǒng)平臺名稱
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匹配括號內(nèi)的表達式,也表示一個組 |
| (?imx) | 正則表達式包含三種可選標志:i, m, 或 x 。只影響括號中的區(qū)域。 |
| (?-imx) | 正則表達式關閉 i, m, 或 x 可選標志。只影響括號中的區(qū)域。 |
| (?: re) | 類似 (...), 但是不表示一個組 |
| (?imx: re) | 在括號中使用i, m, 或 x 可選標志 |
| (?-imx: re) | 在括號中不使用i, m, 或 x 可選標志 |
| (?#...) | 注釋. |
| (?= re) | 前向肯定界定符。如果所含正則表達式,以 ... 表示,在當前位置成功匹配時成功,否則失敗。但一旦所含表達式已經(jīng)嘗試,匹配引擎根本沒有提高;模式的剩余部分還要嘗試界定符的右邊。 |
| (?! re) | 前向否定界定符。與肯定界定符相反;當所含表達式不能在字符串當前位置匹配時成功 |
| (?> re) | 匹配的獨立模式,省去回溯。 |
| \w | 匹配字母數(shù)字 |
| \W | 匹配非字母數(shù)字 |
| \s | 匹配任意空白字符,等價于 [\t\n\r\f]. |
| \S | 匹配任意非空字符 |
| \d | 匹配任意數(shù)字,等價于 [0-9]. |
| \D | 匹配任意非數(shù)字 |
| \A | 匹配字符串開始 |
| \Z | 匹配字符串結(jié)束,如果是存在換行,只匹配到換行前的結(jié)束字符串。c |
| \z | 匹配字符串結(jié)束 |
| \G | 匹配最后匹配完成的位置。 |
| \b | 匹配一個單詞邊界,也就是指單詞和空格間的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
| \B | 匹配非單詞邊界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
| \n, \t, 等. | 匹配一個換行符。匹配一個制表符。等 |
| \1...\9 | 匹配第n個分組的子表達式。 |
| \10 | 匹配第n個分組的子表達式,如果它經(jīng)匹配。否則指的是八進制字符碼的表達式。 |
正則表達式常用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] | 匹配中括號內(nèi)的任意一個字母 |
| [0-9] | 匹配任何數(shù)字。類似于 [0123456789] |
| [a-z] | 匹配任何小寫字母 |
| [A-Z] | 匹配任何大寫字母 |
| [a-zA-Z0-9] | 匹配任何字母及數(shù)字 |
| [^aeiou] | 除了aeiou字母以外的所有字符 |
| [^0-9] | 匹配除了數(shù)字外的字符 |
特殊字符類
| 實例 | 描述 |
|---|---|
| . | 匹配除 "\n" 之外的任何單個字符。要匹配包括 '\n' 在內(nèi)的任何字符,請使用象 '[.\n]' 的模式。 |
| \d | 匹配一個數(shù)字字符。等價于 [0-9]。 |
| \D | 匹配一個非數(shù)字字符。等價于 [^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的區(qū)別
re.match只匹配字符串的開始,如果字符串開始不符合正則表達式,則匹配失敗,函數(shù)返回None;而re.search匹配整個字符串,直到找到一個匹配。
PS:這里再為大家提供2款非常方便的正則表達式工具供大家參考使用:
JavaScript正則表達式在線測試工具:
http://tools.jb51.net/regex/javascript
正則表達式在線生成工具:
http://tools.jb51.net/regex/create_reg
更多關于Python相關內(nèi)容可查看本站專題:《Python正則表達式用法總結(jié)》、《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》、《Python入門與進階經(jīng)典教程》及《Python文件與目錄操作技巧匯總》
希望本文所述對大家Python程序設計有所幫助。
相關文章
python發(fā)送多人郵件沒有展示收件人問題的解決方法
這篇文章主要為大家詳細介紹了python發(fā)送多人郵件沒有展示收件人問題的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-06-06
Pytorch中.detach()與.data的用法小結(jié)
這篇文章主要介紹了Pytorch中.detach()與.data的用法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07
pytorch如何對image和label同時進行隨機翻轉(zhuǎn)
這篇文章主要介紹了pytorch如何對image和label同時進行隨機翻轉(zhuǎn)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09
使用py2exe在Windows下將Python程序轉(zhuǎn)為exe文件
這篇文章主要介紹了Windows下用py2exe將Python程序轉(zhuǎn)為exe文件的方法,注意py2exe只是負責文件格式的轉(zhuǎn)換,并不能將Python程序編譯為機器碼,要的朋友可以參考下2016-03-03
PyTorch中clone()、detach()及相關擴展詳解
這篇文章主要給大家介紹了關于PyTorch中clone()、detach()及相關擴展的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12

