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

