当前位置: 首页 > news >正文

【音频字幕】构建一个离线视频字幕生成系统:使用 WhisperX 和 Faster-Whisper 的 Python 实现

一、背景介绍

  • 对于一端没有字幕外国视频、字幕,在不懂外语的情况下,怎么获取相关内容?
  • 作为技术宅,怎么自建搭建一个语音转文字的环境
  • 当前AI技术这么发达? 试试

二、系统设计

  • 音频提取(仅仅是视频需要该逻辑、本身就是音频不需要)
  • 优化(去掉静音、噪声等)
  • 语音识别
  • 对齐
  • 合并和 生成最终文字信息

系统主要依赖 Faster-Whisper(语音识别加速版)、WhisperX(时间戳对齐工具)、以及音频处理模块(如 LUFS 标准化和高通滤波)。

Faster-Whisper(语音识别加速版)

Whisper large-v3(推荐,多语言,性能最佳):https://huggingface.co/openai/whisper-large-v3
Whisper medium(代码中默认,平衡速度和精度):https://huggingface.co/openai/whisper-medium
Whisper medium.en(英语专用,速度更快):https://huggingface.co/openai/whisper-medium.en
Whisper small(轻量,适合低资源设备):https://huggingface.co/openai/whisper-small
Whisper tiny(最小模型,速度最快):https://huggingface.co/openai/whisper-tiny

WhisperX(时间戳对齐工具)

中文对齐模型(wav2vec2-large-xlsr-53-chinese-zh-cn):https://huggingface.co/jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh-cn
日语对齐模型(wav2vec2-large-xlsr-53-japanese):https://huggingface.co/jonatasgrosman/wav2vec2-large-xlsr-53-japanese
多语言备用模型(wav2vec2-large-xlsr-53):https://huggingface.co/facebook/wav2vec2-large-xlsr-53

音频处理模块(如 LUFS 标准化和高通滤波)

系统整体设计思路

系统的设计核心是“端到端”处理视频到字幕的流程:提取音频 → 优化信号 → 转录文本 → 对齐时间戳 → 合并并输出 SRT。离线运行是关键需求,因此优先使用本地模型和量化加速,避免网络依赖。

  • 设计方案:采用模块化架构,每个步骤独立函数,便于调试和扩展。预加载模型减少重复计算,支持 GPU 加速(Torch 和 CUDA)。回退逻辑(如模型缺失时使用未对齐结果)确保鲁棒性。
  • 思路:结合监督学习(Whisper 的多语言训练)和自监督表示(Wav2Vec2),实现高精度 ASR。音频优化先行,以提升识别准确率(噪音减少可降低字错误率 WER 达 10-20%)。

Whisper 模型:语音识别的核心算法与原理

Whisper 是 OpenAI 开发的通用语音识别模型,Faster-Whisper 是其加速实现。Whisper 的设计思路是构建一个多任务、多语言的序列到序列(seq2seq)模型,能够处理转录、翻译和语言识别等多项任务。

原理与设计思路

  • 核心思路:Whisper 使用 Transformer 架构,将音频梅尔谱图(Mel-spectrogram)作为输入,输出文本序列。训练于 680,000 小时多语言数据,支持 99 种语言,强调鲁棒性(处理噪音、口音)。 多任务学习允许模型同时预测时间戳和文本,减少了单独的时间对齐步骤。
  • 算法流程
    1. 音频预处理:将原始音频转换为梅尔谱图。
    2. 编码器:提取音频特征。
    3. 解码器:生成文本序列,使用 beam search 优化输出(beam_size=5 在代码中)。
  • 设计方案:Transformer 的自注意力机制捕捉长距离依赖,适合长音频。模型大小从 tiny 到 large,平衡准确率和速度。

Faster-Whisper:加速原理与优化方案

Faster-Whisper 是 Whisper 的再实现,使用 CTranslate2 引擎加速推理。

原理与设计思路

  • 核心思路:针对 Whisper 的高计算成本(Transformer 的 O(n^2) 复杂度),Faster-Whisper 通过量化、批处理和内核优化加速。设计方案聚焦于生产环境:支持 INT8/FP16 量化,减少内存占用 50%,推理速度提升 4-12 倍。
  • 算法流程
    1. 模型转换:将 PyTorch 模型转换为 CTranslate2 格式。
    2. 量化:权重从 FP32 降到 INT8,代码中 COMPUTE_TYPE=“int8” 是默认。
    3. 并行处理:支持 batch_size>1 和多线程。
  • 设计方案:静态缓存和内核融合减少冗余计算。例如,在长音频中,分段处理并合并结果。

