Bytes 位元運算
在處理二進位資料時,位元運算(Bitwise Operations)是不可或缺的工具。無論是資料加 密、校驗碼計算,還是效能優化,理解 XOR、OR、AND 這些基本運算都能讓你的程式碼更強大且高效。
🔢 什麼是位元運算?
位元運算直接操作資料的二進位表示,**逐位元(bit by bit)**進行運算。在 Python 中,bytes 物件支援這些操作,讓我們能夠高效地處理二進位資料。
基本規則表
| A | B | AND (∧) | OR (∨) | XOR (⊕) |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
⚙️ Python Bytes 位元運算語法
在 Python 中,bytes 物件支援三種位元運算符號:
&:AND(按位與)|:OR(按位或)^:XOR(按位異或)
⚠️ 重要限制: 兩個 bytes 物件必須長度相同才能進行位元運算。
# ✅ 正確:長度相同
result = b'\x01\x02' & b'\x03\x04'
# ❌ 錯誤:長度不同會拋出 TypeError
result = b'\x01' & b'\x02\x03' # TypeError: operands must be of equal length
🔷 AND 運算(&):按位與
AND 運算會將兩個位元組的對應位元進行「與」運算,只有當兩個位元都是 1 時,結果才是 1。
基本用法
a = b'\x0F' # 0000 1111 (二進位)
b = b'\x33' # 0011 0011 (二進位)
result = bytes(a[0] & b[0] for a, b in zip(a, b))
# 結果: 0x03
# 計算過程:
# 0000 1111 (0x0F)
# & 0011 0011 (0x33)
# -----------
# 0000 0011 (0x03)
實際應用:遮罩(Masking)
AND 運算常用於遮罩操作,用來提取或清除特定的位元:
def extract_low_nibble(byte_value: int) -> int:
"""提取低 4 位元(低半位元組)"""
mask = 0x0F # 0000 1111
return byte_value & mask
def extract_high_nibble(byte_value: int) -> int:
"""提取高 4 位元(高半位元組)"""
mask = 0xF0 # 1111 0000
return (byte_value & mask) >> 4
def clear_bits(data: bytes, mask: bytes) -> bytes:
"""使用遮罩清除特定位元(1 表示保留,0 表示清除)"""
return bytes(d & m for d, m in zip(data, mask))
# 使用範例
value = 0xAB # 1010 1011
low = extract_low_nibble(value) # 0xB
high = extract_high_nibble(value) # 0xA
original = b'\xFF\xFF'
clear_mask = b'\xF0\x0F' # 清除第一個位元組的低 4 位和第二個位元組的高 4 位
cleared = clear_bits(original, clear_mask) # b'\xF0\x0F'
🔶 OR 運算(|):按位或
OR 運算會將兩個位元組的對應位元進行「或」運算,只要其中一個位元是 1,結果就是 1。
基本用法
a = b'\x0F' # 0000 1111
b = b'\x30' # 0011 0000
result = bytes(a[0] | b[0] for a, b in zip(a, b))
# 結果: 0x3F
# 計算過程:
# 0000 1111 (0x0F)
# | 0011 0000 (0x30)
# -----------
# 0011 1111 (0x3F)
實際應用:設定位元(Setting Bits)
OR 運算常用於設定特定位元為 1:
def set_bits(data: bytes, mask: bytes) -> bytes:
"""使用遮罩設定特定位元為 1(1 表示設定,0 表示保持原樣)"""
return bytes(d | m for d, m in zip(data, mask))
def set_flag(byte_value: int, bit_position: int) -> int:
"""設定指定位元為 1(bit_position: 0-7,0 是最低位元)"""
mask = 1 << bit_position
return byte_value | mask
# 使用範例
original = b'\x00\x00'
set_mask = b'\xF0\x0F' # 設定第一個位元組的高 4 位和第二個位元組的低 4 位
set_result = set_bits(original, set_mask) # b'\xF0\x0F'
value = 0x00
value = set_flag(value, 3) # 設定第 3 位元 → 0x08 (0000 1000)
🔸 XOR 運算(^):按位異或
XOR 運算是最有趣的位元運算,當兩個位元不同時結果為 1,相同時結果為 0。
基本用法
a = b'\x0F' # 0000 1111
b = b'\x33' # 0011 0011
result = bytes(a[0] ^ b[0] for a, b in zip(a, b))
# 結果: 0x3C
# 計算過程:
# 0000 1111 (0x0F)
# ^ 0011 0011 (0x33)
# -----------
# 0011 1100 (0x3C)
XOR 的重要特性
XOR 運算有幾個非常有用的特性:
| 特性 | 說明 | 範例 |
|---|---|---|
| 可逆性 | A ^ B ^ B = A | 加密後再 XOR 一次就能解密 |
| 交換律 | A ^ B = B ^ A | 運算順序不影響結果 |
| 結合律 | (A ^ B) ^ C = A ^ (B ^ C) | 可以任意組合 |
| 自反性 | A ^ A = 0 | 相同值 XOR 結果為 0 |
實際應用 1:簡單加密/解密
利用 XOR 的可逆性,我們可以實現簡單的加密:
def simple_xor_cipher(data: bytes, key: bytes) -> bytes:
"""
使用 XOR 進行簡單的加密/解密
注意:這只是教學範例,不適合生產環境使用!
"""
if not key:
raise ValueError("金鑰不能為空")
# 循環使用金鑰
key_cycle = (key * ((len(data) // len(key)) + 1))[:len(data)]
return bytes(d ^ k for d, k in zip(data, key_cycle))
# 使用範例
plaintext = b"Hello, World!"
key = b"SECRET"
# 加密
encrypted = simple_xor_cipher(plaintext, key)
# 解密(XOR 的特性:加密和解密是同一個操作)
decrypted = simple_xor_cipher(encrypted, key)
assert decrypted == plaintext # True
實際應用 2:校驗碼計算
XOR 常用於計算簡單的校驗碼:
def calculate_xor_checksum(data: bytes) -> int:
"""計算 XOR 校驗碼"""
checksum = 0
for byte in data:
checksum ^= byte
return checksum
def verify_xor_checksum(data: bytes, expected_checksum: int) -> bool:
"""驗證 XOR 校驗碼"""
return calculate_xor_checksum(data) == expected_checksum
# 使用範例
message = b"Hello, World!"
checksum = calculate_xor_checksum(message)
is_valid = verify_xor_checksum(message, checksum) # True
實際應用 3:交換變數值(不使用暫存 變數)
雖然這在 bytes 中不常用,但展示了 XOR 的巧妙應用:
def swap_without_temp(a: int, b: int) -> tuple[int, int]:
"""使用 XOR 交換兩個整數值(不使用暫存變數)"""
a = a ^ b # a 現在包含 a 和 b 的差異
b = a ^ b # b 現在等於原始的 a
a = a ^ b # a 現在等於原始的 b
return a, b
# 使用範例
x, y = 10, 20
x, y = swap_without_temp(x, y)
# 結果: x=20, y=10
🛠️ 綜合應用:位元操作工具類
class BytesBitwise:
"""Bytes 位元運算工具類"""
@staticmethod
def and_op(data1: bytes, data2: bytes) -> bytes:
"""執行 AND 位元運算"""
if len(data1) != len(data2):
raise ValueError(f"位元組長度必須相同: {len(data1)} != {len(data2)}")
return bytes(a & b for a, b in zip(data1, data2))
@staticmethod
def or_op(data1: bytes, data2: bytes) -> bytes:
"""執行 OR 位元運算"""
if len(data1) != len(data2):
raise ValueError(f"位元組長度必須相同: {len(data1)} != {len(data2)}")
return bytes(a | b for a, b in zip(data1, data2))
@staticmethod
def xor_op(data1: bytes, data2: bytes) -> bytes:
"""執行 XOR 位元運算"""
if len(data1) != len(data2):
raise ValueError(f"位元組長度必須相同: {len(data1)} != {len(data2)}")
return bytes(a ^ b for a, b in zip(data1, data2))
@staticmethod
def flip_bits(data: bytes) -> bytes:
"""翻轉所有位元(相當於與 0xFF 進行 XOR)"""
return bytes(byte ^ 0xFF for byte in data)
@staticmethod
def to_binary_string(data: bytes, separator: str = " ") -> str:
"""將位元組序列轉換為二進位字串表示"""
return separator.join(f"{byte:08b}" for byte in data)
# 使用範例
tool = BytesBitwise()
data1 = b'\xAA\x55'
data2 = b'\xF0\x0F'
print(tool.to_binary_string(data1)) # 10101010 01010101
print(tool.to_binary_string(data2)) # 11110000 00001111
and_result = tool.and_op(data1, data2) # b'\xA0\x05'
or_result = tool.or_op(data1, data2) # b'\xFA\x5F'
xor_result = tool.xor_op(data1, data2) # b'\x5A\x5A'
⚡ 效能考量與最佳實踐
1. 長度檢查
在進行位元運算前,務必確保兩個 bytes 物件長度相同:
def safe_bitwise_op(data1: bytes, data2: bytes, operation: str = "xor") -> bytes:
"""安全的位元運算,自動處理長度不匹配的情況"""
min_len = min(len(data1), len(data2))
truncated1 = data1[:min_len]
truncated2 = data2[:min_len]
match operation:
case "and":
return bytes(a & b for a, b in zip(truncated1, truncated2))
case "or":
return bytes(a | b for a, b in zip(truncated1, truncated2))
case "xor":
return bytes(a ^ b for a, b in zip(truncated1, truncated2))
case _:
raise ValueError(f"不支援的運算: {operation}")
2. 錯誤處理
實作穩健的錯誤處理機制:
def robust_bitwise_op(data1: bytes, data2: bytes, operation: str = "xor") -> bytes:
"""帶有完整錯誤處理的位元運算"""
# 類型檢查
if not isinstance(data1, bytes) or not isinstance(data2, bytes):
raise TypeError("輸入必須是 bytes 類型")
# 長度檢查
if len(data1) != len(data2):
raise ValueError(f"位元組長度必須相同: {len(data1)} != {len(data2)}")
# 執行運算
match operation.lower():
case "and":
return bytes(a & b for a, b in zip(data1, data2))
case "or":
return bytes(a | b for a, b in zip(data1, data2))
case "xor":
return bytes(a ^ b for a, b in zip(data1, data2))
case _:
raise ValueError(f"不支援的運算類型: {operation}")
⚠️ 常見問題與陷阱
1. 長度不匹配錯誤
# ❌ 錯誤示範
try:
result = bytes(a & b for a, b in zip(b'\x01', b'\x02\x03'))
# 這不會拋出錯誤,但只會處理第一個位元組
except Exception as e:
print(f"錯誤: {e}")
# ✅ 正確做法
def safe_operation(data1: bytes, data2: bytes) -> bytes:
if len(data1) != len(data2):
raise ValueError("長度必須相同")
return bytes(a & b for a, b in zip(data1, data2))
2. 不可變性
bytes 是不可變的,運算會產生新的物件:
original = b'\x01\x02'
mask = b'\xFF\x00'
result = bytes(a & b for a, b in zip(original, mask))
# original 不會被修改
print(original.hex()) # 0102
print(result.hex()) # 0100
3. 與整數運算的差異
# bytes 運算需要逐位元組處理
bytes_data1 = b'\x01\x02'
bytes_data2 = b'\x03\x04'
result_bytes = bytes(a & b for a, b in zip(bytes_data1, bytes_data2))
# 整數運算可以直接使用運算符
int1 = 0x0102
int2 = 0x0304
result_int = int1 & int2
print(result_bytes.hex()) # 0100
print(f"0x{result_int:04X}") # 0x0100
✅ 小結
| 運算 | 符號 | 主要用途 | 關鍵特性 |
|---|---|---|---|
| AND | & | 遮罩、提取特定位 元 | 只有兩個位元都是 1 時結果為 1 |
| OR | | | 設定特定位元為 1 | 只要有一個位元是 1 結果就是 1 |
| XOR | ^ | 加密、校驗碼、資料交換 | 兩個位元不同時結果為 1,可逆 |
關鍵要點
- ✅ 兩個
bytes物件必須長度相同才能進行位元運算 - ✅
bytes是不可變的,運算會產生新物件 - ✅ XOR 具有可逆性,適合簡單的加密場景
- ✅ 使用適當的錯誤處理確保程式碼穩健性
延伸學習
- 學習更多位元操作技巧(如位移運算
<<、>>) - 探索位元運算在密碼學中的應用
- 研究如何優化大型資料的位元運算效能
掌握這些基礎後,你就能更自信地處理二進位資料,無論是網路通訊、檔案格式解析,還是效能優化,都能游刃有餘!