代码中文抽取工具并替换工具(以ts为例)
文章目录
- 基本思路
- 目录结构
- 配置文件
- AST解析
- 替换代码中文
- 生成Excel
- 启动脚本
基本思路
- 通过对应语言的AST解析出中文
- 相关信息(文件、所在行列等)存到临时文件
- 通过相关信息,逐个文件位置替换掉中文
- 基于临时文件,通过py脚本生成Excel表格
目录结构
配置文件
// config.json
{"_NOTE": "// _NOTE后缀都是注释, 无需修改","start_id_NOTE": "// 生成配置表的起始id","start_id": 10000,"funCls_NOTE": "// 静态替换函数的类名","funCls": "LangUtil","replaceFunc_NOTE": "// 替换函数, 形如: public static getLang(code: number, ...args): string; ","replaceFunc": "LangUtil.getLang","importAbsolute_NOTE": "// import的绝对路径","importAbsolute": "import { LangUtil } from 'src/game/mod/common/utils/LangUtil';","ignore_file_NOTE": "// 忽略的.ts文件名","ignore_file": ["Const.ts","CharConst.ts","CharConstLog.ts","CharConstFeat.ts","CharConstMars.ts","CharConstCommon.ts","CharConstMeridian.ts","LangUtil.ts"],"ignore_dir_NOTE": "// 忽略的文件目录名","ignore_dir": [],"isReplaceStaticProperty_NOTE": "// 是否替换静态的属性的中文字符(建议第一波先不替换;第二波再统一替换,会报错,看能不能优化代码不要用静态的)","isReplaceStaticProperty": false,"replacer_exe_NOTE": "// 是否执行代码中文替换","replacer_exe": true,"gen_xls_exe_NOTE": "// 是否生成xls","gen_xls_exe": true,"xls_name_NOTE": "// 生成的xls文件名","xls_name": "lang_tool.xls","OPTIONAL_PARAMS_NOTE": "// ==========================================下方一般都没必要修改=============================","columns_NOTE": "// 生成表的列占位符","columns": ["NOTE", "ID", "文本", "空列", "代码文件来源", "被替换的源代码"],"ecmaVersion_NOTE": "// 解析所需参数","ecmaVersion": 2020,"sourceType_NOTE": "// 解析所需参数","sourceType": "module","genTsEnum_NOTE": "// 是否生成ts枚举类","genTsEnum": true,"isPrettyJsn_NOTE": "// 是否美化的json","isPrettyJsn": true,"tempJ_NOTE": " // json文件名","tempJ": "tempJ.json"
}
AST解析
// tool_chinese_strings_extracted.js
const fs = require('fs');
const path = require('path');
const { parse } = require('@typescript-eslint/typescript-estree');
const readline = require('readline');const POSTFIX_TS = ".ts"
let ENUM_START_ID = 1;
let tblStartId = 1; //表的id
const FILENAME_POSTFIX = "Lang";
const __Mark = "________";
const scriptDir = __dirname;
const CharConstClsName = "CharConst";
const IO_ENCODING = "utf8";
const COMBINE_NAME = `${CharConstClsName}${FILENAME_POSTFIX}`;
const CharConstTSFile = `${COMBINE_NAME}${POSTFIX_TS}`;
const input_dir = `${scriptDir}\\input\\`;
const output_dir = `${scriptDir}\\output\\`;
const output = `${COMBINE_NAME}.txt`;
const output_simple = `${COMBINE_NAME}_Simple.txt`;
const SHOW_FILE_LINE = true; // 必须为 true 才能获取文件行号生成注释
const IS_GEN_SIMPLE_STR = true;
let strings = new Set();
let strings_simple = new Set();
const tempJson = {};function insertJson(tableId, srcTxt, replaceFullPath, line, type, tgtTxt, args = null) {tempJson[tableId] = {srcTxt: srcTxt,path: replaceFullPath,line: line,type: type,tgtTxt: tgtTxt,}if (args) {tempJson[tableId]['args'] = args;}
}
//single 解析 AST
function traverse(filePath) {const content = fs.readFileSync(filePath, IO_ENCODING);try {const ast = parse(content, {ecmaVersion: getValueByKey("ecmaVersion"),sourceType: getValueByKey("sourceType"),loc: true,range: true});traverseAST(ast, filePath, content); // 递归遍历 AST 节点} catch (e) {console.warn(`Parse error in ${filePath}`);}
}
function isStaticProperty(node) {if(getValueByKey("isReplaceStaticProperty")){return false; //要替换静态的时候,当做不是静态属性即可}if (node.type === 'PropertyDefinition' && node.static) {// if(node.key && node.key.type == "Identifier"){// console.log(`忽略属性:${node.accessibility} static ${node.key.name}`);// }return true;}return false;
}
// 递归AST节点,查找中文字符串
function traverseAST(node, file, content) {if(isStaticProperty(node)){return;}if (node.type === 'Literal' &&typeof node.value === 'string' &&/[\u4e00-\u9fa5]/.test(node.value)) {handleStringLiteral(node, file, content, 0);}if (node.type === 'TemplateLiteral') {let processedStr = '';let argIndex = 0; // 参数索引计数器for (let i = 0; i < node.quasis.length; i++) {const quasi = node.quasis[i];processedStr += quasi.value.raw;if (i < node.quasis.length - 1) {processedStr += `{${argIndex++}}`; // 递增索引}}if (/[\u4e00-\u9fa5]/.test(processedStr)) {handleStringLiteral(/* {value: processedStr,loc: node.loc // 使用模板字符串整体位置} */node, file, content, 1, processedStr);}}for (let key in node) {if (node.hasOwnProperty(key) &&typeof node[key] === 'object' &&node[key] !== null) {traverseAST(node[key], file, content); // dfs}}
}function handleStringLiteral(node, file, content, type/* 1有processedStr */, processedStr = null) {if (!node.loc) {console.error(`未知行,不予记录,跳过; file=>${file}`)return;}const newTblId = tblStartId++;const line = node.loc.start.line;const isLiterStringType = isLiteral(type);let srcTxt = "";let args = [];if(isLiterStringType){srcTxt = node.raw;args = null;}else{srcTxt = content.slice(node.range[0], node.range[1]);node.expressions.forEach(expr => {const [start, end] = expr.range;args.push(content.slice(start, end)); });}const uid = `${__Mark}${newTblId}`;const tblSaveStr = isLiterStringType ? node.value : processedStr;insertJson(newTblId, srcTxt, file, line, type, tblSaveStr, args)if (SHOW_FILE_LINE) {// const line = node.loc ? node.loc.start.line : '未知行';strings.add(`${file}:${line}${uid}\n${/* node.value */tblSaveStr}`);// console.log(`enterCount-------------------->${testEnterCount}|||${strings.size}`)}if (IS_GEN_SIMPLE_STR) {strings_simple.add(`${/* node.value */tblSaveStr}${uid}`);// console.log(`enterCount-------------------->${testEnterCount}|||${strings_simple.size}`)}
}function isLiteral(type){return type == 0;
}function walkDir(currentPath) {if (!fs.existsSync(currentPath)) {console.error(`路径不存在: ${currentPath}`);return;}const files = fs.readdirSync(currentPath);for (const file of files) {const fullPath = path.join(currentPath, file);const stat = fs.statSync(fullPath);if (stat.isDirectory()) {if(ignore_dir.indexOf(file) > -1){console.log(`==========忽略文件夹:${file}`)}else{walkDir(fullPath);}} else if (file.endsWith(POSTFIX_TS)) {if(ignore_file.indexOf(file) > -1){console.log(`==========忽略文件:${file}`)}else{traverse(fullPath);}} else {}}
}function generateEnumFile() {const entries = Array.from(strings).map(entry => {const [fileLine, value] = entry.split('\n');const lastColonIndex = fileLine.lastIndexOf(':');return {file: fileLine.slice(0, lastColonIndex),line: disposeUIdStrByReg(fileLine.slice(lastColonIndex + 1)),value: value};});const enumLines = entries.map(({ file, line, value }, index) => {const id = index + ENUM_START_ID;const key = `ID_${id}`;const fileName = path.basename(file); // 提取文件名(去除路径)// 使用 JSON.stringify 自动转义特殊字符(如 "、\n 等)return `\t// ${fileName}:${line}\n\t${key} = ${id}, //${JSON.stringify(value)}`;});const enumContent = `/** Attention! The annotations will not change along with the actual table. */\nexport enum ${COMBINE_NAME} {\n${enumLines.join('\n')}\n}`;fs.writeFileSync(output_dir + CharConstTSFile, enumContent, IO_ENCODING);
}
const enableDelOutput = true;
const delOutputTipsShow = false;async function clearOutputDir() {const dirPath = output_dir;if (!enableDelOutput) return;console.log(`enableDelOutput:${output_dir}`)if (!fs.existsSync(dirPath)) {console.log(`目录不存在: ${dirPath}`);return;}// 创建 readline 接口const rl = readline.createInterface({input: process.stdin,output: process.stdout});try {// 等待用户确认let rst = "y";if (delOutputTipsShow) {rst = await new Promise(resolve => {rl.question(`即将清空目录 ${dirPath},确认继续吗?(y/n) `, resolve);});}if (rst.trim().toLowerCase() === 'y') {// 执行删除操作fs.rmSync(dirPath, { recursive: true, force: true });fs.mkdirSync(dirPath);console.log('√ 目录已清空');} else {console.log('× 操作已取消');}} finally {rl.close();}
}function disposeUIdStrByReg(s) {return s.replace(/________\d+/, '');
}
async function main() {try {await clearOutputDir();load_config();init_cfg_value();walkDir(input_dir);fs.writeFileSync(path.join(output_dir, output),Array.from(strings).map(s => disposeUIdStrByReg(s)).join('\n'),IO_ENCODING);fs.writeFileSync(path.join(output_dir, output_simple), Array.from(strings_simple).map(str => str.split(__Mark)[0]).join('\n'), IO_ENCODING);const genTsEnum = getValueByKey("genTsEnum");if(genTsEnum){generateEnumFile();}console.log(`√ 提取完成:- 找到 strings.size=>${strings.size}|||strings_simple.size=>${strings_simple.size} 条中文字符串(已写入 ${output} 和 ${output_simple})${genTsEnum ? "- 生成枚举文件 ${CharConstTSFile}" : ""}`);let jsonData;if(getValueByKey("isPrettyJsn")){jsonData = JSON.stringify(tempJson, null, 2);}else{jsonData = JSON.stringify(tempJson);}fs.writeFileSync(path.join(output_dir, getValueByKey("tempJ")), jsonData);} catch (err) {console.error('× 运行出错:', err);process.exit(1);}
}
main();let json_obj;
let ignore_file;
let ignore_dir;
function init_cfg_value(){tblStartId = ENUM_START_ID = getValueByKey("start_id");ignore_file = getValueByKey("ignore_file") || [];ignore_dir = getValueByKey("ignore_dir") || [];
}
function load_config(){try {const data = fs.readFileSync('./config.json', 'utf8');json_obj = JSON.parse(data);} catch (err) {console.error('config.json解析失败:', err);process.exit(1);}
}function getValueByKey(key) {if (json_obj && key in json_obj) {return json_obj[key];}return null;
}
替换代码中文
// tool_replacer.py
import json
import os
import re
from pathlib import Path
import logging
import osdef setup_logger():log_dir = "output"os.makedirs(log_dir, exist_ok=True)log_file = os.path.join(log_dir, "log.txt")logger = logging.getLogger("my_logger")logger.setLevel(logging.INFO)file_handler = logging.FileHandler(log_file, encoding="utf-8", mode="a")file_handler.setFormatter(logging.Formatter('[%(asctime)s] %(message)s'))logger.addHandler(file_handler)console_handler = logging.StreamHandler()console_handler.setFormatter(logging.Formatter('%(message)s'))logger.addHandler(console_handler)return loggerclass TsTextReplacer:def __init__(self, config_path="config.json", data_path="output/tempJ.json"):self.config = self.load_config(config_path)self.json_data = self.load_data(data_path)str_cfg=self.config['funCls']pattern = r'^import\s+.*?\b{}\b'.format(re.escape(str_cfg))self.import_pattern = re.compile(pattern, re.MULTILINE)str_import_cfg=self.config['importAbsolute']self.import_statement = f"{str_import_cfg}\n"def load_config(self, path):with open(path, 'r', encoding='utf-8') as f:return json.load(f)def load_data(self, path):with open(path, 'r', encoding='utf-8') as f:return json.load(f)# 接收键和项作为参数def generate_replacement(self, key, item):base = f"{self.config['replaceFunc']}({key}"if item.get('type', 0)!= 0 and item.get('args'):args = ', '.join(item['args'])return f"{base}, {args})"return f"{base})"def add_import_statement(self, content):if not self.import_pattern.search(content):return self.import_statement + contentreturn contentdef process_file(self, file_path, line, src_txt, replacement):try:with open(file_path, 'r', encoding='utf-8') as f:content = f.readlines()modified = Falsefor i in range(line - 1, len(content)):if src_txt in content[i]:content[i] = content[i].replace(src_txt, replacement, 1)modified = Truebreakif modified:full_content = ''.join(content)full_content = self.add_import_statement(full_content)with open(file_path, 'w', encoding='utf-8') as f:f.write(full_content)return Truereturn Falseexcept Exception as e:logger.info(f"处理文件 {file_path} 失败: {str(e)}")return Falsedef run(self):if self.config['replacer_exe']:success_count = 0for key, item in self.json_data.items():if not all(k in item for k in ['srcTxt', 'path', 'line']):continuereplacement = self.generate_replacement(key, item)file_path = Path(item['path']).resolve()if not file_path.exists():# print(f"文件不存在: {file_path}")logger.info(f"文件不存在: {file_path}")continueif self.process_file(file_path, item['line'], item['srcTxt'], replacement):# print(f"已处理: {file_path} [行{item['line']}]")logger.info(f"已处理: {file_path} [行{item['line']}]")success_count += 1else:# print(f"未找到匹配: {file_path} [行{item['line']}]")logger.info(f"未找到匹配: {file_path} [行{item['line']}]")# print(f"\n处理完成!成功替换 {success_count}/{len(self.json_data)} 处")endLog = f"************处理完成!成功替换 {success_count}/{len(self.json_data)} 处"logger.info(f"{endLog}")escape_info_str(endLog)else:endLog = "************跳过了替换代码中文步骤!"logger.info(f"{endLog}")escape_info_str(endLog)def escape_info_str(value):escaped_value = value.replace("%", "%%")print(f'echo "{escaped_value}"')if __name__ == "__main__":logger = setup_logger()replacer = TsTextReplacer()replacer.run()
生成Excel
// tool_excel_generator.py
import json
import os
from pathlib import Path
from openpyxl import Workbook
from openpyxl.utils import get_column_letterclass ExcelGenerator:def __init__(self):self.config = self.load_config()self.json_data = self.load_data()self.output_dir = Path("output")self.output_dir.mkdir(exist_ok=True)# eddef load_config(self):with open("config.json", "r", encoding="utf-8") as f:config = json.load(f)return {"xls_name": config.get("xls_name", "lang") + ".xls","columns": ["NOTE", "ID", "文本", "空列", "代码文件来源", "被替换的源代码"]}# eddef load_data(self):with open("output/tempJ.json", "r", encoding="utf-8") as f:return json.load(f)# eddef get_filename_from_path(self, path):return Path(path).stem# eddef clean_output_xls_rtn_name(self):output_path = self.output_dir / self.config["xls_name"]if output_path.exists():os.remove(output_path)return output_path# eddef generate_excel(self):if self.config["gen_xls_exe"]:"""生成Excel文件"""output_path = self.clean_output_xls_rtn_name()wb = Workbook()ws = wb.activews.title = "Lang Data"ws.column_dimensions[get_column_letter(2)].width = 15 # Key列ws.column_dimensions[get_column_letter(3)].width = 40 # tgtTxt列ws.column_dimensions[get_column_letter(5)].width = 35 # Path:Line列ws.column_dimensions[get_column_letter(6)].width = 60 # srcTxt列# 写入标题行ws.append(self.config["columns"])# 写入数据row_counter = 0for idx, (key, item) in enumerate(sorted(self.json_data.items(), key=lambda x: int(x[0])), 1):filename = self.get_filename_from_path(item["path"])path_line = f"{filename}:{item['line']}"row = [f"VALUE",key,item.get("tgtTxt", ""),"",path_line,item.get("srcTxt", "")]ws.append(row)row_counter += 1# 每20行插入空行if row_counter % 20 == 0:ws.append([""]*len(self.config["columns"]))# 保存文件wb.save(output_path)print(f"生成成功!文件位置:{output_path}")else:print(f"跳过了生成xls文件步骤!")if __name__ == "__main__":generator = ExcelGenerator()generator.generate_excel()
启动脚本
REM zstartup.bat
@echo off
echo start tool_chinese_strings_extracted...
node tool_chinese_strings_extracted.js
if errorlevel 1 (echo tool_chinese_strings_extracted fail.pauseexit /b
)echo start tool_replacer...
REM start tool_replacer.exe
call tool_replacer.exe > temp___vars.bat
call temp___vars.bat
del temp___vars.bat
if errorlevel 1 (echo tool_replacer fail.start log_show.batpauseexit /b
)echo start tool_excel_generator...
start tool_excel_generator.exe
if errorlevel 1 (echo tool_excel_generator fail.start log_show.batpauseexit /b
)
call log_show.bat
echo success...
pause
相关文章:

代码中文抽取工具并替换工具(以ts为例)
文章目录 基本思路目录结构配置文件AST解析替换代码中文生成Excel启动脚本 基本思路 通过对应语言的AST解析出中文相关信息(文件、所在行列等)存到临时文件通过相关信息,逐个文件位置替换掉中文基于临时文件,通过py脚本生成Excel…...

pgsql batch insert optimization (reWriteBatchedInserts )
reWriteBatchedInserts 是 PostgreSQL JDBC 驱动 提供的一个优化选项,它可以 重写批量插入语句,从而提高插入性能。 作用 当 reWriteBatchedInsertstrue 时,PostgreSQL JDBC 驱动会将 多个单独的 INSERT 语句 转换为 一个多行 INSERT 语句&a…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(上)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

华为云Flexus+DeepSeek征文 | 基于DeepSeek-V3构建企业知识库问答机器人实战
作者简介 我是摘星,一名专注于云计算和AI技术的开发者。本次通过华为云MaaS平台体验DeepSeek系列模型,将实际使用经验分享给大家,希望能帮助开发者快速掌握华为云AI服务的核心能力。 目录 作者简介 1. 引言 2. 技术选型与架构设计 2.1 技…...

【Docker 01】Docker 简介
🌈 一、虚拟化、容器化 ⭐ 1. 什么是虚拟化、容器化 物理机:真实存在的服务器 / 计算机,对于虚拟机来说,物理机为虚拟机提供了硬件环境。虚拟化:通过虚拟化技术将一台计算机虚拟为 1 ~ n 台逻辑计算机。在一台计算机…...
信息最大化(Information Maximization)
信息最大化在目标域无标签的域自适应任务中,它迫使模型在没有真实标签的情况下,对未标记数据产生高置信度且类别均衡的预测。此外,这些预测也可以作为伪标签用于自训练。 例如,在目标域没有标签时,信息最大化损失可以…...
整数的字典序怎么算
在Python中,字典序(lexicographical order)通常指的是按照字符串的字典顺序进行比较或排序。对于整数来说,字典序可以理解为将整数转换为字符串后进行比较的顺序。 计算整数的字典序 要计算整数的字典序,可以按照以下…...
知识拓展卡————————关于Access、Trunk、Hybrid端口
目录 什么是Trunk List、VLAN ID、PVID: VLAN ID(Virtual Local Area Network Identifier): Trunk List(Trunk列表): PVID(Prot VLAN ID): 关于Native VLAN &#x…...

AUTOSAR实战教程--DoIP_02_诊断链路建立流程
第一步:DoIP实体车辆声明/诊断仪车辆识别请求 打开激活线以后,DoIP实体发的三帧车辆声明报文。其中包含了DoIP实体的诊断逻辑地址(可以类比DoCAN的物理请求/响应地址),对应车辆的VIN码(若已配置࿰…...

音频剪辑软件少之又少好用
我们平时见到的图片以及视频编辑工具非常多,但是音频剪辑软件却是少之又少,更不用说有没有好用的,今天,给大家带来一款非常专业的音频剪辑软件,而且是会员喔。 软件简介 一款手机号登录即可以享受会员的超专业音频剪…...

客户端和服务器已成功建立 TCP 连接【输出解析】
文章目录 图片**1. 连接状态解析****第一条记录(服务器监听)****第二条记录(客户端 → 服务器)****第三条记录(服务器 → 客户端)** **2. 关键概念澄清****(1) 0.0.0.0 的含义****(2) 端口号的分配规则** *…...
多标签多分类 用什么函数激活
在多标签多分类任务中,激活函数的选择需要根据任务特性和输出层的设计来决定。以下是常见的激活函数及其适用场景: 一、多标签分类任务的特点 每个样本可以属于多个类别(标签之间非互斥,例如一篇文章可能同时属于 “科技” 和 “…...

day26-计算机网络-4
1. tcp的11种状态 ss -ant -a 表示看所有状态 -n 表示不将ip解析为主机名 -t 表示tcp 1.1. closed状态(客户端、服务端) 客户端发起建立连接前的状态服务端启动服务前的状态 1.2. listen状态(服务端) 服务端软件运行的时候状…...
ngx_stream_geo_module在传输层实现高性能 IP Region 路由
一、模块定位与核心价值 层次:工作在 Stream (TCP/UDP) 层,和 ngx_http_geo_module 的 L7 语义互补。作用:基于客户端 IP 前缀 / 范围生成一个 Nginx 变量,可在后续 proxy_pass、map、limit_conn、access 等指令中使用࿰…...

国防科技大学计算机基础慕课课堂学习笔记
1.信息论 香农作为信息论的这个创始人,给出来了这个信息熵的计算方法,为我们现在的这个生活的很多领域奠定了基础,我第一次听说这个信息熵是在这个数学建模里面的理论学习中有关于这个:决策树的模型,在那个问题里面&a…...

【第七篇】 SpringBoot项目的热部署
简介 本文介绍了热部署(Hot Deployment)的概念、使用场景及在IDEA中的配置方法。热部署可在不重启应用的情况下动态更新代码,提升开发效率,适用于调试、微服务架构和自动化测试等场景。文章详细说明了热部署的实现步骤(…...

解决pycharm同一个文件夹下from *** import***仍显示No module named
1、,from ***import *,同文件夹中已有.py文件但是仍然报错No module named 原因是因为pycharm没有把文件夹设置为根目录,只需要在文件夹的上一级设置为根目录即可,测试过如果仅仅将当前的文件夹设置仍然报错,如果把最上…...
GO 基础语法和数据类型面试题及参考答案(上)
目录 Go 中变量定义方式有哪些?各有什么适用场景? 使用 : 定义变量的限制是什么? 全局变量可以使用 : 声明吗?为什么? Go 中如何声明一个多变量赋值?有哪些注意事项? 常量能否通过表达式赋值…...

使用 Redisson 实现分布式锁—解决方案详解
Redisson 是 Redis 官方推荐的 Java 客户端,提供了一系列分布式服务实现,其中分布式锁是其核心功能之一。本文将深入解析 Redisson 分布式锁的实现原理、高级特性和最佳实践。 一、Redisson 分布式锁的优势 与传统实现的对比 特性手动实现Redisson 实现…...

结合三维基因建模与智能体技术打造工业软件无码平台
通过深度整合 Protocol Buffers (Protobuf)、gRPC 及 Microsoft AI 技术,构建面向智能制造的高性能、智能化 PLM 平台。 一、Protocol Buffers 深度集成 1. 基因模型标准化定义 三维基因容器 Protobuf 规范: protobuf syntax "proto3"; pa…...

Python Day46
Task: 1.不同CNN层的特征图:不同通道的特征图 2.什么是注意力:注意力家族,类似于动物园,都是不同的模块,好不好试了才知道。 3.通道注意力:模型的定义和插入的位置 4.通道注意力后的特征图和热力…...

基于PostGIS的各地级市路网长度统计及Echarts图表可视化实践-以湖南省为例
目录 前言 一、路网长度计算 1、地级市列表查询 2、地级市路网长度查询 二、Echarts可视化实现 1、Echarts后端生成 2、引入Colormap配色 3、前端微调 三、总结 前言 在当今快速发展的社会中,交通路网的建设与布局对于一个地区的经济发展、居民生活以及城市…...

mac版excel如何制作时长版环形图
设置辅助列 创建簇状柱形图 将辅助列绘制在次坐标轴 工作时长在主坐标轴,右键分别更改图表类型为圆环。 辅助列圆环全部为灰色,边框为白色 辅助列设置透明度100% 设置辅助列和工作时长列同样的圆环大小 可得 核心:只要辅助列边框不透明…...
PCB设计教程【大师篇】——STM32开发板原理图设计(电源部分)
前言 本教程基于B站Expert电子实验室的PCB设计教学的整理,为个人学习记录,旨在帮助PCB设计新手入门。所有内容仅作学习交流使用,无任何商业目的。若涉及侵权,请随时联系,将会立即处理 目录 前言 1. 工程创建与前期…...
k8s4部署
configMap configmap概述:数据会存储在etcd数据库,其应用场景主要在应用程序的配置 configmap支持的类型(1)键值对(2)多行数据 pod使用configmap资源有两种常见的方式(1)变量注入&a…...
贝叶斯医学分析中“先验”的如何进行选择(文献解读)
贝叶斯医学分析中“先验”的如何进行选择(文献解读) 作者:Callum Taylor, Kathryn Puxty, Tara Quasim, Martin Shaw 文章标题:Understanding Bayesian analysis of clinical trials: an overview for clinicians 期刊名称&#x…...
【汇编逆向系列】七、函数调用包含多个参数之浮点型- XMM0-3寄存器
目录 1. 汇编代码 1.1 debug编译 1.2 release编译 2. 汇编分析 2.1 浮点参数传递规则 2.2 栈帧rsp的变化时序 2.3 参数的访问逻辑 2.4 返回值XMM0寄存器 3. 汇编转化 3.1 Debug编译 3.2 Release 编译 3.3 C语言转化 1. 汇编代码 上一节介绍了整型的函数传参&#x…...

【MySQL系列】MySQL 执行 SQL 文件
博客目录 一、MySQL 执行 SQL 文件的常见场景二、MySQL 执行 SQL 文件的主要方法1. 使用 MySQL 命令行客户端2. 在 MySQL 交互界面中使用 source 命令3. 使用 MySQL Workbench 等图形化工具4. 使用编程语言接口 三、执行 SQL 文件时的注意事项1. 字符集问题2. 事务处理3. 错误处…...

论文MR-SVD
每个像素 7 个 FLOPs意思: FLOPs(浮点运算次数):衡量算法计算复杂度的指标,数值越小表示运算越高效。含义:对图像中每个像素进行处理时,仅需执行7 次浮点运算(如加减乘除等…...

Java 日期时间类全面解析
Java 日期时间类全面解析:从传统到现代的演进 一、发展历程概览 二、传统日期类(Java 8前) 1. java.util.Date - 日期表示类 Date now new Date(); // 当前日期时间 System.out.println(now); // Wed May 15 09:30:45 CST 2023// 特定时间…...