三、系统实现

系统概述和技术栈

在开始拆解代码前,让我们先了解系统的整体架构:

  • 输入:视频文件(例如 MP4 格式)。
  • 输出:对应的 SRT 字幕文件。
  • 关键步骤
    1. 提取音频。
    2. 优化音频(音量标准化和高通滤波)。
    3. 语音识别和时间戳对齐。
    4. 字幕合并。
    5. 生成 SRT 文件。
  • 技术栈
    • 语音识别:Faster-Whisper(Whisper 模型的加速版,支持 INT8 量化以减少内存占用)。
    • 时间戳对齐:WhisperX(基于 Wav2Vec2 的对齐工具)。
    • 音频处理:MoviePy(提取音频)、Pydub(优化音频)。
    • 其他:Torch(GPU 加速)、Tqdm(进度条)、Logging(日志记录)。
    • 环境配置:强制离线模式,通过环境变量避免网络访问。

这个系统优化了资源管理(如 GPU 内存清理),并支持配置文件和命令行参数,提高了灵活性。现在,让我们逐模块拆解代码。

全局配置和环境设置

代码开头定义了全局参数,这是系统的“控制中心”。这里设置了模型路径、设备(GPU 或 CPU)和计算类型。

# 配置日志
logging.basicConfig(level=logging.INFO,format="%(asctime)s [%(levelname)s] %(message)s",handlers=[logging.FileHandler("subtitle_generator.log"), logging.StreamHandler()]
)# 加载配置文件(如果存在)
CONFIG_FILE = "config.json"
config = {}
if os.path.exists(CONFIG_FILE):try:with open(CONFIG_FILE, "r", encoding="utf-8") as f:config = json.load(f)logging.info("已加载配置文件: config.json")except Exception as e:logging.warning(f"加载配置文件失败: {e},使用默认配置")# 全局参数与模型路径配置
MODELS_DIR = config.get("models_dir", "/path/to/your/models")  # 示例路径,请替换为实际路径
os.environ["WHISPERX_MODEL_DIR"] = MODELS_DIR
os.environ["HF_HUB_OFFLINE"] = "1"  # 强制使用本地模型
os.environ["TRANSFORMERS_OFFLINE"] = "1"  # 禁用transformers的网络访问WHISPER_MODEL_PATH = config.get("whisper_model_path", f"{MODELS_DIR}/faster-whisper-medium")
ALIGN_MODEL_PATHS = config.get("align_model_paths", {"zh": f"{MODELS_DIR}/wav2vec2-large-xlsr-53-chinese-zh-cn","ja": f"{MODELS_DIR}/wav2vec2-large-xlsr-53-japanese",
})
OTHER_LANGUAGES_MODEL_PATHS = config.get("other_languages_model_path", f"{MODELS_DIR}/wav2vec2-large-xlsr-53")DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
COMPUTE_TYPE = config.get("compute_type", "int8")
MAX_GAP_MS = config.get("max_gap_ms", 1000)
MERGE_IDENTICAL = config.get("merge_identical", True)
MAX_LENGTH_CHARS = config.get("max_length_chars", 80)
TARGET_LUFS = config.get("target_lufs", -20.0)
HIGH_PASS_FREQ = config.get("high_pass_freq", 100)

拆解解释

  • 日志配置:使用 Python 的 logging 模块记录信息、警告和错误,支持控制台和文件输出。这有助于调试和监控系统运行。
  • 配置文件加载:通过 JSON 文件(如 config.json)动态加载参数,提高可配置性。如果文件不存在,使用默认值。
  • 环境变量:设置 HF_HUB_OFFLINETRANSFORMERS_OFFLINE 确保 Hugging Face 模型库不从网络下载,强制本地运行。
  • 模型路径:支持特定语言的对齐模型(如中文和日语),并提供多语言备用。DEVICECOMPUTE_TYPE 优化 GPU 使用,INT8 量化可将内存需求降低 50% 以上。
  • 其他参数:如字幕合并的最大间隔(1000ms)和字符长度限制(80),这些是经验值,可根据实际需求调整。

这个部分体现了系统的灵活性:用户可以通过修改 config.json 轻松自定义路径和参数,而无需更改代码。

