是的,Python 也可以实现类似的记录结构。我来展示几种实现方式:
- 使用 struct 模块实现固定长度记录:
import struct
from dataclasses import dataclass
from typing import List
import os
# 定义记录结构
@dataclass
class UserEntry:
account: str # 账号
password: str # 密码
username: str # 用户名
ssno: str # 身份证
quiz: str # 密保问题
answer: str # 密保答案
email: str # 邮箱
@dataclass
class UserEntryAdd:
quiz2: str # 密保问题2
answer2: str # 密保答案2
birthday: str # 生日
mobile_phone: str # 手机
@dataclass
class AccountDBRecord:
user_entry: UserEntry
user_entry_add: UserEntryAdd
error_count: int
action_tick: int
class FileIDDB:
# 文件头格式
HEADER_FORMAT = "16sii" # desc(16字节) + last_index(4字节) + deleted_index(4字节)
# 记录格式(每个字符串字段用32字节存储)
RECORD_FORMAT = "32s32s32s32s32s32s32s32s32s32s32sii"
def __init__(self, filename: str):
self.filename = filename
self.header_size = struct.calcsize(self.HEADER_FORMAT)
self.record_size = struct.calcsize(self.RECORD_FORMAT)
self.index_map = {} # 账号->文件位置的映射
def open(self):
"""打开或创建数据库文件"""
if not os.path.exists(self.filename):
# 创建新文件
with open(self.filename, "wb") as f:
# 写入文件头
header = struct.pack(self.HEADER_FORMAT,
b"ACCOUNT_DB", 0, 0)
f.write(header)
self._load_index()
def _load_index(self):
"""加载索引"""
self.index_map.clear()
with open(self.filename, "rb") as f:
# 跳过文件头
f.seek(self.header_size)
while True:
# 读取记录
data = f.read(self.record_size)
if not data:
break
# 解析记录
record = self._unpack_record(data)
if record:
# 建立索引映射
pos = f.tell() - self.record_size
self.index_map[record.user_entry.account] = pos
def add(self, record: AccountDBRecord) -> int:
"""添加记录"""
with open(self.filename, "r+b") as f:
# 1. 检查是否有删除空位可用
header = self._read_header(f)
if header.deleted_index > 0:
# 使用删除空位
pos = header.deleted_index
f.seek(pos)
# 读取下一个删除位置
next_del = struct.unpack("i", f.read(4))[0]
# 更新文件头
header.deleted_index = next_del
self._write_header(f, header)
else:
# 追加到文件末尾
f.seek(0, 2) # 移到文件末尾
pos = f.tell()
header.last_index += 1
self._write_header(f, header)
# 2. 写入记录
f.seek(pos)
data = self._pack_record(record)
f.write(data)
# 3. 更新索引
self.index_map[record.user_entry.account] = pos
return pos
def _pack_record(self, record: AccountDBRecord) -> bytes:
"""将记录打包为字节串"""
return struct.pack(self.RECORD_FORMAT,
record.user_entry.account.encode(),
record.user_entry.password.encode(),
record.user_entry.username.encode(),
record.user_entry.ssno.encode(),
record.user_entry.quiz.encode(),
record.user_entry.answer.encode(),
record.user_entry.email.encode(),
record.user_entry_add.quiz2.encode(),
record.user_entry_add.answer2.encode(),
record.user_entry_add.birthday.encode(),
record.user_entry_add.mobile_phone.encode(),
record.error_count,
record.action_tick
)
def _unpack_record(self, data: bytes) -> AccountDBRecord:
"""将字节串解包为记录"""
fields = struct.unpack(self.RECORD_FORMAT, data)
# 解码字符串字段
account = fields[0].decode().rstrip('\0')
password = fields[1].decode().rstrip('\0')
username = fields[2].decode().rstrip('\0')
ssno = fields[3].decode().rstrip('\0')
quiz = fields[4].decode().rstrip('\0')
answer = fields[5].decode().rstrip('\0')
email = fields[6].decode().rstrip('\0')
quiz2 = fields[7].decode().rstrip('\0')
answer2 = fields[8].decode().rstrip('\0')
birthday = fields[9].decode().rstrip('\0')
mobile = fields[10].decode().rstrip('\0')
user_entry = UserEntry(
account=account,
password=password,
username=username,
ssno=ssno,
quiz=quiz,
answer=answer,
email=email
)
user_entry_add = UserEntryAdd(
quiz2=quiz2,
answer2=answer2,
birthday=birthday,
mobile_phone=mobile
)
return AccountDBRecord(
user_entry=user_entry,
user_entry_add=user_entry_add,
error_count=fields[11],
action_tick=fields[12]
)
使用示例:
# 创建数据库
db = FileIDDB("accounts.db")
db.open()
# 添加记录
record = AccountDBRecord(
user_entry=UserEntry(
account="test1",
password="123456",
username="Test User",
ssno="123456789",
quiz="Your pet name?",
answer="dog",
email="test@test.com"
),
user_entry_add=UserEntryAdd(
quiz2="Your city?",
answer2="Beijing",
birthday="1990-01-01",
mobile_phone="13800138000"
),
error_count=0,
action_tick=0
)
pos = db.add(record)
print(f"Record added at position: {pos}")
- 使用 SQLite 实现(更简单的方案):
import sqlite3
from dataclasses import dataclass
import time
class AccountDB:
def __init__(self, db_file: str):
self.db_file = db_file
self.conn = None
def open(self):
self.conn = sqlite3.connect(self.db_file)
self._create_tables()
def _create_tables(self):
"""创建数据表"""
cur = self.conn.cursor()
cur.execute('''
CREATE TABLE IF NOT EXISTS accounts (
account TEXT PRIMARY KEY,
password TEXT,
username TEXT,
ssno TEXT,
quiz TEXT,
answer TEXT,
email TEXT,
quiz2 TEXT,
answer2 TEXT,
birthday TEXT,
mobile_phone TEXT,
error_count INTEGER,
action_tick INTEGER
)
''')
self.conn.commit()
def add(self, record: AccountDBRecord):
"""添加记录"""
cur = self.conn.cursor()
cur.execute('''
INSERT INTO accounts VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
''', (
record.user_entry.account,
record.user_entry.password,
record.user_entry.username,
record.user_entry.ssno,
record.user_entry.quiz,
record.user_entry.answer,
record.user_entry.email,
record.user_entry_add.quiz2,
record.user_entry_add.answer2,
record.user_entry_add.birthday,
record.user_entry_add.mobile_phone,
record.error_count,
record.action_tick
))
self.conn.commit()
两种方案比较:
struct 方案:
优点:
- 完全控制文件格式
- 固定长度记录,读写快速
- 支持记录重用
缺点:
SQLite 方案:
优点:
缺点:
根据实际需求选择合适的方案:
- 如果需要完全控制文件格式,用 struct 方案
- 如果追求开发效率,用 SQLite 方案