Python字符串相關操作及正則表達式學習教程
一、字符串的常用方法
| 類別 | 方法 | 描述 | 代碼示例 |
|---|---|---|---|
| 大小寫 | .upper() | 轉大寫 | "hi".upper() → "HI" |
.lower() | 轉小寫 | "HI".lower() → "hi" | |
.title() | 單詞首字母大寫 | "hi there".title() → "Hi There" | |
| 搜索替換 | .find(x) | 查找子串位置 | "hi".find("i") → 1 |
.count(x) | 統(tǒng)計出現(xiàn)次數(shù) | "hello".count("l") → 2 | |
.replace(a,b) | 替換子串 | "ha".replace("a","i") → "hi" | |
| 字符串判斷 | .startswith(x) | 檢查前綴 | "hi".startswith("h") → True |
.endswith(x) | 檢查后綴 | "hi".endswith("i") → True | |
.isalpha() | 是否全字母 | "hi".isalpha() → True | |
| 去除空白 | .strip() | 移除首尾空白 | " hi ".strip() → "hi" |
| 分割連接 | .split() | 分割為列表 | "a b".split() → ['a','b'] |
.join(list) | 連接列表 | "-".join(['a','b']) → "a-b" | |
| 對齊 | .center(n) | 居中對齊 | "hi".center(4) → " hi " |
1. 大小寫轉換
# 原始字符串 original_str = "hello World, Python3!" # .upper(): 將所有字符轉換為大寫 print(original_str.upper()) # 輸出: HELLO WORLD, PYTHON3! # .lower(): 將所有字符轉換為小寫 print(original_str.lower()) # 輸出: hello world, python3! # .capitalize(): 將字符串首字母大寫,其余小寫 print(original_str.capitalize()) # 輸出: Hello world, python3! # .title(): 將每個單詞的首字母大寫 print(original_str.title()) # 輸出: Hello World, Python3! # .swapcase(): 交換大小寫 print(original_str.swapcase()) # 輸出: HELLO wORLD, pYTHON3!
2. 搜索與替換
text = "I love Python. Python is awesome."
# .find(sub): 返回子串首次出現(xiàn)的索引,未找到返回-1
print(text.find("Python")) # 輸出: 7
print(text.find("Java")) # 輸出: -1
# .index(sub): 與find類似,但未找到時會報錯
print(text.index("Python")) # 輸出: 7
# print(text.index("Java")) # 這會引發(fā)ValueError異常
# .count(sub): 統(tǒng)計子串出現(xiàn)次數(shù)
print(text.count("Python")) # 輸出: 2
# .replace(old, new): 替換子串
print(text.replace("Python", "Java")) # 輸出: I love Java. Java is awesome.
注意:慎重使用
index()方法,如果沒有找到對應的字符串會報錯"ValueError"。
3. 字符串判斷
# .startswith(prefix): 檢查是否以指定前綴開頭
filename = "document.pdf"
print(filename.startswith("doc")) # 輸出: True
print(filename.startswith("xls")) # 輸出: False
# .endswith(suffix): 檢查是否以指定后綴結尾
print(filename.endswith(".pdf")) # 輸出: True
print(filename.endswith(".docx")) # 輸出: False
# .isalpha(): 是否全是字母(中文也被認為是字母)
print("Hello".isalpha()) # 輸出: True
print("Hello123".isalpha()) # 輸出: False
print("你好".isalpha()) # 輸出: True
# .isdigit(): 是否全是數(shù)字
print("123".isdigit()) # 輸出: True
print("12.3".isdigit()) # 輸出: False
# .isalnum(): 是否全是字母或數(shù)字
print("Hello123".isalnum()) # 輸出: True
print("Hello 123".isalnum()) # 輸出: False(包含空格)
# .isspace(): 是否全是空白字符
print(" ".isspace()) # 輸出: True
print(" x ".isspace()) # 輸出: False
注意:
isalpha方法是判斷全是字母的方法,但是中文也會被識別為字母。
5. 分割與連接
# .split(sep): 使用分隔符分割字符串
csv_data = "apple,banana,orange,grape"
print(csv_data.split(",")) # 輸出: ['apple', 'banana', 'orange', 'grape']
sentence = "I love programming"
print(sentence.split()) # 輸出: ['I', 'love', 'programming'](默認按空格分割)
# .join(iterable): 將序列中的元素用指定字符串連接
fruits = ['apple', 'banana', 'orange']
print("-".join(fruits)) # 輸出: apple-banana-orange
print(" ".join(["Hello", "World"])) # 輸出: Hello World
6. 對齊與格式化
對齊
text = "Python"
# .center(width): 居中對齊
print(f"'{text.center(10)}'") # 輸出: ' Python '
# .ljust(width): 左對齊
print(f"'{text.ljust(10)}'") # 輸出: 'Python '
# .rjust(width): 右對齊
print(f"'{text.rjust(10)}'") # 輸出: ' Python'
# 可以指定填充字符
print(f"'{text.center(10, '*')}'") # 輸出: '**Python**'
格式化
?? 格式化分為三種方式:
- 占位符(不推薦)
- f-string
- format
name = "Alice"
age = 25
# f-string (Python 3.6+ 推薦)
message = f"我叫{name},今年{age}歲"
print(message) # 我叫Alice,今年25歲
# 表達式和格式化
price = 15.5
print(f"價格: {price:.2f}元") # 價格: 15.50元
print(f"明年年齡: {age + 1}") # 明年年齡: 26
# format()方法
template = "我叫{},今年{}歲".format(name, age)
named_template = "我叫{name},今年{age}歲".format(name=name, age=age)
# %操作符 (傳統(tǒng)方式,不推薦新項目使用)
old_style = "我叫%s,今年%d歲" % (name, age)
format詳細解釋
Python的format()方法通過格式說明符({}中的:后面部分)提供精細的格式控制。格式說明符的基本語法為:{[字段名]!轉換字符:格式說明},其中格式說明又包含[[填充]對齊][符號][#][0][寬度][分組選項][.精度][類型]。
基本用法回顧
# 位置參數(shù)
print("{} {}".format("Hello", "World")) # 輸出: Hello World
# 關鍵字參數(shù)
print("{name} is {age} years old".format(name="Alice", age=25))
# 混合使用
print("{0} scored {score} points".format("Bob", score=95))
# 訪問屬性或索引
data = {"x": 10, "y": 20}
print("x={0[x]}, y={0[y]}".format(data))
對齊與填充
text = "Python"
# 左對齊 <,默認填充空格
print("|{:<10}|".format(text)) # 輸出: |Python |
# 右對齊 >
print("|{:>10}|".format(text)) # 輸出: | Python|
# 居中對齊 ^
print("|{:^10}|".format(text)) # 輸出: | Python |
# 指定填充字符(必須在對齊符號前)
print("|{:*^10}|".format(text)) # 輸出: |**Python**|
print("|{:=^10}|".format(text)) # 輸出: |==Python==|
數(shù)字格式:符號控制
num = 42
neg_num = -42
# +: 總是顯示符號(正數(shù)顯示+,負數(shù)顯示-)
print("{:+} {:+}".format(num, neg_num)) # 輸出: +42 -42
# -: 僅負數(shù)顯示符號(默認行為)
print("{:-} {:-}".format(num, neg_num)) # 輸出: 42 -42
# 空格: 正數(shù)前加空格,負數(shù)前加負號
print("|{: }| |{: }|".format(num, neg_num)) # 輸出: | 42| |-42|
數(shù)字格式:寬度、精度與千位分隔符
price = 1234.5678
# 寬度和精度控制
print("|{:10.2f}|".format(price)) # 輸出: | 1234.57|
# 千位分隔符 ,
print("{:,}".format(1000000)) # 輸出: 1,000,000
print("{:,.2f}".format(1234567.89)) # 輸出: 1,234,567.89
# 組合使用
print("|{:>15,.2f}|".format(price)) # 輸出: | 1,234.57|
數(shù)字格式:進制轉換
number = 255
# 十進制 d
print("{:d}".format(number)) # 輸出: 255
# 二進制 b(# 顯示前綴)
print("{:#b}".format(number)) # 輸出: 0b11111111
# 八進制 o
print("{:#o}".format(number)) # 輸出: 0o377
# 十六進制 x/X(大小寫控制十六進制字符)
print("{:#x}".format(number)) # 輸出: 0xff
print("{:#X}".format(number)) # 輸出: 0xFF
精度控制與類型指定
import math
# 浮點數(shù)精度控制
pi = math.pi
print("{:.2f}".format(pi)) # 輸出: 3.14
print("{:.4f}".format(pi)) # 輸出: 3.1416
# 百分比顯示 %
print("{:.1%}".format(0.256)) # 輸出: 25.6%
# 科學計數(shù)法 e/E
print("{:.2e}".format(123456789)) # 輸出: 1.23e+08
print("{:.2E}".format(0.000012345)) # 輸出: 1.23E-05
# 一般格式 g/G(在浮點和科學計數(shù)法間自動選擇)
print("{:.5g}".format(123.456789)) # 輸出: 123.46
print("{:.5g}".format(123456789)) # 輸出: 1.2346e+08
零填充與特殊格式
# 零填充(相當于右對齊并用0填充)
print("{:05d}".format(42)) # 輸出: 00042
print("{:08.2f}".format(3.14)) # 輸出: 00003.14
# 字符串截斷
text = "Hello World"
print("{:.5}".format(text)) # 輸出: Hello
f-string擴展,語法基本一致(不可忽視!?。。?/strong>
name = "Alice"
age = 25
price = 19.99
# 基本使用
print(f"Name: {name:>10}, Age: {age:03d}")
# 數(shù)字格式化
print(f"Price: ${price:.2f}")
print(f"Percentage: {0.256:.1%}")
# 嵌套表達式
print(f"Result: {age * price:,.2f}")
格式說明符完整語法表
| 類別 | 組件 | 符號 | 作用 | 示例 |
|---|---|---|---|---|
| 填充 | 填充 | 任意字符 | 填充字符(需與對齊符配合) | {:*<10} |
| 對齊 | 對齊 | < | 左對齊 | {:<10} |
> | 右對齊 | {:>10} | ||
^ | 居中對齊 | {:^10} | ||
= | 數(shù)字符號后填充 | {:=10} | ||
| 符號 | 符號 | + | 總是顯示符號 | {:+} |
- | 僅負數(shù)顯示符號 | {:-} | ||
| 空格 | 正數(shù)前加空格 | {: } | ||
| 特殊 | 特殊 | # | 顯示進制前綴 | {:#x} |
0 | 零填充 | {:05} | ||
| 寬度 | 寬度 | 數(shù)字 | 最小字段寬度 | {:10} |
| 分組 | 分組 | , | 千位分隔符 | {:,} |
| 精度 | 精度 | .數(shù)字 | 浮點數(shù)精度 | {:.2f} |
| 類型 | 類型 | s | 字符串 | {:s} |
d | 十進制整數(shù) | {:d} | ||
b | 二進制 | {:b} | ||
o | 八進制 | {:o} | ||
x/X | 十六進制 | {:x} | ||
f/F | 浮點數(shù) | {:.2f} | ||
e/E | 科學計數(shù)法 | {:.2e} | ||
g/G | 通用格式 | {:.5g} | ||
% | 百分比 | {:.1%} |
二、字符串的編碼和解碼、常用的加密解密基礎
1. 編碼和解碼
1.1. 基本使用
# 字符串在Python內(nèi)部以Unicode形式存儲
text = "你好,世界!Hello World! ??"
print(f"原始字符串: {text}")
print(f"字符串類型: {type(text)}")
# 編碼:字符串 → 字節(jié)序列
# UTF-8編碼(最常用,兼容ASCII)
utf8_bytes = text.encode('utf-8')
print(f"UTF-8編碼: {utf8_bytes}")
print(f"編碼后類型: {type(utf8_bytes)}")
# GBK編碼(中文環(huán)境常用)
gbk_bytes = text.encode('gbk')
print(f"GBK編碼: {gbk_bytes}")
# 解碼:字節(jié)序列 → 字符串
# UTF-8解碼
decoded_utf8 = utf8_bytes.decode('utf-8')
print(f"UTF-8解碼: {decoded_utf8}")
# GBK解碼
decoded_gbk = gbk_bytes.decode('gbk')
print(f"GBK解碼: {decoded_gbk}")
# 編碼解碼不匹配會導致亂碼或錯誤
try:
wrong_decode = utf8_bytes.decode('gbk')
print(f"錯誤解碼嘗試: {wrong_decode}")
except UnicodeDecodeError as e:
print(f"解碼錯誤: {e}")
1.2. 常用編碼方式對比
def compare_encodings(text, encodings=None):
"""比較不同編碼方式的結果"""
if encodings is None:
encodings = ['utf-8', 'gbk', 'ascii', 'latin-1']
print(f"\n原始文本: {text}")
for encoding in encodings:
try:
encoded = text.encode(encoding)
decoded = encoded.decode(encoding)
print(f"{encoding:>8}: 字節(jié)數(shù)={len(encoded):2d}, 可逆={text == decoded}")
except Exception as e:
print(f"{encoding:>8}: 錯誤 - {e}")
# 測試不同文本
compare_encodings("Hello World")
compare_encodings("你好世界")
compare_encodings("Special chars: ?, ?, 日本")
D:\env\pycharm\pythonProject\demo1\demo2.py
原始文本: Hello World
utf-8: 字節(jié)數(shù)=11, 可逆=True
gbk: 字節(jié)數(shù)=11, 可逆=True
ascii: 字節(jié)數(shù)=11, 可逆=True
latin-1: 字節(jié)數(shù)=11, 可逆=True
原始文本: 你好世界
utf-8: 字節(jié)數(shù)=12, 可逆=True
gbk: 字節(jié)數(shù)= 8, 可逆=True
ascii: 錯誤 - 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
latin-1: 錯誤 - 'latin-1' codec can't encode characters in position 0-3: ordinal not in range(256)
原始文本: Special chars: ?, ?, 日本
utf-8: 字節(jié)數(shù)=29, 可逆=True
gbk: 錯誤 - 'gbk' codec can't encode character '\xf1' in position 15: illegal multibyte sequence
ascii: 錯誤 - 'ascii' codec can't encode character '\xf1' in position 15: ordinal not in range(128)
latin-1: 錯誤 - 'latin-1' codec can't encode characters in position 21-22: ordinal not in range(256)
Process finished with exit code 0
上面的例子是用來比較不同編碼方式對不同字符的支持程序,經(jīng)過對比我們可以得出以下的結論。
UTF-8是最通用的編碼,支持所有字符集
GBK對中文更高效但兼容性有限
ASCII/Latin-1適用范圍有限,只能處理有限字符集
1.3. 基于chardet庫的編碼解碼最佳示例
import chardet
# 需要安裝: pip install chardet
# 直接在控制臺使用該命令安裝即可
def improved_smart_decode(byte_data):
detection = chardet.detect(byte_data)
encoding = detection['encoding']
confidence = detection['confidence']
print(f"檢測結果: {encoding}, 置信度: {confidence:.2f}")
# 添加置信度閾值
if encoding and confidence > 0.6:
try:
return byte_data.decode(encoding)
except UnicodeDecodeError:
pass
print(f"檢測結果不可信,通常小于0.3視為完全不符合,小于0.6可信但不準確,當前置信度為:{confidence:^6.2f}不可信")
print("基于常見的編碼方式進行解碼...")
# 回退方案:嘗試常見編碼
for enc in ['utf-8', 'gbk', 'gb2312', 'latin-1']:
try:
new_result = byte_data.decode(enc)
print(f"正確的編碼方式為:{enc}")
return new_result
except UnicodeDecodeError:
continue
return byte_data.decode('utf-8', errors='replace')
# 測試智能解碼
test_bytes = "你好世界".encode('gbk')
result = improved_smart_decode(test_bytes)
print(f"智能解碼結果: {result}")
輸出結果為:
D:\env\pycharm\pythonProject\demo1\demo2.py 檢測結果: TIS-620, 置信度: 0.27 檢測結果不可信,通常小于0.3視為完全不符合,小于0.6可信但不準確,當前置信度為: 0.27 不可信 基于常見的編碼方式進行解碼... 正確的編碼方式為:gbk 智能解碼結果: 你好世界 Process finished with exit code 0
2. 加密解密算法擴展——重點!??!
2.1 哈希算法(不可逆)
import hashlib
import hmac
def demo_hash_functions():
"""演示常用哈希函數(shù)"""
data = "Hello World! 你好世界!"
print(f"原始數(shù)據(jù): {data}")
# MD5 (128位,已不推薦用于安全用途,但仍用于校驗)
md5_hash = hashlib.md5(data.encode()).hexdigest()
print(f"MD5: {md5_hash}")
# SHA-256 (更安全)
sha256_hash = hashlib.sha256(data.encode()).hexdigest()
print(f"SHA-256: {sha256_hash}")
# 加鹽哈希 (更安全)
salt = "random_salt_123"
salted_hash = hashlib.sha256((data + salt).encode()).hexdigest()
print(f"加鹽SHA-256: {salted_hash}")
# HMAC (密鑰相關的哈希)
key = "secret_key"
hmac_hash = hmac.new(key.encode(), data.encode(), hashlib.sha256).hexdigest()
print(f"HMAC-SHA256: {hmac_hash}")
demo_hash_functions()
?? 注意閱讀上面的代碼示例,考慮以下幾個問題:
- 哈希算法到底是什么?哈希算法的用途有哪些?
- 為什么MD5哈希算法不推薦安全校驗了?
- 其余的加鹽哈希、SHA-256、HMAC有什么用途,使用場景是什么?
哈希算法到底有什么作用?
哈希函數(shù)(常被不嚴謹?shù)胤Q為Hash加密,但其本質(zhì)是單向的,并非加密)的核心作用是為任意數(shù)據(jù)生成一個唯一的、固定長度的“數(shù)字指紋”。其主要用途包括:
- 數(shù)據(jù)完整性驗證
- 如上文所述,通過對比哈希值來確認數(shù)據(jù)在傳輸或存儲過程中是否被改變。
- 安全存儲密碼
- 系統(tǒng)不應明文存儲用戶密碼。當用戶注冊時,系統(tǒng)對密碼進行哈希并將哈希值存入數(shù)據(jù)庫。登錄時,對用戶輸入的密碼再次進行同樣的哈希操作,然后與數(shù)據(jù)庫中的哈希值對比。即使數(shù)據(jù)庫泄露,攻擊者看到的也只是哈希值,很難反推出原始密碼。
- 這一點深有體會,目前我們公司的產(chǎn)品服務對象多為國企和事業(yè)單位,近些年來不斷要求對數(shù)據(jù)進行加密存儲,許多新客戶也要求大部分的數(shù)據(jù)都要用密文存儲,這導致間接的損失了一些客戶。
- 數(shù)字簽名與證書
- 對消息或文件進行哈希,然后對生成的哈希值進行加密(即簽名),效率遠高于加密整個文件。接收者可以驗證哈希值來確認文件的真實性和完整性。
- 數(shù)據(jù)唯一性標識
- 例如,Git使用SHA-1哈希來唯一標識每一次提交和每一個文件。區(qū)塊鏈使用哈希來鏈接每一個區(qū)塊,確保其不可篡改性。
- 消息認證碼(HMAC)
- 如代碼中所示,HMAC結合一個密鑰和消息進行哈希。用于驗證消息不僅完整,而且確實來自擁有相同密鑰的發(fā)送方。
為什么MD5不安全了,但可以用于校驗?
MD5為何不安全(針對密碼/防篡改):
- 碰撞攻擊:MD5的核心漏洞是能夠被輕易地找到“碰撞”,即兩個不同的輸入產(chǎn)生相同的哈希值。攻擊者可以制造一個和原文件MD5值相同的惡意文件。
- 計算速度過快:對于哈希函數(shù)來說,速度快在安全領域是缺點。這使得攻擊者可以用“BL破解”(嘗試所有可能組合)或“彩虹表”(預計算的哈希值數(shù)據(jù)庫)的方式快速反推原始數(shù)據(jù)。
- 不再抗碰撞:在現(xiàn)代計算機上,可以在可接受的時間內(nèi)故意制造出MD5碰撞,這使得它無法用于驗證數(shù)據(jù)的唯一性和真實性(即無法保證數(shù)據(jù)未被惡意替換)。
為何仍可用于校驗(針對非惡意錯誤)
數(shù)據(jù)完整性校驗:在非安全敏感的場景下,如軟件下載、文件傳輸,MD5仍然可以用來檢查非惡意的數(shù)據(jù)損壞。例如,下載一個文件后計算MD5,與官網(wǎng)提供的MD5對比。如果不同,說明文件可能在網(wǎng)絡傳輸過程中出現(xiàn)了比特錯誤。它的計算速度反而成了優(yōu)點。
場景假設不同:在這種使用場景中,我們假設錯誤是隨機發(fā)生的(如網(wǎng)絡丟包),而不是有一個主動的攻擊者故意制造一個具有相同MD5值的惡意文件。對于隨機錯誤,MD5的校驗能力依然是足夠的。
其他幾種加密方式的區(qū)別與使用場景
SHA-256
- 是什么:SHA-2家族的一員,生成256位(32字節(jié))的哈希值。
- 與MD5區(qū)別:
- 更安全:目前沒有公開的有效碰撞攻擊方法。
- 更長的哈希值:256位 vs MD5的128位,大大增加了BL破解的難度。
- 更慢:計算速度比MD5慢,這在安全上是優(yōu)點。
- 使用場景:
- 替代MD5的所有安全場景:數(shù)字證書、SSL/TLS、區(qū)塊鏈(比特幣)、軟件完整性校驗等。
- 是目前廣泛推薦使用的通用安全哈希算法。
加鹽哈希(Salted Hash)
- 是什么:在原始數(shù)據(jù)(如密碼)后面拼接一個隨機字符串(鹽)后再進行哈希。
- 與普通SHA-256區(qū)別:
- 防御彩虹表攻擊:即使兩個用戶的密碼相同,由于他們的“鹽”不同,最終的哈希值也完全不同。攻擊者無法使用一個預先計算好的彩虹表來批量破解密碼。
- 強制個性化:即使攻擊者破解了其中一個密碼,也無法推知其他使用相同密碼的賬戶。
- 使用場景:
- 密碼存儲的首選方法?,F(xiàn)代最佳實踐是使用專門的密碼哈希函數(shù)(如bcrypt, argon2),它們內(nèi)置了加鹽并且計算速度可調(diào)(故意慢),但“SHA-256 + 隨機鹽”的原理是相同的。
HMAC(密鑰散列消息認證碼)
- 是什么:一種使用加密哈希函數(shù)(如SHA-256)和一個密鑰來驗證消息完整性和真實性的技術。
- 與普通/加鹽哈希的根本區(qū)別:
- 需要密鑰:HMAC的計算過程依賴于一個雙方共享的密鑰。沒有密鑰,就無法計算出正確的HMAC值。
- 目標不同:普通哈希驗證“數(shù)據(jù)是否被更改”,HMAC驗證“數(shù)據(jù)是否被更改,且是否來自合法的、擁有密鑰的發(fā)送方”。
- 使用場景:
- API請求認證:客戶端和服務器共享一個密鑰,客戶端在發(fā)送請求時計算請求內(nèi)容的HMAC,服務器收到后使用相同密鑰驗證HMAC,從而認證客戶端身份并確保請求未被篡改。
- JWT(JSON Web Tokens):可以使用HMAC進行簽名。
- 安全通信協(xié)議:在SSL/TLS等協(xié)議中也有應用。
不同哈希加密方式總結和建議
| 方法 | 核心特點 | 安全性 | 主要使用場景 |
|---|---|---|---|
| MD5 | 速度快,哈希值短 | 不安全 | 非惡意場景的數(shù)據(jù)完整性校驗(如文件下載) |
| SHA-256 | 安全性高,通用性強 | 安全 | 數(shù)字簽名、證書、區(qū)塊鏈、替代MD5的校驗 |
| 加鹽哈希 | 密碼+隨機鹽,防彩虹表 | 安全 | 密碼存儲(但更推薦bcrypt等) |
| HMAC | 消息+密鑰,驗證來源和完整性 | 安全 | API認證、消息防篡改與身份驗證 |
2.2 對稱加密(AES)
核心模式:“非對稱加密握手 + 對稱加密通話”。幾乎所有的現(xiàn)代安全通信協(xié)議都遵循這一高效且安全的模式。
宏觀角度分析對稱加密的使用場景
1. 數(shù)據(jù)存儲加密(全盤加密/文件加密)
?? 這是最直接的應用場景,目的是保護靜態(tài)數(shù)據(jù)。
全盤加密:像 Windows 的 BitLocker、macOS 的 FileVault、Linux 的 LUKS 都使用 AES 等對稱加密算法對整個硬盤驅動器進行加密。當系統(tǒng)啟動時,通過密碼、TPM 芯片或 USB 密鑰來解鎖加密密鑰,之后的所有讀寫操作在后臺自動加解密,用戶無感知。
文件/文件夾加密:對單個文件或壓縮包進行加密后存儲或傳輸。例如,使用 7-Zip 創(chuàng)建加密的壓縮包,或者使用 EFS(加密文件系統(tǒng))加密特定文件。
2. 安全網(wǎng)絡通信(TLS/SSL, VPN)
?? 雖然 TLS/SSL 使用非對稱加密來初始握手和身份驗證,但一旦建立安全連接,所有后續(xù)數(shù)據(jù)的傳輸都使用對稱加密。
HTTPS 網(wǎng)站:當你訪問一個 HTTPS 網(wǎng)站時,瀏覽器和服務器在握手階段協(xié)商出一個唯一的“會話密鑰”。之后,你們之間傳輸?shù)乃芯W(wǎng)頁內(nèi)容、Cookie、個人信息都使用這個對稱會話密鑰進行加密,確保傳輸過程中不被竊聽和篡改。這也是為什么要單獨購買https流量的主要原因之一,會讓你的網(wǎng)頁更加安全。
虛擬專用網(wǎng)絡:VPN(如 IPsec, OpenVPN)同樣使用對稱加密來加密你的所有網(wǎng)絡流量,形成一個安全的“隧道”,保護你的上網(wǎng)行為不被本地網(wǎng)絡提供商或黑客窺探。
3. 數(shù)據(jù)庫加密
?? 為了保護數(shù)據(jù)庫中存儲的敏感信息(如用戶密碼哈希、個人信息、金融數(shù)據(jù)),可以使用對稱加密。
- 列級加密:對數(shù)據(jù)庫中特定的敏感列(如“信用卡號”列)進行加密。應用程序在寫入數(shù)據(jù)前加密,在讀取數(shù)據(jù)后解密。這樣,即使數(shù)據(jù)庫文件被拖庫,攻擊者沒有密鑰也無法讀取明文信息。
4. 消息應用與即時通訊
?? 像 WhatsApp、Signal、Telegram(秘密聊天)等應用使用端到端加密。其原理是:
- 使用非對稱加密協(xié)商或交換一個對稱密鑰。
- 使用該對稱密鑰加密所有的聊天消息、圖片、文件。
- 只有通信的雙方持有該密鑰,服務提供商本身也無法解密消息內(nèi)容。
5. 支付系統(tǒng)與金融交易
銀行卡芯片:EMV 芯片卡在交易時,與 POS 機進行動態(tài)認證,過程中使用對稱加密來保護交易數(shù)據(jù)。
支付網(wǎng)關:處理信用卡交易時,從商戶到支付網(wǎng)關的傳輸過程中,支付信息通常使用對稱加密保護。
6. 軟件與固件保護
- 數(shù)字版權管理:為了保護版權內(nèi)容(如流媒體視頻、游戲、軟件),供應商會使用對稱加密對內(nèi)容本身進行加密,只有合法的、經(jīng)過授權的播放器或客戶端才能獲取密鑰進行解密播放。
- 固件更新:設備(如路由器、智能手機)在進行固件在線升級時,下載的固件包通常是經(jīng)過對稱加密和簽名的,以防止攻擊者植入惡意固件。
Python代碼示例
注意!?。?/p>
- 下面的例子非常全面,每一步都有解釋,建議自己在Pycharm仔細閱讀。
"""
AES對稱加密解密工具類
改進版:增加詳細注釋、錯誤處理、類型提示和安全性增強
"""
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
import base64
import os
from typing import Union, Optional
class AESCipher:
"""
AES加密解密工具類
使用CBC模式,PKCS7填充,支持AES-256加密
"""
"""
__init__是Python對象創(chuàng)建后自動執(zhí)行的初始化函數(shù)
類似Java的構造函數(shù),但是Java的構造函數(shù)作用是創(chuàng)建對象
而__init__是創(chuàng)建對象之后自動執(zhí)行,Python中的構造函數(shù)是__new__
"""
"""
self是Python中實例化方法必須要有的一個參數(shù),作用類似于Java中的this
但Java中this是隱式的,而Python中是顯式的,而且必須手動申明
"""
"""
key: Optional[bytes]類似于TypeScript寫法,用于限制參數(shù)的類型,一般寫法直接key=None即可
"""
def __init__(self, key: Optional[bytes] = None):
"""
初始化AES加密器
作用:
- 設置加密密鑰
- 確定AES塊大小
參數(shù):
key: 可選的密鑰字節(jié),如果為None則自動生成32字節(jié)(256位)隨機密鑰
"""
# AES塊大小固定為16字節(jié)
self.block_size = AES.block_size # 值為16
if key:
# 驗證密鑰長度
if len(key) not in [16, 24, 32]:
raise ValueError("密鑰長度必須為16(AES-128)、24(AES-192)或32(AES-256)字節(jié)")
self.key = key
else:
# 生成32字節(jié)(256位)隨機密鑰 - AES-256
# get_random_bytes()作用:生成密碼學安全的隨機字節(jié)序列
self.key = get_random_bytes(32)
print(f"[初始化] 密鑰長度: {len(self.key)}字節(jié), 塊大小: {self.block_size}字節(jié)")
# -> str 這種寫法是用于定義函數(shù)的返回值類型為str字符串
def encrypt(self, plaintext: str) -> str:
"""
加密文本字符串
步驟:
1. 生成隨機初始化向量(IV)
2. 創(chuàng)建AES加密器
3. 對明文進行填充
4. 執(zhí)行加密
5. 組合IV和密文
6. Base64編碼返回
參數(shù):
plaintext: 要加密的原始文本
返回:
Base64編碼的加密字符串
"""
print(f"\n[加密開始] 明文: '{plaintext}'")
# 步驟1: 生成隨機初始化向量(IV)
# IV作用:確保同樣的明文每次加密結果不同,增強安全性
iv = get_random_bytes(self.block_size)
print(f"[步驟1] 生成IV: {iv.hex()} (長度: {len(iv)}字節(jié))")
# 步驟2: 創(chuàng)建AES加密器
# AES.new()作用:創(chuàng)建AES加密對象
# 參數(shù)說明:
# - self.key: 加密密鑰
# - AES.MODE_CBC: 密碼塊鏈模式
# - iv: 初始化向量
cipher = AES.new(self.key, AES.MODE_CBC, iv)
print(f"[步驟2] 創(chuàng)建CBC模式加密器完成")
# 步驟3: 將文本編碼為字節(jié)并填充
# encode()作用:將字符串轉換為UTF-8編碼的字節(jié)序列
plaintext_bytes = plaintext.encode('utf-8')
print(f"[步驟3] 文本編碼為字節(jié): {plaintext_bytes} (長度: {len(plaintext_bytes)}字節(jié))")
# pad()作用:使用PKCS7填充方案對數(shù)據(jù)進行填充,使其長度為塊大小的整數(shù)倍
# PKCS7填充原理:缺少n個字節(jié)就填充n個值為n的字節(jié)
padded_bytes = pad(plaintext_bytes, self.block_size)
print(f"[步驟3] PKCS7填充后: {padded_bytes} (長度: {len(padded_bytes)}字節(jié))")
# 步驟4: 執(zhí)行加密
# encrypt()作用:對填充后的數(shù)據(jù)進行AES加密
ciphertext = cipher.encrypt(padded_bytes)
print(f"[步驟4] 加密完成,密文: {ciphertext.hex()} (長度: {len(ciphertext)}字節(jié))")
# 步驟5: 組合IV和密文
# IV + 密文的作用:解密時需要相同的IV,一起傳輸?shù)粫孤睹荑€
encrypted_data = iv + ciphertext
print(f"[步驟5] 組合IV和密文: {encrypted_data.hex()} (總長度: {len(encrypted_data)}字節(jié))")
# 步驟6: Base64編碼
# base64.b64encode()作用:將二進制數(shù)據(jù)編碼為ASCII字符串,便于傳輸和存儲
# decode()作用:將字節(jié)轉換為字符串
result = base64.b64encode(encrypted_data).decode('utf-8')
print(f"[步驟6] Base64編碼結果: {result}")
print(f"[加密完成] 最終密文長度: {len(result)}字符")
return result
def decrypt(self, encrypted_text: str) -> str:
"""
解密文本字符串
步驟:
1. Base64解碼
2. 分離IV和密文
3. 創(chuàng)建AES解密器
4. 執(zhí)行解密
5. 去除填充
6. 解碼為字符串
參數(shù):
encrypted_text: Base64編碼的加密字符串
返回:
解密后的原始文本字符串
"""
print(f"\n[解密開始] 密文: {encrypted_text[:50]}...")
try:
# 步驟1: Base64解碼
# base64.b64decode()作用:將Base64字符串解碼回二進制數(shù)據(jù)
encrypted_data = base64.b64decode(encrypted_text)
print(f"[步驟1] Base64解碼后數(shù)據(jù)長度: {len(encrypted_data)}字節(jié)")
# 步驟2: 分離IV和密文
# IV是前16字節(jié),剩余部分是密文
iv = encrypted_data[:self.block_size]
ciphertext = encrypted_data[self.block_size:]
print(f"[步驟2] 分離IV: {iv.hex()} (長度: {len(iv)}字節(jié))")
print(f"[步驟2] 分離密文: {ciphertext.hex()[:50]}... (長度: {len(ciphertext)}字節(jié))")
# 步驟3: 創(chuàng)建AES解密器
# 使用相同的密鑰和IV創(chuàng)建解密器
cipher = AES.new(self.key, AES.MODE_CBC, iv)
print(f"[步驟3] 創(chuàng)建CBC模式解密器完成")
# 步驟4: 執(zhí)行解密
# decrypt()作用:對密文進行AES解密
decrypted_padded = cipher.decrypt(ciphertext)
print(f"[步驟4] 解密后數(shù)據(jù)(含填充): {decrypted_padded} (長度: {len(decrypted_padded)}字節(jié))")
# 步驟5: 去除填充
# unpad()作用:移除PKCS7填充,恢復原始數(shù)據(jù)
decrypted_bytes = unpad(decrypted_padded, self.block_size)
print(f"[步驟5] 去除填充后: {decrypted_bytes} (長度: {len(decrypted_bytes)}字節(jié))")
# 步驟6: 解碼為字符串
# decode()作用:將UTF-8字節(jié)序列轉換回字符串
result = decrypted_bytes.decode('utf-8')
print(f"[步驟6] 解碼為字符串: '{result}'")
print(f"[解密完成] 成功恢復明文")
return result
except Exception as e:
error_msg = f"解密失敗: {e}"
print(f"[錯誤] {error_msg}")
raise ValueError(error_msg)
def get_key_base64(self) -> str:
"""
獲取Base64編碼的密鑰(用于安全存儲)
作用:
- 將二進制密鑰轉換為可安全存儲的字符串格式
"""
return base64.b64encode(self.key).decode('utf-8')
def set_key_from_base64(self, key_b64: str) -> None:
"""
從Base64字符串設置密鑰
作用:
- 從存儲的Base64字符串恢復密鑰
"""
self.key = base64.b64decode(key_b64)
def demo_aes_encryption():
"""
演示AES加密解密的完整流程
"""
print("\n" + "=" * 60)
print("AES對稱加密完整演示")
print("=" * 60)
# 創(chuàng)建加密器實例
print("1. 創(chuàng)建AES加密器...")
cipher = AESCipher()
# 測試數(shù)據(jù)
test_cases = [
"這是一段需要加密的敏感信息!",
"Hello, World!",
"短文本",
"Very long text " * 10 # 測試長文本
]
# enumerate是內(nèi)置函數(shù),用于在循環(huán)時同時獲取索引和元素值,第二個參數(shù)是定義開始索引的,表示i的起始值
# 但這不代表我們可以用test_cases[i]取數(shù),這只是內(nèi)置函數(shù)給我們自動將i進行計算而已
for i, original_text in enumerate(test_cases, 1):
print(f"\n{'#' * 40}")
print(f"測試案例 {i}: '{original_text[:30]}{'...' if len(original_text) > 30 else ''}'")
print(f"{'#' * 40}")
try:
# 加密演示
print(f"原始文本長度: {len(original_text)}字符")
encrypted = cipher.encrypt(original_text)
print(f"? 加密成功")
# 解密演示
decrypted = cipher.decrypt(encrypted)
print(f"? 解密成功")
# 驗證結果
if original_text == decrypted:
print(f"? 驗證通過: 原始文本與解密文本一致")
else:
print(f"? 驗證失敗: 文本不一致")
except Exception as e:
print(f"? 加解密過程出錯: {e}")
# 密鑰管理演示
print(f"\n{'#' * 40}")
print("密鑰管理演示")
print(f"{'#' * 40}")
# 保存密鑰
saved_key = cipher.get_key_base64()
print(f"Base64編碼密鑰: {saved_key}")
# 使用保存的密鑰創(chuàng)建新加密器
print("\n使用保存的密鑰創(chuàng)建新加密器...")
new_cipher = AESCipher()
new_cipher.set_key_from_base64(saved_key)
# 測試密鑰兼容性
test_text = "測試密鑰兼容性"
encrypted = cipher.encrypt(test_text)
decrypted = new_cipher.decrypt(encrypted)
if test_text == decrypted:
print("? 密鑰兼容性測試通過")
else:
print("? 密鑰兼容性測試失敗")
def security_considerations():
"""
安全性考慮和建議
"""
print(f"\n{'#' * 40}")
print("安全性考慮和建議")
print(f"{'#' * 40}")
considerations = [
"? 使用CBC模式,每次加密生成隨機IV,避免相同明文產(chǎn)生相同密文",
"? 使用PKCS7填充,符合密碼學標準",
"? 默認使用AES-256(32字節(jié)密鑰),提供強安全性",
"?? 密鑰必須安全存儲,不要硬編碼在代碼中",
"?? 考慮使用環(huán)境變量或密鑰管理服務存儲密鑰",
"?? CBC模式需要正確的IV管理,確保每次加密使用不同IV",
"?? 對于大量數(shù)據(jù),考慮使用GCM模式提供認證加密",
"?? 在生產(chǎn)環(huán)境中添加完整性校驗(MAC)"
]
for consideration in considerations:
print(consideration)
if __name__ == "__main__":
# 執(zhí)行演示
demo_aes_encryption()
security_considerations()
總結一下上面的例子,非常的nice我只能說,至少用于對稱加密的學習那是相當?shù)膲蛴谩?/strong>
- 首先利用get_random_bytes隨機生成32位的密鑰。
- 之后使用get_random_bytes生成隨機的iv,iv的作用是為了相同的內(nèi)容密文也是不同的。
- 利用上面的iv和密鑰創(chuàng)建AES加密容器。
- 將明文用UTF-8進行encode,然后使用pad進行填充,要保證是塊大小的整數(shù)倍。
- 執(zhí)行加密,之后組合iv和密文并進行Base64編碼,方便存儲和傳輸。
那么上面的加密過程已經(jīng)清晰了,解密則相反,只不過有一個致命的問題,在生產(chǎn)環(huán)境中,密鑰怎么進行存儲?總不能傳輸給后臺吧,那不是泄露了嗎,畢竟這種規(guī)則都是通用的,可以被破解,而密鑰的管理要用到非對稱加密,我們接下來再說。
2.3 非對稱加密(RSA)
RSA的實現(xiàn)流程
- 密鑰生成:創(chuàng)建私鑰/公鑰對
- 公鑰加密:使用公鑰加密數(shù)據(jù)
- 私鑰解密:使用私鑰解密數(shù)據(jù)
RSA的程序特性
- 非對稱性:加密和解密使用不同的密鑰。
- 安全性高:底層使用復雜的數(shù)學算法,有效的保證了安全。
- 速度較慢:對比AES對稱加密慢100-1000倍。
- 長度限制:一次性加密的數(shù)據(jù)量有限制。
- 密鑰對管理:公鑰可公開,私鑰必須保密。
RSA使用場景
- 安全通信初始化(TLS/SSL握手)
- 數(shù)字簽名和身份驗證
- 安全電子郵件(PGP)
- SSH遠程登錄
- 區(qū)塊鏈和加密貨幣
- API安全認證
- 數(shù)字證書(PKI)
RSA代碼示例
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64
class RSACipher:
"""RSA非對稱加密解密工具類"""
def __init__(self, key_size=2048):
"""
初始化RSA密鑰對
步驟作用:
1. 設置密鑰長度(2048位是當前安全標準)
2. 生成RSA密鑰對
3. 分離公鑰和私鑰
"""
self.key_size = key_size # 密鑰位數(shù),決定安全性
# RSA.generate()作用:生成RSA密鑰對,基于大素數(shù)分解難題
self.key_pair = RSA.generate(key_size)
# 私鑰:用于解密和簽名,必須保密
self.private_key = self.key_pair
# 公鑰:用于加密和驗證,可以公開分發(fā)
self.public_key = self.key_pair.publickey()
print(f"[RSA初始化] 生成{key_size}位密鑰對完成")
def encrypt(self, plaintext, public_key=None):
"""使用公鑰加密
步驟作用:
1. 創(chuàng)建PKCS1_OAEP加密器
2. 將文本轉換為字節(jié)
3. 檢查長度限制(RSA不能加密太長數(shù)據(jù))
4. 執(zhí)行加密
5. Base64編碼便于傳輸
"""
try:
# PKCS1_OAEP.new()作用:創(chuàng)建使用OAEP填充的RSA加密器
# OAEP填充:增加隨機性,防止確定性攻擊
cipher = PKCS1_OAEP.new(public_key or self.public_key)
# encode()作用:將字符串轉換為UTF-8字節(jié)序列
plaintext_bytes = plaintext.encode('utf-8')
# RSA長度限制計算:
# - 密鑰大?。簁ey_size//8 = 字節(jié)數(shù)
# - OAEP填充占用42字節(jié)
max_length = (self.key_size // 8) - 42
print(f"[RSA加密] 明文長度: {len(plaintext_bytes)}字節(jié), 最大支持: {max_length}字節(jié)")
if len(plaintext_bytes) > max_length:
raise ValueError(f"文本過長,最大支持 {max_length} 字節(jié)")
# encrypt()作用:使用公鑰加密數(shù)據(jù)
# 原理:使用公鑰的指數(shù)e和模數(shù)n進行模冪運算
ciphertext = cipher.encrypt(plaintext_bytes)
print(f"[RSA加密] 加密完成,密文長度: {len(ciphertext)}字節(jié)")
# base64編碼:將二進制數(shù)據(jù)轉換為可安全傳輸?shù)奈谋靖袷?
return base64.b64encode(ciphertext).decode('utf-8')
except Exception as e:
return f"加密失敗: {e}"
def decrypt(self, ciphertext):
"""使用私鑰解密
步驟作用:
1. Base64解碼
2. 創(chuàng)建PKCS1_OAEP解密器
3. 使用私鑰解密
4. 將字節(jié)轉換回字符串
"""
try:
# 創(chuàng)建使用私鑰的解密器
cipher = PKCS1_OAEP.new(self.private_key)
# base64解碼:將文本格式轉換回二進制數(shù)據(jù)
ciphertext_bytes = base64.b64decode(ciphertext)
print(f"[RSA解密] 密文長度: {len(ciphertext_bytes)}字節(jié)")
# decrypt()作用:使用私鑰解密數(shù)據(jù)
# 原理:使用私鑰的指數(shù)d和模數(shù)n進行模冪運算
decrypted_bytes = cipher.decrypt(ciphertext_bytes)
print(f"[RSA解密] 解密完成,明文長度: {len(decrypted_bytes)}字節(jié)")
# decode()作用:將UTF-8字節(jié)序列轉換回字符串
return decrypted_bytes.decode('utf-8')
except Exception as e:
return f"解密失敗: {e}"
def get_public_key_pem(self):
"""獲取PEM格式的公鑰
作用:將公鑰轉換為標準PEM格式,便于存儲和傳輸"""
return self.public_key.export_key().decode('utf-8')
def get_private_key_pem(self):
"""獲取PEM格式的私鑰
作用:將私鑰轉換為標準PEM格式,便于安全存儲"""
return self.private_key.export_key().decode('utf-8')
RSA和AES的組合使用
"""
混合加密系統(tǒng):RSA加密AES密鑰 + AES加密實際數(shù)據(jù)
這是行業(yè)標準做法,兼顧安全性和性能
"""
class HybridEncryptionSystem:
def __init__(self):
# RSA用于密鑰交換
self.rsa_cipher = RSACipher()
# AES用于數(shù)據(jù)加密
self.aes_cipher = AESCipher()
def encrypt_data(self, plaintext: str, recipient_public_key) -> dict:
"""
加密流程:
1. 使用AES加密原始數(shù)據(jù)(高效)
2. 使用RSA加密AES密鑰(安全傳輸)
"""
# AES加密數(shù)據(jù)
encrypted_data = self.aes_cipher.encrypt(plaintext)
# RSA加密AES密鑰
encrypted_key = self.rsa_cipher.encrypt(
self.aes_cipher.get_key_base64(),
recipient_public_key
)
return {
"encrypted_data": encrypted_data,
"encrypted_aes_key": encrypted_key,
"rsa_public_key": self.rsa_cipher.get_public_key_pem() # 可選,用于回復
}
def decrypt_data(self, encrypted_package: dict, rsa_private_key) -> str:
"""
解密流程:
1. 使用RSA私鑰解密AES密鑰
2. 使用AES密鑰解密數(shù)據(jù)
"""
# 解密AES密鑰
decrypted_key_b64 = self.rsa_cipher.decrypt(
encrypted_package["encrypted_aes_key"],
rsa_private_key
)
# 設置AES密鑰
self.aes_cipher.set_key_from_base64(decrypted_key_b64)
# 解密數(shù)據(jù)
return self.aes_cipher.decrypt(encrypted_package["encrypted_data"])
# 使用示例
def hybrid_encryption_demo():
# 發(fā)送方
sender = HybridEncryptionSystem()
# 接收方的RSA公鑰(假設已安全獲?。?
recipient_public_key = "接收方的RSA公鑰PEM格式"
# 加密數(shù)據(jù)
original_data = "敏感業(yè)務數(shù)據(jù)"
encrypted_package = sender.encrypt_data(original_data, recipient_public_key)
print("加密完成:")
print(f"- AES加密數(shù)據(jù)長度: {len(encrypted_package['encrypted_data'])}")
print(f"- RSA加密的AES密鑰: {encrypted_package['encrypted_aes_key'][:50]}...")
# 接收方解密(使用自己的RSA私鑰)
# decrypted = recipient.decrypt_data(encrypted_package, recipient_private_key)
上述代碼的核心就是使用RSA來加密AES的密鑰,傳輸數(shù)據(jù)的時候將AES加密后的密鑰和加密的數(shù)據(jù)一起傳輸,之后服務端用私鑰進行解密
RSA私鑰的安全存儲方案
1. 硬件安全模塊(HSM)
?? 最安全的方案,私鑰永遠不出硬件
- 原理: 專用硬件設備,私鑰在硬件內(nèi)生成和存儲,永不導出
- 優(yōu)點: 最高安全性,防物理攻擊
- 缺點: 成本高,部署復雜
- 適用: 金融、政府、大型企業(yè)
2. 云密鑰管理服務(KMS)
- 原理: 使用云服務商的密鑰管理服務,應用程序通過API調(diào)用加解密
- 優(yōu)點: 易用,無需管理密鑰基礎設施
- 缺點: 依賴云服務商,可能有合規(guī)問題
- 適用: 云原生應用,中小企業(yè)
3. 密碼加密存儲
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Cipher import AES
import os
# 生成加密密鑰
salt = os.urandom(32)
key = PBKDF2(password, salt, 32, count=100000)
# 加密私鑰
private_key_pem = "RSA私鑰PEM內(nèi)容"
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted_private_key = cipher.encrypt(pad(private_key_pem.encode(), 16))
# 存儲:salt + iv + 加密私鑰
storage_data = salt + iv + encrypted_private_key
return {
"存儲格式": "salt(32) + iv(16) + 加密私鑰",
"解密要求": "需要正確密碼",
"安全性": "中等,依賴密碼強度"
}
4. 環(huán)境變量 + 文件權限
- 將私鑰文件放在安全目錄
- 設置嚴格文件權限(chmod 600)
- 通過環(huán)境變量指定密鑰路徑
- 應用程序運行時讀取
5. 如何選擇合適的加密方案
根據(jù)需求和企業(yè)的實力進行選擇,一般來說大部分使用的都是環(huán)境變量+文件的方案,稍微大點的企業(yè)使用第三方進行密鑰管理也完全足夠了,只有像政府和大型企業(yè)才會采用最復雜的HSM進行加密。
三、數(shù)據(jù)驗證的方法
?? 這里說的數(shù)據(jù)驗證是通過程序自帶的字符串方法檢查數(shù)據(jù)是否簡單的符合自己的要求,也就是下面這些方法的使用,上面已經(jīng)說過了。
# .startswith(prefix): 檢查是否以指定前綴開頭
filename = "document.pdf"
print(filename.startswith("doc")) # 輸出: True
print(filename.startswith("xls")) # 輸出: False
# .endswith(suffix): 檢查是否以指定后綴結尾
print(filename.endswith(".pdf")) # 輸出: True
print(filename.endswith(".docx")) # 輸出: False
# .isalpha(): 是否全是字母(中文也被認為是字母)
print("Hello".isalpha()) # 輸出: True
print("Hello123".isalpha()) # 輸出: False
print("你好".isalpha()) # 輸出: True
# .isdigit(): 是否全是數(shù)字
print("123".isdigit()) # 輸出: True
print("12.3".isdigit()) # 輸出: False
# .isalnum(): 是否全是字母或數(shù)字
print("Hello123".isalnum()) # 輸出: True
print("Hello 123".isalnum()) # 輸出: False(包含空格)
# .isspace(): 是否全是空白字符
print(" ".isspace()) # 輸出: True
print(" x ".isspace()) # 輸出: False
1. 主流數(shù)據(jù)驗證庫介紹
1. Pydantic(最推薦)
"""
Pydantic - 現(xiàn)代Python的數(shù)據(jù)驗證和設置管理
特點:性能高、類型提示友好、功能強大
安裝:pip install pydantic
"""
from pydantic import BaseModel, EmailStr, validator, Field
from typing import Optional
import re
class User(BaseModel):
# 基礎字段驗證
name: str = Field(..., min_length=1, max_length=50)
age: int = Field(..., ge=0, le=150) # 大于等于0,小于等于150
# 內(nèi)置郵箱驗證
email: EmailStr
# 自定義手機號驗證
phone: str = Field(..., regex=r'^1[3-9]\d{9}$')
# 自定義驗證器
@validator('name')
def name_must_contain_letters(cls, v):
if not any(c.isalpha() for c in v):
raise ValueError('姓名必須包含字母')
return v.title() # 自動格式化
@validator('phone')
def validate_phone_format(cls, v):
if not re.match(r'^1[3-9]\d{9}$', v):
raise ValueError('手機號格式不正確')
return v
# 使用示例
def pydantic_demo():
# 正確數(shù)據(jù)
try:
user = User(
name="張三",
age=25,
email="test@example.com",
phone="13800138000"
)
print(f"驗證成功: {user.dict()}")
except Exception as e:
print(f"驗證失敗: {e}")
# 錯誤數(shù)據(jù)測試
test_cases = [
{"name": "", "age": 25, "email": "invalid", "phone": "123"},
{"name": "John", "age": 200, "email": "test@example.com", "phone": "13800138000"},
]
for data in test_cases:
try:
user = User(**data)
except Exception as e:
print(f"驗證失敗: {e}")
pydantic_demo()
2. Cerberus(輕量靈活)
"""
Cerberus - 輕量級數(shù)據(jù)驗證庫
特點:Schema定義簡單、靈活性強
安裝:pip install cerberus
"""
from cerberus import Validator
# 定義驗證規(guī)則
user_schema = {
'name': {
'type': 'string',
'minlength': 1,
'maxlength': 50,
'required': True
},
'age': {
'type': 'integer',
'min': 0,
'max': 150,
'required': True
},
'email': {
'type': 'string',
'regex': r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
'required': True
},
'phone': {
'type': 'string',
'regex': r'^1[3-9]\d{9}$',
'required': True
}
}
# 自定義驗證規(guī)則
def validate_email_domain(field, value, error):
"""自定義郵箱域名驗證"""
blocked_domains = ['temp-mail.org', '10minutemail.com']
domain = value.split('@')[1]
if domain in blocked_domains:
error(field, f"不允許使用臨時郵箱域名: {domain}")
user_schema['email']['validator'] = validate_email_domain
def cerberus_demo():
validator = Validator(user_schema)
# 測試數(shù)據(jù)
test_data = {
'name': '李四',
'age': 30,
'email': 'lisi@example.com',
'phone': '13900139000'
}
if validator.validate(test_data):
print("數(shù)據(jù)驗證通過")
print(f"規(guī)范化后的數(shù)據(jù): {validator.document}")
else:
print("數(shù)據(jù)驗證失敗:")
print(f"錯誤信息: {validator.errors}")
cerberus_demo()
3. Voluptuous(語法優(yōu)雅)
"""
Voluptuous - 專注于數(shù)據(jù)驗證的庫
特點:語法優(yōu)雅、錯誤信息清晰
安裝:pip install voluptuous
"""
from voluptuous import Schema, Required, All, Length, Range, Email, Url, MultipleInvalid
import re
# 定義驗證schema
user_schema = Schema({
Required('name'): All(str, Length(min=1, max=50)),
Required('age'): All(int, Range(min=0, max=150)),
Required('email'): Email(),
Required('phone'): All(str, Length(min=11, max=11)),
'website': Url(), # 可選字段
})
# 自定義驗證函數(shù)
def validate_phone(value):
if not re.match(r'^1[3-9]\d{9}$', value):
raise ValueError('手機號格式不正確')
return value
# 更新schema加入自定義驗證
user_schema = Schema({
Required('name'): All(str, Length(min=1, max=50)),
Required('age'): All(int, Range(min=0, max=150)),
Required('email'): Email(),
Required('phone'): All(str, Length(min=11, max=11), validate_phone),
})
def voluptuous_demo():
# 正確數(shù)據(jù)
valid_data = {
'name': '王五',
'age': 28,
'email': 'wangwu@example.com',
'phone': '13700137000'
}
try:
result = user_schema(valid_data)
print(f"驗證成功: {result}")
except MultipleInvalid as e:
print(f"驗證失敗: {e}")
# 錯誤數(shù)據(jù)
invalid_data = {
'name': '',
'age': 200,
'email': 'invalid-email',
'phone': '123'
}
try:
result = user_schema(invalid_data)
except MultipleInvalid as e:
print(f"驗證失敗詳情:")
for error in e.errors:
print(f"- {error}")
voluptuous_demo()
四、正則表達式的使用
1. re模塊
1.1 基本使用
import re
# re模塊核心函數(shù)
text = "我的電話是138-0013-8000,郵箱是test@example.com"
# 1. re.search() - 查找第一個匹配
match = re.search(r'\d{3}-\d{4}-\d{4}', text)
if match:
print(f"找到電話: {match.group()}") # 138-0013-8000
# 2. re.findall() - 查找所有匹配
emails = re.findall(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', text)
print(f"所有郵箱: {emails}") # ['test@example.com']
# 3. re.finditer() - 返回迭代器(可獲取位置)
for match in re.finditer(r'\d+', text):
print(f"數(shù)字: {match.group()}, 位置: {match.span()}")
# 4. re.sub() - 替換
anonymized = re.sub(r'\d{4}$', 'XXXX', '13800138000')
print(f"匿名化: {anonymized}") # 1380013XXXX
# 5. re.split() - 分割
parts = re.split(r'[,。]', "你好,世界。今天天氣不錯。")
print(f"分割結果: {parts}") # ['你好', '世界', '今天天氣不錯', '']
| 函數(shù)名 | 基本使用示例 | 功能描述 |
|---|---|---|
re.search() | match = re.search(r'\d{3}-\d{4}-\d{4}', text)if match: print(match.group()) | 在字符串中搜索第一個匹配正則表達式的模式,返回匹配對象或None |
re.findall() | emails = re.findall(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', text) | 查找字符串中所有匹配正則表達式的子串,以列表形式返回所有匹配結果 |
re.finditer() | for match in re.finditer(r'\d+', text):print(f"數(shù)字: {match.group()}, 位置: {match.span()}") | 查找所有匹配項,返回一個迭代器,每個元素都是匹配對象,可獲取匹配內(nèi)容和位置信息 |
re.sub() | anonymized = re.sub(r'\d{4}$', 'XXXX', '13800138000') | 使用指定的替換字符串替換所有匹配正則表達式的子串 |
re.split() | parts = re.split(r'[,。]', "你好,世界。今天天氣不錯。") | 根據(jù)正則表達式模式分割字符串,返回分割后的子串列表 |
1.2 正則表達式語法詳解
"""
正則表達式元字符和語法
"""
def regex_syntax_demo():
text = "價格是¥100.50,折扣后¥80.25,聯(lián)系人: 張先生"
# 字符類
print("1. 字符類:")
print(re.findall(r'[張李王]先生', text)) # ['張先生']
print(re.findall(r'[0-9]', text)) # ['1', '0', '0', '5', '0', '8', '0', '2', '5']
# 量詞
print("\n2. 量詞:")
print(re.findall(r'\d+', text)) # ['100', '50', '80', '25'] - 1次或多次
print(re.findall(r'\d*', text)) # 包含空字符串
print(re.findall(r'\d{2,3}', text)) # ['100', '50', '80', '25'] - 2到3次
print(re.findall(r'¥\d+\.?\d*', text)) # ['¥100.50', '¥80.25']
# 邊界和位置
print("\n3. 邊界匹配:")
print(re.findall(r'^價格', text)) # ['價格'] - 開頭
print(re.findall(r'先生$', "張先生")) # ['先生'] - 結尾
print(re.findall(r'\b聯(lián)系人', text)) # ['聯(lián)系人'] - 單詞邊界
# 分組和捕獲
print("\n4. 分組捕獲:")
match = re.search(r'¥(\d+)\.(\d+)', text)
if match:
print(f"完整匹配: {match.group(0)}") # ¥100.50
print(f"整數(shù)部分: {match.group(1)}") # 100
print(f"小數(shù)部分: {match.group(2)}") # 50
print(f"所有分組: {match.groups()}") # ('100', '50')
regex_syntax_demo()
1.3. 高級特性
import re
def advanced_regex_features():
"""高級正則特性"""
text = """
用戶信息:
- 姓名: 張三, 年齡: 25, 郵箱: zhangsan@example.com
- 姓名: 李四, 年齡: 30, 郵箱: lisi@test.org
- 姓名: 王五, 年齡: 28, 郵箱: wangwu@domain.cn
"""
# 1. 命名分組
pattern = r"姓名: (?P<name>[^,]+), 年齡: (?P<age>\d+), 郵箱: (?P<email>[^\s]+)"
for match in re.finditer(pattern, text):
print(f"\n命名分組結果:")
print(f" 姓名: {match.group('name')}")
print(f" 年齡: {match.group('age')}")
print(f" 郵箱: {match.group('email')}")
# 2. 非捕獲分組
# (?:...) 不捕獲的分組
numbers = "100元 200美元 300歐元"
currencies = re.findall(r'(\d+)(?:元|美元|歐元)', numbers)
print(f"\n非捕獲分組: {currencies}") # ['100', '200', '300']
# 3. 前后查找
# (?=...) 正向肯定前瞻, (?!...) 正向否定前瞻
# (?<=...) 正向肯定后顧, (?<!...) 正向否定后顧
# 匹配后面是"元"的數(shù)字
prices = re.findall(r'\d+(?=元)', numbers)
print(f"價格(元): {prices}") # ['100']
# 匹配前面是"$"的數(shù)字
dollar_text = "價格$100 重量100kg"
dollar_prices = re.findall(r'(?<=\$)\d+', dollar_text)
print(f"美元價格: {dollar_prices}") # ['100']
# 4. 條件匹配
pattern = r'(\d+)(?(1)元|美元)' # 如果有分組1匹配"元",否則匹配"美元"
# 5. 注釋和詳細模式
complex_pattern = r"""
^ # 字符串開始
(\w+) # 用戶名 - 分組1
@ # @符號
(\w+ # 域名前綴 - 分組2
\. # 點號
\w+) # 域名后綴 - 分組3
$ # 字符串結束
"""
email = "user@example.com"
match = re.search(complex_pattern, email, re.VERBOSE)
if match:
print(f"\n詳細模式解析:")
print(f" 用戶名: {match.group(1)}")
print(f" 域名: {match.group(2)}")
advanced_regex_features()
2. regex庫
regex庫要比re庫更加全面,支持的場景更多,目前可以作為了解,如果有復雜的需求可以考慮使用regex庫進行解決,一般的需求re庫就可以支持,regex更多用于復雜的需求比如日志分析。
"""
regex庫 - 更強大、更兼容的正則表達式庫
安裝: pip install regex
特點: 支持更多特性、更好的Unicode支持、兼容re API
"""
import regex # 注意導入的是regex不是re
def regex_library_demo():
"""regex庫高級特性演示"""
text = "測試unicode: ???? 表情符號和中文"
# 1. 更好的Unicode支持
print("1. Unicode支持:")
# 匹配所有字母字符(包括中文)
print(regex.findall(r'\p{L}+', text)) # ['測試unicode', '表情符號和中文']
# 匹配表情符號
print(regex.findall(r'\p{Emoji}', text)) # ['??', '??']
# 2. 重疊匹配
print("\n2. 重疊匹配:")
text = "aaa"
# 標準re庫只能找到 ['aaa']
# regex可以找到所有重疊匹配
matches = regex.findall(r'a.a', text, overlapped=True)
print(f"重疊匹配: {matches}") # ['aaa']
# 3. 模糊匹配(容錯匹配)
print("\n3. 模糊匹配:")
text = "color centre favorite"
# 允許拼寫錯誤(美式vs英式英語)
pattern = regex.compile(r'(?e)(color){e<=1}') # 允許1個錯誤
matches = pattern.findall(text)
print(f"模糊匹配: {matches}") # ['color', 'colou']
# 4. 遞歸模式
print("\n4. 遞歸模式:")
html = "<div>內(nèi)容<span>嵌套</span>更多內(nèi)容</div>"
# 匹配嵌套的HTML標簽
pattern = regex.compile(r'<([^>]+)>(?:[^<>]|(?R))*</\1>')
matches = pattern.findall(html)
print(f"遞歸匹配: {matches}")
# 5. 集合操作
print("\n5. 集合操作:")
text = "abc123漢字??"
# 匹配數(shù)字或字母
matches = regex.findall(r'[[:alnum:]]', text)
print(f"字母數(shù)字: {matches}")
# 6. 分支重置
print("\n6. 分支重置:")
text = "日期: 2023-01-15 或 2023/01/15"
# 使用分支重置組,組號在不同分支中保持一致
pattern = r'(\d{4})(?|-(0[1-9]|1[0-2])-([0-2][0-9]|3[01])|/(0[1-9]|1[0-2])/([0-2][0-9]|3[01]))'
matches = regex.findall(pattern, text)
print(f"分支重置匹配: {matches}")
regex_library_demo()
總結
這篇文章的重點還是在加密那里,是非常重要的知識點,我們閱讀文章的時候不需要將所有的知識點都牢記,代碼還是更多的在編寫上,寫的多了熟悉就好了,我們可以有一個簡單的記憶,等到后面用的時候也知道一個方向,然后利用AI快速的解決問題。
到此這篇關于Python字符串相關操作及正則表達式的文章就介紹到這了,更多相關Python字符串及正則表達式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Python 新建文件夾與復制文件夾內(nèi)所有內(nèi)容的方法
今天小編就為大家分享一篇Python 新建文件夾與復制文件夾內(nèi)所有內(nèi)容的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10
Python環(huán)境下安裝PyGame和PyOpenGL的方法
這篇文章主要介紹了Python環(huán)境下安裝PyGame和PyOpenGL的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03
Django添加bootstrap框架時無法加載靜態(tài)文件的解決方式
這篇文章主要介紹了Django添加bootstrap框架時無法加載靜態(tài)文件的解決方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
Python中json文件和jsonl文件的區(qū)別小結
本文主要介紹了JSON和JSONL兩種文件格式的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-01-01
PyMongo進行MongoDB查詢和插入操作的高效使用示例
這篇文章主要為大家介紹了PyMongo進行MongoDB查詢和插入操作的高效使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11
python生成requirements.txt文件的兩種方法
requirements.txt 文件是項目的依賴包及其對應版本號的信息列表,本文主要介紹了python生成requirements.txt文件的兩種方法,具有一定的參考價值,感興趣的可以了解一下2023-12-12