工具函数

接下来是辅助函数,用于格式化时间戳。

def format_time(seconds):"""将秒数转换为SRT标准时间格式 (HH:MM:SS,ms)"""hours = int(seconds // 3600)minutes = int((seconds % 3600) // 60)secs = int(seconds % 60)millis = int((seconds - int(seconds)) * 1000)return f"{hours:02}:{minutes:02}:{secs:02},{millis:03}"

拆解解释

  • 这个函数将浮点秒数转换为 SRT 所需的字符串格式(如 “00:01:23,456”)。
  • 使用整数除法和模运算处理小时、分钟、秒和毫秒,确保输出始终是两位数(使用 f-string 格式化)。
  • 这是 SRT 文件的标准要求,避免了手动字符串拼接的错误。

核心处理流程

系统的“心脏”部分,包括音频提取、优化、转录、对齐、合并和 SRT 生成。

音频提取

def extract_and_prepare_audio(video_path, output_audio_path):"""从视频中提取音频"""logging.info("[步骤 1/5] 正在从视频中提取音频...")try:os.makedirs(os.path.dirname(output_audio_path), exist_ok=True)with AudioFileClip(video_path) as audio_clip:audio_clip.write_audiofile(output_audio_path,codec='pcm_s16le',fps=16000,ffmpeg_params=['-ac', '1'])logging.info(f"  音频已成功提取并保存至: {output_audio_path}")return Trueexcept Exception as e:logging.error(f"  [错误] 音频提取失败: {e}")raise

拆解解释

  • 使用 MoviePy 的 AudioFileClip 从视频提取音频,转换为 WAV 格式(采样率 16000Hz,单声道)。
  • FFmpeg 参数确保兼容 Whisper 模型的输入要求。
  • 异常处理:如果失败,记录错误并抛出异常,防止后续步骤执行。

音频优化

def optimize_audio(input_audio_path, output_audio_path, target_lufs=TARGET_LUFS, high_pass_freq=HIGH_PASS_FREQ):"""音频优化 (不使用AI降噪)"""logging.info("[步骤 2/5] 正在进行音频优化...")try:audio = AudioSegment.from_wav(input_audio_path)normalized_audio = audio.apply_gain(target_lufs - audio.dBFS)filtered_audio = normalized_audio.high_pass_filter(high_pass_freq)filtered_audio.export(output_audio_path, format="wav")logging.info(f"  优化后的音频已保存至: {output_audio_path}")return Trueexcept Exception as e:logging.error(f"  [错误] 音频优化失败: {e}")try:os.rename(input_audio_path, output_audio_path)logging.info(f"  使用原始音频作为替代: {output_audio_path}")return Trueexcept:logging.error("  [严重错误] 无法保存音频文件")return False

拆解解释

  • 使用 Pydub 加载音频,应用音量标准化(目标 -20 LUFS,广播标准)和高通滤波(去除 100Hz 以下低频噪音)。
  • 如果优化失败,回退到原始音频,确保流程不中断。
  • 这步提升了语音识别准确率,尤其在嘈杂或低音视频中。

语音识别与时间戳对齐

def transcribe_and_align(audio_path, whisper_model, align_models):"""语音识别与时间戳对齐"""logging.info("[步骤 3/5] 正在进行高精度语音识别与对齐...")try:# 阶段1: 语音识别logging.info(f"  - 正在使用 Whisper 模型: {WHISPER_MODEL_PATH}")audio = whisperx.load_audio(audio_path)segments, info = whisper_model.transcribe(audio,language=None,  # 自动检测语言task="transcribe",beam_size=5,word_timestamps=True)segments = list(tqdm(segments, desc="转录音频"))result = {"segments": [],"language": info.language}for segment in segments:result["segments"].append({"start": segment.start,"end": segment.end,"text": segment.text,"words": [{"word": word.word,"start": word.start,"end": word.end,"probability": word.probability} for word in segment.words] if segment.words else None})# 检测语言detected_language = info.languagelogging.info(f"  - 检测到语言: '{detected_language}'")# 阶段2: 时间戳对齐logging.info("  - 准备时间戳对齐...")align_model_path = ALIGN_MODEL_PATHS.get(detected_language)if align_model_path is None or not os.path.exists(align_model_path):align_model_path = OTHER_LANGUAGES_MODEL_PATHSif not os.path.exists(align_model_path):logging.warning(f"  [警告] 多语言对齐模型路径不存在: {align_model_path}")logging.info("  使用未对齐的识别结果")return resultlogging.info(f"  - 未找到语言 '{detected_language}' 的本地对齐模型,切换到多语言模型")required_files = ["preprocessor_config.json", "pytorch_model.bin", "config.json", "vocab.json"]missing_files = [f for f in required_files if not os.path.exists(os.path.join(align_model_path, f))]if missing_files:logging.warning(f"  [警告] 对齐模型缺少文件: {', '.join(missing_files)}")logging.info("  使用未对齐的识别结果")return resultlogging.info(f"  - 加载本地对齐模型: {align_model_path}")if detected_language in align_models:model_a, metadata = align_models[detected_language]else:model_a, metadata = whisperx.load_align_model(language_code=detected_language,device=DEVICE,model_dir=align_model_path)align_models[detected_language] = (model_a, metadata)  # 缓存模型aligned_result = whisperx.align(result["segments"],model_a,metadata,audio,DEVICE,return_char_alignments=False)logging.info("  - 语音识别与对齐完成。")return aligned_resultexcept Exception as e:logging.error(f"  [错误] 语音识别失败: {e}")traceback.print_exc()return None

拆解解释

  • 语音识别:使用 Faster-Whisper 转录音频,自动检测语言,支持单词级时间戳。Tqdm 添加进度条,提升用户体验。
  • 结果转换:将转录结果格式化为 WhisperX 兼容的字典。
  • 时间戳对齐:加载 Wav2Vec2 模型进行精确对齐,支持语言特定模型和缓存(避免重复加载)。
  • 回退逻辑:如果模型缺失,使用未对齐结果,确保系统鲁棒性。
  • 技术亮点:Beam search (beam_size=5) 提升准确率,单词级时间戳便于后续合并。

字幕合并

def merge_subtitles(transcription_result, max_gap_ms=MAX_GAP_MS, merge_identical=MERGE_IDENTICAL, max_length_chars=MAX_LENGTH_CHARS):"""合并字幕的核心函数"""logging.info("[步骤 4/5] 正在进行字幕合并...")segments = []for seg in transcription_result["segments"]:if 'words' in seg and seg['words']:for word_info in seg['words']:text = word_info.get('word', '').strip()if text:segments.append({'start': word_info['start'],'end': word_info['end'],'text': text})else:text = seg.get('text', '').strip()if text:segments.append({'start': seg['start'],'end': seg['end'],'text': text})if not segments:logging.info("  - 没有检测到有效字幕内容,跳过合并。")return []logging.info(f"  - 合并前字幕条数: {len(segments)}")merged_segments = []if segments:current_segment = segments[0].copy()for i in range(1, len(segments)):next_segment = segments[i]gap_ms = (next_segment['start'] - current_segment['end']) * 1000if merge_identical and current_segment['text'] == next_segment['text']:current_segment['end'] = next_segment['end']continueelif gap_ms <= max_gap_ms and len(current_segment['text']) + len(next_segment['text']) + 1 <= max_length_chars:current_segment['text'] += " " + next_segment['text']current_segment['end'] = next_segment['end']else:merged_segments.append(current_segment)current_segment = next_segment.copy()merged_segments.append(current_segment)logging.info(f"  - 合并后字幕条数: {len(merged_segments)}")return merged_segments

拆解解释

  • 将转录结果拆分成单词或句子级片段。
  • 合并逻辑:如果间隔小于 max_gap_ms 且总长度不超过 max_length_chars,则合并;相同文本直接扩展时间。
  • 这避免了过碎的字幕,提高可读性。日志记录合并前后条数,便于调试。

SRT 文件生成

def generate_srt_file(transcription_result, output_srt_path, max_gap_ms=MAX_GAP_MS, merge_identical=MERGE_IDENTICAL):"""生成SRT字幕文件 (包含合并逻辑)"""merged_transcription = merge_subtitles(transcription_result,max_gap_ms=max_gap_ms,merge_identical=merge_identical)logging.info("[步骤 5/5] 正在生成SRT字幕文件...")try:with open(output_srt_path, "w", encoding="utf-8") as srt_file:for i, segment in enumerate(merged_transcription):start_time = format_time(segment['start'])end_time = format_time(segment['end'])text = segment['text'].strip()if text:srt_file.write(f"{i + 1}\n")srt_file.write(f"{start_time} --> {end_time}\n")srt_file.write(f"{text}\n\n")logging.info(f"  字幕文件已成功生成: {output_srt_path}")return Trueexcept Exception as e:logging.error(f"  [错误] 生成SRT文件失败: {e}")return False

拆解解释

  • 调用合并函数后,遍历片段写入 SRT 文件(序号、时间戳、文本)。
  • UTF-8 编码确保多语言兼容。异常处理防止文件写入失败。

主工作流程和模型加载

def main_workflow(video_file_path, whisper_model, align_models):"""主工作流程控制器"""if not os.path.exists(video_file_path):logging.error(f"[错误] 输入的视频文件不存在: {video_file_path}")return Falselogging.info(f"\n{'=' * 50}")logging.info(f"开始处理视频: {os.path.basename(video_file_path)}")logging.info(f"{'=' * 50}\n")start_total_time = time.time()temp_dir = "temp_audio"os.makedirs(temp_dir, exist_ok=True)base_name = os.path.splitext(os.path.basename(video_file_path))[0]temp_raw_audio = os.path.join(temp_dir, f"temp_{base_name}_raw.wav")temp_final_audio = os.path.join(temp_dir, f"temp_{base_name}_final.wav")output_srt = f"{os.path.dirname(video_file_path)}\\{base_name}.srt"try:extract_and_prepare_audio(video_file_path, temp_raw_audio)optimize_audio(temp_raw_audio, temp_final_audio)transcription_data = transcribe_and_align(temp_final_audio, whisper_model, align_models)if transcription_data is None:logging.error("  [错误] 语音识别失败,无法生成字幕")return Falsegenerate_srt_file(transcription_data, output_srt)return Trueexcept Exception as e:logging.error(f"\n[!!!] 工作流程中断,发生严重错误: {e}")traceback.print_exc()return Falsefinally:for temp_file in [temp_raw_audio, temp_final_audio]:if os.path.exists(temp_file):try:os.remove(temp_file)logging.info(f"  - 已删除: {temp_file}")except OSError as e:logging.warning(f"  [警告] 无法删除临时文件 {temp_file}: {e}")end_total_time = time.time()total_time = end_total_time - start_total_timeminutes = int(total_time // 60)seconds = int(total_time % 60)logging.info(f"\n{'=' * 50}")logging.info(f"所有任务完成! 总耗时: {minutes}{seconds}秒")logging.info(f"生成的字幕文件: {output_srt}")logging.info(f"{'=' * 50}")if torch.cuda.is_available():torch.cuda.empty_cache()gc.collect()logging.info("显存和内存清理完成")def load_models():"""预加载模型"""logging.info("预加载 Whisper 模型...")try:whisper_model = WhisperModel(WHISPER_MODEL_PATH, device=DEVICE, compute_type=COMPUTE_TYPE)except Exception as e:logging.warning(f"无法使用 {COMPUTE_TYPE} 量化: {e},尝试 float16")whisper_model = WhisperModel(WHISPER_MODEL_PATH, device=DEVICE, compute_type="float16")align_models = {}for lang, path in ALIGN_MODEL_PATHS.items():if os.path.exists(path):logging.info(f"预加载 {lang} 对齐模型...")align_models[lang] = whisperx.load_align_model(language_code=lang, device=DEVICE, model_dir=path)return whisper_model, align_models

拆解解释

  • 模型加载:预加载 Whisper 和对齐模型,支持量化回退,提高批量处理效率。
  • 主流程:顺序调用各步骤,管理临时文件和时间计算。Finally 块确保清理资源,防止内存泄漏。
  • 返回布尔值表示成功,便于批量脚本调用。

程序入口

if __name__ == '__main__':parser = argparse.ArgumentParser(description="离线字幕生成系统")parser.add_argument("input_dir", help="视频文件目录路径")args = parser.parse_args()dir_path = args.input_dirwhisper_model, align_models = load_models()videos = [vid for vid in os.listdir(dir_path) if vid.lower().endswith(('.mp4', '.mkv', '.avi'))]for vid in tqdm(videos, desc="处理视频"):input_video = os.path.join(dir_path, vid)if main_workflow(input_video, whisper_model, align_models):logging.info(f"成功处理: {input_video}")else:logging.error(f"处理失败: {input_video}")sys.exit(0)

拆解解释

  • 使用 Argparse 支持命令行输入目录。
  • 过滤视频文件,Tqdm 显示进度。
  • 循环调用主流程,支持批量处理。

总结与改进建议

这个系统展示了如何将 AI 模型集成到 Python 脚本中,实现高效的离线字幕生成。优势包括:离线隐私保护、GPU 优化和可配置性。潜在改进:添加说话人识别(diarization)、支持更多格式,或集成 GUI 接口。

注意:

  • 如果你想运行这个系统,确保下载相应模型。
  • 注意环境配置,AI大模型对于最新依赖项和代码是对不上的,需要自己看依赖项目,避免出现依赖地狱
# ============== Core AI Libraries (Version Locked) ==============
numpy<2.0
torch==2.1.2
torchaudio==2.1.2
whisperx==3.1.1
speechbrain==1.0.0
ctranslate2==3.24.0
pyannote.audio==3.1.1
pytorch-lightning<2.0.0# ============== Audio & Video Tools ==============
moviepy
pydub
ffmpeg-python
srt# ============== Other Dependencies ==============
onnxruntime

文章转载自:

http://zxjHJMtS.cnfxr.cn
http://kkPEwmcc.cnfxr.cn
http://cn8mKLPg.cnfxr.cn
http://XlEWzYdM.cnfxr.cn
http://8YegI7EY.cnfxr.cn
http://ujw8KabE.cnfxr.cn
http://3WKyUlbZ.cnfxr.cn
http://2shdwunh.cnfxr.cn
http://O1QGKacJ.cnfxr.cn
http://CRK7bAhJ.cnfxr.cn
http://7o2VCnzb.cnfxr.cn
http://jhTY1u5N.cnfxr.cn
http://o8hvbfwZ.cnfxr.cn
http://fPVqvVmM.cnfxr.cn
http://cRuTlVKD.cnfxr.cn
http://QxBTNPRS.cnfxr.cn
http://zAkYXi8J.cnfxr.cn
http://HzocRjdN.cnfxr.cn
http://twTCRJyr.cnfxr.cn
http://LLooMs2Q.cnfxr.cn
http://rBInKHj8.cnfxr.cn
http://iDFIu5qB.cnfxr.cn
http://BjOSRQsD.cnfxr.cn
http://dyXhmr4v.cnfxr.cn
http://1gZuTlSS.cnfxr.cn
http://Hfb6eieT.cnfxr.cn
http://cdYhCKp1.cnfxr.cn
http://JRAGNQoS.cnfxr.cn
http://lkm0DYZ6.cnfxr.cn
http://9EBPkTsL.cnfxr.cn
http://www.dtcms.com/a/371581.html

相关文章:

  • ncnn-Android-mediapipe_hand 踩坑部署实录
  • java面试中经常会问到的mysql问题有哪些(基础版)
  • SoundSource for Mac 音频控制工具
  • Unity学习----【进阶】Input System学习(一)--导入与基础的设备调用API
  • 第11篇:降维算法:PCA、t-SNE、UMAP
  • 【Leetcode100】算法模板之二叉树
  • 深入理解假设检验:从抛硬币到药物实验的全景讲解
  • JavaScript笔记之JS 和 HTML5 的关系
  • 第4篇 conda install pytorch==2.0.0报错
  • 基于Echarts+HTML5可视化数据大屏展示-学生综合成绩评价系统大屏
  • 探索OpenResty:高性能Web开发利器
  • Lua 核心知识点详解
  • 26考研——内存管理_内存管理策略(3)
  • MySQL索引和B+Tree的关系
  • 《云原生配置危机:从服务瘫痪到韧性重建的实战全解》
  • 论文阅读-SelectiveStereo
  • 架构思维:重温限流算法原理与实战
  • 【面试题】关于RAG的五道题
  • redis的数据类型:List
  • 【mysql】SQL自连接:什么时候需要,什么时候不需要?
  • Android网络之WIFI技术网络模型概述
  • 【Pandas】3.1-数据预处理:列的基本操作
  • 【数据结构】经典 Leetcode 题
  • vector的使用和模拟实现
  • 开发思路篇:转账接口设计
  • 20250907-03:LangChain的六大核心模块概览
  • Python-LLMChat
  • 【C++】C++入门—(下)
  • 大数据毕业设计选题推荐-基于大数据的国家基站整点数据分析系统-Hadoop-Spark-数据可视化-BigData
  • 如何编写ICT模拟功能测试